Add basic browsing and playing ui

custom
jacqueline 2 years ago
parent ec28b36a44
commit 80170642ea
  1. 2
      src/ui/CMakeLists.txt
  2. 12
      src/ui/include/screen.hpp
  3. 5
      src/ui/include/screen_menu.hpp
  4. 14
      src/ui/include/ui_events.hpp
  5. 14
      src/ui/include/ui_fsm.hpp
  6. 8
      src/ui/lvgl_task.cpp
  7. 30
      src/ui/screen_menu.cpp
  8. 71
      src/ui/ui_fsm.cpp

@ -3,7 +3,7 @@
# SPDX-License-Identifier: GPL-3.0-only
idf_component_register(
SRCS "lvgl_task.cpp" "ui_fsm.cpp" "screen_splash.cpp" "screen_menu.cpp" "wheel_encoder.cpp"
SRCS "lvgl_task.cpp" "ui_fsm.cpp" "screen_splash.cpp" "screen_menu.cpp" "wheel_encoder.cpp" "screen_track_browser.cpp" "screen_playing.cpp"
INCLUDE_DIRS "include"
REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer")
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})

@ -8,6 +8,7 @@
#include <memory>
#include "core/lv_group.h"
#include "core/lv_obj.h"
#include "core/lv_obj_tree.h"
#include "lvgl.h"
@ -16,13 +17,20 @@ namespace ui {
class Screen {
public:
Screen() : root_(lv_obj_create(NULL)) {}
virtual ~Screen() { lv_obj_del(root_); }
Screen() : root_(lv_obj_create(NULL)), group_(lv_group_create()) {}
virtual ~Screen() {
lv_obj_del(root_);
lv_group_del(group_);
}
virtual auto Tick() -> void {}
auto root() -> lv_obj_t* { return root_; }
auto group() -> lv_group_t* { return group_; }
protected:
lv_obj_t* const root_;
lv_group_t* const group_;
};
} // namespace ui

@ -7,7 +7,9 @@
#pragma once
#include <memory>
#include <vector>
#include "index.hpp"
#include "lvgl.h"
#include "screen.hpp"
@ -17,10 +19,11 @@ namespace screens {
class Menu : public Screen {
public:
Menu();
explicit Menu(std::vector<database::IndexInfo> indexes);
~Menu();
private:
std::vector<database::IndexInfo> indexes_;
lv_obj_t* container_;
lv_obj_t* label_;
};

@ -6,6 +6,8 @@
#pragma once
#include "database.hpp"
#include "index.hpp"
#include "tinyfsm.hpp"
namespace ui {
@ -20,4 +22,16 @@ struct OnStorageChange : tinyfsm::Event {
struct OnSystemError : tinyfsm::Event {};
namespace internal {
struct RecordSelected : tinyfsm::Event {
database::IndexRecord record;
};
struct IndexSelected : tinyfsm::Event {
database::IndexInfo index;
};
} // namespace internal
} // namespace ui

@ -7,6 +7,7 @@
#pragma once
#include <memory>
#include <stack>
#include "relative_wheel.hpp"
#include "tinyfsm.hpp"
@ -16,6 +17,7 @@
#include "storage.hpp"
#include "system_events.hpp"
#include "touchwheel.hpp"
#include "ui_events.hpp"
namespace ui {
@ -37,15 +39,23 @@ class UiState : public tinyfsm::Fsm<UiState> {
virtual void react(const system_fsm::KeyLockChanged&){};
virtual void react(const internal::RecordSelected&){};
virtual void react(const internal::IndexSelected&){};
virtual void react(const system_fsm::DisplayReady&) {}
virtual void react(const system_fsm::BootComplete&) {}
virtual void react(const system_fsm::StorageMounted&) {}
protected:
void PushScreen(std::shared_ptr<Screen>);
static drivers::IGpios* sIGpios;
static std::shared_ptr<drivers::TouchWheel> sTouchWheel;
static std::shared_ptr<drivers::RelativeWheel> sRelativeWheel;
static std::shared_ptr<drivers::Display> sDisplay;
static std::weak_ptr<database::Database> sDb;
static std::stack<std::shared_ptr<Screen>> sScreens;
static std::shared_ptr<Screen> sCurrentScreen;
};
@ -61,7 +71,11 @@ class Splash : public UiState {
class Interactive : public UiState {
void entry() override;
void react(const internal::RecordSelected&) override;
void react(const internal::IndexSelected&) override;
void react(const system_fsm::KeyLockChanged&) override;
void react(const system_fsm::StorageMounted&) override;
};
class FatalError : public UiState {};

@ -54,9 +54,6 @@ void LvglMain(std::weak_ptr<drivers::RelativeWheel> weak_touch_wheel,
lv_init();
TouchWheelEncoder encoder(weak_touch_wheel);
lv_group_t* nav_group = lv_group_create();
lv_group_set_default(nav_group);
lv_indev_set_group(encoder.registration(), nav_group);
std::shared_ptr<Screen> current_screen;
auto& events = events::EventQueue::GetInstance();
@ -68,9 +65,14 @@ void LvglMain(std::weak_ptr<drivers::RelativeWheel> weak_touch_wheel,
if (screen != current_screen && screen != nullptr) {
// TODO(jacqueline): animate this sometimes
lv_scr_load(screen->root());
lv_indev_set_group(encoder.registration(), screen->group());
current_screen = screen;
}
if (current_screen) {
current_screen->Tick();
}
lv_task_handler();
// 30 FPS
// TODO(jacqueline): make this dynamic

@ -11,35 +11,41 @@
#include "core/lv_group.h"
#include "core/lv_obj_pos.h"
#include "event_queue.hpp"
#include "extra/widgets/list/lv_list.h"
#include "extra/widgets/menu/lv_menu.h"
#include "extra/widgets/spinner/lv_spinner.h"
#include "hal/lv_hal_disp.h"
#include "index.hpp"
#include "misc/lv_area.h"
#include "ui_events.hpp"
#include "ui_fsm.hpp"
#include "widgets/lv_label.h"
namespace ui {
namespace screens {
static void item_click_cb(lv_event_t* ev) {
ESP_LOGI("menu", "clicked!");
if (ev->user_data == NULL) {
return;
}
database::IndexInfo* index =
reinterpret_cast<database::IndexInfo*>(ev->user_data);
events::Dispatch<internal::IndexSelected, UiState>(
internal::IndexSelected{.index = *index});
}
Menu::Menu() {
Menu::Menu(std::vector<database::IndexInfo> indexes) : indexes_(indexes) {
lv_obj_t* list = lv_list_create(root_);
lv_obj_set_size(list, lv_disp_get_hor_res(NULL), lv_disp_get_ver_res(NULL));
lv_obj_center(list);
lv_obj_t* button;
button = lv_list_add_btn(list, NULL, "hi");
lv_obj_add_event_cb(button, item_click_cb, LV_EVENT_CLICKED, NULL);
button = lv_list_add_btn(list, NULL, "second");
lv_obj_add_event_cb(button, item_click_cb, LV_EVENT_CLICKED, NULL);
button = lv_list_add_btn(list, NULL, "third");
lv_obj_add_event_cb(button, item_click_cb, LV_EVENT_CLICKED, NULL);
for (database::IndexInfo& index : indexes_) {
lv_obj_t* item = lv_list_add_btn(list, NULL, index.name.c_str());
lv_obj_add_event_cb(item, item_click_cb, LV_EVENT_CLICKED, &index);
lv_group_add_obj(group_, item);
}
}
Menu::~Menu() {}

@ -6,23 +6,33 @@
#include "ui_fsm.hpp"
#include <memory>
#include "audio_events.hpp"
#include "core/lv_obj.h"
#include "display.hpp"
#include "event_queue.hpp"
#include "lvgl_task.hpp"
#include "relative_wheel.hpp"
#include "screen.hpp"
#include "screen_menu.hpp"
#include "screen_playing.hpp"
#include "screen_splash.hpp"
#include "screen_track_browser.hpp"
#include "system_events.hpp"
#include "touchwheel.hpp"
namespace ui {
static constexpr char kTag[] = "ui_fsm";
static const std::size_t kRecordsPerPage = 10;
drivers::IGpios* UiState::sIGpios;
std::shared_ptr<drivers::TouchWheel> UiState::sTouchWheel;
std::shared_ptr<drivers::RelativeWheel> UiState::sRelativeWheel;
std::shared_ptr<drivers::Display> UiState::sDisplay;
std::weak_ptr<database::Database> UiState::sDb;
std::stack<std::shared_ptr<Screen>> UiState::sScreens;
std::shared_ptr<Screen> UiState::sCurrentScreen;
auto UiState::Init(drivers::IGpios* gpio_expander) -> bool {
@ -52,6 +62,13 @@ auto UiState::Init(drivers::IGpios* gpio_expander) -> bool {
return true;
}
void UiState::PushScreen(std::shared_ptr<Screen> screen) {
if (sCurrentScreen) {
sScreens.push(sCurrentScreen);
}
sCurrentScreen = screen;
}
namespace states {
void Splash::exit() {
@ -64,14 +81,62 @@ void Splash::react(const system_fsm::BootComplete& ev) {
transit<Interactive>();
}
void Interactive::entry() {
sCurrentScreen.reset(new screens::Menu());
}
void Interactive::entry() {}
void Interactive::react(const system_fsm::KeyLockChanged& ev) {
sDisplay->SetDisplayOn(ev.falling);
}
void Interactive::react(const system_fsm::StorageMounted& ev) {
sDb = ev.db;
auto db = ev.db.lock();
if (!db) {
// TODO(jacqueline): Hmm.
return;
}
PushScreen(std::make_shared<screens::Menu>(db->GetIndexes()));
}
void Interactive::react(const internal::RecordSelected& ev) {
auto db = sDb.lock();
if (!db) {
return;
}
if (ev.record.track()) {
ESP_LOGI(kTag, "selected track '%s'", ev.record.text()->c_str());
// TODO(jacqueline): We should also send some kind of playlist info here.
auto track = ev.record.track().value();
events::Dispatch<audio::PlayTrack, audio::AudioState>(audio::PlayTrack{
.id = track.data().id(),
.data = track.data(),
});
PushScreen(std::make_shared<screens::Playing>(track));
} else {
ESP_LOGI(kTag, "selected record '%s'", ev.record.text()->c_str());
auto cont = ev.record.Expand(kRecordsPerPage);
if (!cont) {
return;
}
auto query = db->GetPage(&cont.value());
std::string title = ev.record.text().value_or("TODO");
PushScreen(
std::make_shared<screens::TrackBrowser>(sDb, title, std::move(query)));
}
}
void Interactive::react(const internal::IndexSelected& ev) {
auto db = sDb.lock();
if (!db) {
return;
}
ESP_LOGI(kTag, "selected index %s", ev.index.name.c_str());
auto query = db->GetTracksByIndex(ev.index, kRecordsPerPage);
PushScreen(std::make_shared<screens::TrackBrowser>(sDb, ev.index.name,
std::move(query)));
}
} // namespace states
} // namespace ui

Loading…
Cancel
Save