summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-07-04 13:06:33 +1000
committerjacqueline <me@jacqueline.id.au>2023-07-04 13:06:33 +1000
commit80170642ea1d8bfc9703af217993ae29e6ee81d6 (patch)
tree564b8f28507619b21b7057ba1642438c976418b2 /src
parentec28b36a447e1a78f2512cc8f3fe579c7ad191d2 (diff)
downloadtangara-fw-80170642ea1d8bfc9703af217993ae29e6ee81d6.tar.gz
Add basic browsing and playing ui
Diffstat (limited to 'src')
-rw-r--r--src/ui/CMakeLists.txt2
-rw-r--r--src/ui/include/screen.hpp12
-rw-r--r--src/ui/include/screen_menu.hpp5
-rw-r--r--src/ui/include/ui_events.hpp14
-rw-r--r--src/ui/include/ui_fsm.hpp14
-rw-r--r--src/ui/lvgl_task.cpp8
-rw-r--r--src/ui/screen_menu.cpp30
-rw-r--r--src/ui/ui_fsm.cpp71
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 <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
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 <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_;
};
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 <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 {};
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<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
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<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() {}
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 <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