From 80170642ea1d8bfc9703af217993ae29e6ee81d6 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Tue, 4 Jul 2023 13:06:33 +1000 Subject: [PATCH] Add basic browsing and playing ui --- src/ui/CMakeLists.txt | 2 +- src/ui/include/screen.hpp | 12 +++++- src/ui/include/screen_menu.hpp | 5 ++- src/ui/include/ui_events.hpp | 14 +++++++ src/ui/include/ui_fsm.hpp | 14 +++++++ src/ui/lvgl_task.cpp | 8 ++-- src/ui/screen_menu.cpp | 30 ++++++++------ src/ui/ui_fsm.cpp | 71 ++++++++++++++++++++++++++++++++-- 8 files changed, 134 insertions(+), 22 deletions(-) diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index bd0b76f0..068691b3 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -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}) diff --git a/src/ui/include/screen.hpp b/src/ui/include/screen.hpp index 87a0d9b8..7ff06fbd 100644 --- a/src/ui/include/screen.hpp +++ b/src/ui/include/screen.hpp @@ -8,6 +8,7 @@ #include +#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 diff --git a/src/ui/include/screen_menu.hpp b/src/ui/include/screen_menu.hpp index a0b07b9e..e4cc0e78 100644 --- a/src/ui/include/screen_menu.hpp +++ b/src/ui/include/screen_menu.hpp @@ -7,7 +7,9 @@ #pragma once #include +#include +#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 indexes); ~Menu(); private: + std::vector indexes_; lv_obj_t* container_; lv_obj_t* label_; }; diff --git a/src/ui/include/ui_events.hpp b/src/ui/include/ui_events.hpp index af07a71d..fabc56fc 100644 --- a/src/ui/include/ui_events.hpp +++ b/src/ui/include/ui_events.hpp @@ -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 diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index 4de9344c..32275fab 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -7,6 +7,7 @@ #pragma once #include +#include #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 { 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); + static drivers::IGpios* sIGpios; static std::shared_ptr sTouchWheel; static std::shared_ptr sRelativeWheel; static std::shared_ptr sDisplay; + static std::weak_ptr sDb; + static std::stack> sScreens; static std::shared_ptr 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 {}; diff --git a/src/ui/lvgl_task.cpp b/src/ui/lvgl_task.cpp index 37cde858..961b5147 100644 --- a/src/ui/lvgl_task.cpp +++ b/src/ui/lvgl_task.cpp @@ -54,9 +54,6 @@ void LvglMain(std::weak_ptr 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 current_screen; auto& events = events::EventQueue::GetInstance(); @@ -68,9 +65,14 @@ void LvglMain(std::weak_ptr 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 diff --git a/src/ui/screen_menu.cpp b/src/ui/screen_menu.cpp index b6d0606a..743dc6fa 100644 --- a/src/ui/screen_menu.cpp +++ b/src/ui/screen_menu.cpp @@ -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(ev->user_data); + + events::Dispatch( + internal::IndexSelected{.index = *index}); } -Menu::Menu() { +Menu::Menu(std::vector 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() {} diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index 709778c7..58b1f641 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -6,23 +6,33 @@ #include "ui_fsm.hpp" #include +#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 UiState::sTouchWheel; std::shared_ptr UiState::sRelativeWheel; std::shared_ptr UiState::sDisplay; +std::weak_ptr UiState::sDb; +std::stack> UiState::sScreens; std::shared_ptr 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) { + 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(); } -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(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{ + .id = track.data().id(), + .data = track.data(), + }); + PushScreen(std::make_shared(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(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(sDb, ev.index.name, + std::move(query))); +} + } // namespace states } // namespace ui