diff --git a/src/drivers/touchwheel.cpp b/src/drivers/touchwheel.cpp index 14cfa689..836e688a 100644 --- a/src/drivers/touchwheel.cpp +++ b/src/drivers/touchwheel.cpp @@ -59,7 +59,6 @@ TouchWheel::TouchWheel() { for (int i = 5; i < 12; i++) { WriteRegister(Register::KEY_CONTROL_BASE + i, 1); } - } TouchWheel::~TouchWheel() {} diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index f17020ac..06bad37e 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -6,6 +6,7 @@ idf_component_register( SRCS "lvgl_task.cpp" "ui_fsm.cpp" "screen_splash.cpp" "screen_menu.cpp" "wheel_encoder.cpp" "screen_track_browser.cpp" "screen_playing.cpp" "themes.cpp" "widget_top_bar.cpp" "screen.cpp" "screen_onboarding.cpp" + "modal_progress.cpp" "modal.cpp" "modal_confirm.cpp" "splash.c" "font_fusion.c" "font_symbols.c" INCLUDE_DIRS "include" REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer") diff --git a/src/ui/include/modal.hpp b/src/ui/include/modal.hpp new file mode 100644 index 00000000..a5ac69b8 --- /dev/null +++ b/src/ui/include/modal.hpp @@ -0,0 +1,37 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include + +#include "core/lv_group.h" +#include "core/lv_obj.h" +#include "core/lv_obj_tree.h" +#include "lvgl.h" +#include "widget_top_bar.hpp" + +#include "screen.hpp" + +namespace ui { + +class Modal { + public: + Modal(Screen* host); + virtual ~Modal(); + + 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_; + + private: + Screen* host_; +}; + +} // namespace ui diff --git a/src/ui/include/modal_confirm.hpp b/src/ui/include/modal_confirm.hpp new file mode 100644 index 00000000..4be6b68e --- /dev/null +++ b/src/ui/include/modal_confirm.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include +#include + +#include "index.hpp" +#include "lvgl.h" + +#include "modal.hpp" + +namespace ui { +namespace modals { + +class Confirm : public Modal { + public: + Confirm(Screen*, const std::string& title, bool has_cancel); + + private: + lv_obj_t* container_; +}; + +} // namespace modals +} // namespace ui diff --git a/src/ui/include/modal_progress.hpp b/src/ui/include/modal_progress.hpp new file mode 100644 index 00000000..96897029 --- /dev/null +++ b/src/ui/include/modal_progress.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include +#include + +#include "index.hpp" +#include "lvgl.h" + +#include "modal.hpp" + +namespace ui { +namespace modals { + +class Progress : public Modal { + public: + Progress(Screen*, std::string title); + + private: + lv_obj_t* container_; +}; + +} // namespace modals +} // namespace ui diff --git a/src/ui/include/screen.hpp b/src/ui/include/screen.hpp index 0ec72a63..250b3c8d 100644 --- a/src/ui/include/screen.hpp +++ b/src/ui/include/screen.hpp @@ -7,6 +7,7 @@ #pragma once #include +#include #include "core/lv_group.h" #include "core/lv_obj.h" @@ -23,14 +24,8 @@ namespace ui { */ class Screen { public: - Screen() : root_(lv_obj_create(NULL)), group_(lv_group_create()) {} - - virtual ~Screen() { - // The group *must* be deleted first. Otherwise, focus events will be - // generated whilst deleting the object tree, which causes a big mess. - lv_group_del(group_); - lv_obj_del(root_); - } + Screen(); + virtual ~Screen(); /* * Called periodically to allow the screen to update itself, e.g. to handle @@ -41,14 +36,27 @@ class Screen { auto UpdateTopBar(const widgets::TopBar::State& state) -> void; auto root() -> lv_obj_t* { return root_; } - auto group() -> lv_group_t* { return group_; } + auto content() -> lv_obj_t* { return content_; } + + auto modal_content() -> lv_obj_t* { return modal_content_; } + auto modal_group(lv_group_t* g) -> void { modal_group_ = g; } + auto group() -> lv_group_t* { + if (modal_group_) { + return modal_group_; + } + return group_; + } protected: auto CreateTopBar(lv_obj_t* parent, const widgets::TopBar::Configuration&) -> widgets::TopBar*; lv_obj_t* const root_; + lv_obj_t* const content_; + lv_obj_t* const modal_content_; + lv_group_t* const group_; + lv_group_t* modal_group_; private: std::unique_ptr top_bar_; diff --git a/src/ui/include/ui_events.hpp b/src/ui/include/ui_events.hpp index a0ef1c31..759a0879 100644 --- a/src/ui/include/ui_events.hpp +++ b/src/ui/include/ui_events.hpp @@ -37,6 +37,9 @@ struct IndexSelected : tinyfsm::Event { struct BackPressed : tinyfsm::Event {}; +struct ModalConfirmPressed : tinyfsm::Event {}; +struct ModalCancelPressed : tinyfsm::Event {}; + } // namespace internal } // namespace ui diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index 1551932a..4985129a 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -15,6 +15,7 @@ #include "tinyfsm.hpp" #include "display.hpp" +#include "modal.hpp" #include "screen.hpp" #include "storage.hpp" #include "system_events.hpp" @@ -40,6 +41,8 @@ class UiState : public tinyfsm::Fsm { /* Fallback event handler. Does nothing. */ void react(const tinyfsm::Event& ev) {} + void react(const system_fsm::BatteryPercentChanged&); + virtual void react(const audio::PlaybackStarted&) {} virtual void react(const audio::PlaybackUpdate&) {} virtual void react(const audio::QueueUpdate&) {} @@ -49,6 +52,12 @@ class UiState : public tinyfsm::Fsm { virtual void react(const internal::RecordSelected&) {} virtual void react(const internal::IndexSelected&) {} virtual void react(const internal::BackPressed&) {} + virtual void react(const internal::ModalCancelPressed&) { + sCurrentModal.reset(); + } + virtual void react(const internal::ModalConfirmPressed&) { + sCurrentModal.reset(); + } virtual void react(const system_fsm::DisplayReady&) {} virtual void react(const system_fsm::BootComplete&) {} @@ -57,6 +66,7 @@ class UiState : public tinyfsm::Fsm { protected: void PushScreen(std::shared_ptr); void PopScreen(); + void UpdateTopBar(); static drivers::IGpios* sIGpios; static audio::TrackQueue* sQueue; @@ -68,6 +78,7 @@ class UiState : public tinyfsm::Fsm { static std::stack> sScreens; static std::shared_ptr sCurrentScreen; + static std::shared_ptr sCurrentModal; }; namespace states { diff --git a/src/ui/lvgl_task.cpp b/src/ui/lvgl_task.cpp index a77efa85..7d4024e5 100644 --- a/src/ui/lvgl_task.cpp +++ b/src/ui/lvgl_task.cpp @@ -35,6 +35,7 @@ #include "misc/lv_color.h" #include "misc/lv_style.h" #include "misc/lv_timer.h" +#include "modal.hpp" #include "relative_wheel.hpp" #include "tasks.hpp" #include "touchwheel.hpp" @@ -75,6 +76,7 @@ void LvglMain(std::weak_ptr weak_touch_wheel, TouchWheelEncoder encoder(weak_touch_wheel); std::shared_ptr current_screen; + lv_group_t* current_group = nullptr; auto* events = events::queues::Ui(); while (1) { while (events->Service(0)) { @@ -88,6 +90,11 @@ void LvglMain(std::weak_ptr weak_touch_wheel, current_screen = screen; } + if (current_screen->group() != current_group) { + current_group = current_screen->group(); + lv_indev_set_group(encoder.registration(), current_group); + } + if (current_screen) { current_screen->Tick(); } diff --git a/src/ui/modal.cpp b/src/ui/modal.cpp new file mode 100644 index 00000000..c0f9b3f5 --- /dev/null +++ b/src/ui/modal.cpp @@ -0,0 +1,57 @@ + +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "misc/lv_color.h" +#include "modal_progress.hpp" + +#include "core/lv_event.h" +#include "esp_log.h" + +#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 "screen.hpp" +#include "ui_events.hpp" +#include "ui_fsm.hpp" +#include "widget_top_bar.hpp" +#include "widgets/lv_label.h" + +namespace ui { + +Modal::Modal(Screen* host) + : root_(lv_obj_create(host->modal_content())), + group_(lv_group_create()), + host_(host) { + lv_obj_set_style_bg_opa(host->modal_content(), LV_OPA_40, 0); + lv_obj_set_style_bg_color(host->modal_content(), lv_color_black(), 0); + + lv_obj_set_size(root_, 120, LV_SIZE_CONTENT); + lv_obj_center(root_); + + lv_obj_set_style_bg_opa(root_, LV_OPA_COVER, 0); + lv_obj_set_style_bg_color(root_, lv_color_white(), 0); + + host_->modal_group(group_); +} + +Modal::~Modal() { + host_->modal_group(nullptr); + lv_obj_set_style_bg_opa(host_->modal_content(), LV_OPA_TRANSP, 0); + + // The group *must* be deleted first. Otherwise, focus events will be + // generated whilst deleting the object tree, which causes a big mess. + lv_group_del(group_); + lv_obj_del(root_); +} + +} // namespace ui diff --git a/src/ui/modal_confirm.cpp b/src/ui/modal_confirm.cpp new file mode 100644 index 00000000..14d56123 --- /dev/null +++ b/src/ui/modal_confirm.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "modal_confirm.hpp" + +#include "core/lv_event.h" +#include "core/lv_obj.h" +#include "core/lv_obj_tree.h" +#include "esp_log.h" + +#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 "widget_top_bar.hpp" +#include "widgets/lv_btn.h" +#include "widgets/lv_label.h" + +namespace ui { +namespace modals { + +static void button_cancel_cb(lv_event_t* e) { + events::Ui().Dispatch(internal::ModalCancelPressed{}); +} + +static void button_confirm_cb(lv_event_t* e) { + events::Ui().Dispatch(internal::ModalConfirmPressed{}); +} + +Confirm::Confirm(Screen* host, const std::string& title_text, bool has_cancel) + : Modal(host) { + lv_obj_set_layout(root_, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(root_, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(root_, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, + LV_FLEX_ALIGN_CENTER); + + lv_obj_t* title = lv_label_create(root_); + lv_label_set_text(title, title_text.c_str()); + lv_obj_set_size(title, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + + lv_obj_t* button_container = lv_obj_create(root_); + lv_obj_set_size(button_container, lv_pct(100), LV_SIZE_CONTENT); + lv_obj_set_layout(button_container, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(button_container, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(button_container, LV_FLEX_ALIGN_SPACE_EVENLY, + LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + + if (has_cancel) { + lv_obj_t* cancel_btn = lv_btn_create(button_container); + lv_obj_t* cancel_label = lv_label_create(cancel_btn); + lv_label_set_text(cancel_label, "Cancel"); + lv_group_add_obj(group_, cancel_btn); + lv_obj_add_event_cb(cancel_btn, button_cancel_cb, LV_EVENT_CLICKED, NULL); + } + + lv_obj_t* ok_btn = lv_btn_create(button_container); + lv_obj_t* ok_label = lv_label_create(ok_btn); + lv_label_set_text(ok_label, "Okay"); + lv_group_add_obj(group_, ok_btn); + lv_obj_add_event_cb(ok_btn, button_confirm_cb, LV_EVENT_CLICKED, NULL); +} + +} // namespace modals +} // namespace ui diff --git a/src/ui/modal_progress.cpp b/src/ui/modal_progress.cpp new file mode 100644 index 00000000..f60b324a --- /dev/null +++ b/src/ui/modal_progress.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "modal_progress.hpp" + +#include "core/lv_event.h" +#include "core/lv_obj.h" +#include "core/lv_obj_tree.h" +#include "esp_log.h" + +#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 "widget_top_bar.hpp" +#include "widgets/lv_label.h" + +namespace ui { +namespace modals { + +Progress::Progress(Screen* host, std::string title_text) : Modal(host) { + lv_obj_set_layout(root_, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(root_, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(root_, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, + LV_FLEX_ALIGN_CENTER); + + lv_obj_t* title = lv_label_create(root_); + lv_label_set_text(title, title_text.c_str()); + lv_obj_set_size(title, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + + lv_obj_t* spinner = lv_spinner_create(root_, 3000, 45); + lv_obj_set_size(spinner, 16, 16); +} + +} // namespace modals +} // namespace ui diff --git a/src/ui/screen.cpp b/src/ui/screen.cpp index 7ea5e3ce..039d2439 100644 --- a/src/ui/screen.cpp +++ b/src/ui/screen.cpp @@ -8,10 +8,38 @@ #include +#include "core/lv_obj_pos.h" +#include "core/lv_obj_tree.h" +#include "misc/lv_area.h" +#include "misc/lv_color.h" #include "widget_top_bar.hpp" namespace ui { +Screen::Screen() + : root_(lv_obj_create(NULL)), + content_(lv_obj_create(root_)), + modal_content_(lv_obj_create(root_)), + group_(lv_group_create()), + modal_group_(nullptr) { + lv_obj_set_size(root_, lv_pct(100), lv_pct(100)); + lv_obj_set_size(content_, lv_pct(100), lv_pct(100)); + lv_obj_set_size(modal_content_, lv_pct(100), lv_pct(100)); + lv_obj_center(root_); + lv_obj_center(content_); + lv_obj_center(modal_content_); + + lv_obj_set_style_bg_opa(modal_content_, LV_OPA_TRANSP, 0); + lv_obj_set_style_bg_color(modal_content_, lv_color_black(), 0); +} + +Screen::~Screen() { + // The group *must* be deleted first. Otherwise, focus events will be + // generated whilst deleting the object tree, which causes a big mess. + lv_group_del(group_); + lv_obj_del(root_); +} + auto Screen::UpdateTopBar(const widgets::TopBar::State& state) -> void { if (top_bar_) { top_bar_->Update(state); diff --git a/src/ui/screen_menu.cpp b/src/ui/screen_menu.cpp index 4730db84..8c402532 100644 --- a/src/ui/screen_menu.cpp +++ b/src/ui/screen_menu.cpp @@ -37,18 +37,18 @@ static void item_click_cb(lv_event_t* ev) { } Menu::Menu(std::vector indexes) : indexes_(indexes) { - lv_obj_set_layout(root_, LV_LAYOUT_FLEX); - lv_obj_set_flex_flow(root_, LV_FLEX_FLOW_COLUMN); - lv_obj_set_flex_align(root_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, + lv_obj_set_layout(content_, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(content_, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(content_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); widgets::TopBar::Configuration config{ .show_back_button = false, .title = "", }; - CreateTopBar(root_, config); + CreateTopBar(content_, config); - lv_obj_t* list = lv_list_create(root_); + lv_obj_t* list = lv_list_create(content_); lv_obj_set_size(list, lv_disp_get_hor_res(NULL), lv_disp_get_ver_res(NULL)); lv_obj_center(list); diff --git a/src/ui/screen_playing.cpp b/src/ui/screen_playing.cpp index cb3d866e..a1f91902 100644 --- a/src/ui/screen_playing.cpp +++ b/src/ui/screen_playing.cpp @@ -106,17 +106,17 @@ Playing::Playing(std::weak_ptr db, audio::TrackQueue* queue) next_tracks_(), new_track_(), new_next_tracks_() { - lv_obj_set_layout(root_, LV_LAYOUT_FLEX); + lv_obj_set_layout(content_, LV_LAYOUT_FLEX); lv_group_set_wrap(group_, false); - lv_obj_set_size(root_, lv_pct(100), LV_SIZE_CONTENT); - lv_obj_set_flex_flow(root_, LV_FLEX_FLOW_COLUMN); - lv_obj_set_flex_align(root_, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, + lv_obj_set_size(content_, lv_pct(100), LV_SIZE_CONTENT); + lv_obj_set_flex_flow(content_, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(content_, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); - lv_obj_set_scrollbar_mode(root_, LV_SCROLLBAR_MODE_OFF); + lv_obj_set_scrollbar_mode(content_, LV_SCROLLBAR_MODE_OFF); - lv_obj_t* above_fold_container = lv_obj_create(root_); + lv_obj_t* above_fold_container = lv_obj_create(content_); lv_obj_set_layout(above_fold_container, LV_LAYOUT_FLEX); lv_obj_set_size(above_fold_container, lv_pct(100), lv_disp_get_ver_res(NULL)); lv_obj_set_flex_flow(above_fold_container, LV_FLEX_FLOW_COLUMN); @@ -182,7 +182,7 @@ Playing::Playing(std::weak_ptr db, audio::TrackQueue* queue) lv_obj_set_style_text_font(next_up_hint_, &font_symbols, 0); lv_obj_set_size(next_up_hint_, LV_SIZE_CONTENT, lv_pct(100)); - next_up_container_ = lv_list_create(root_); + next_up_container_ = lv_list_create(content_); lv_obj_set_layout(next_up_container_, LV_LAYOUT_FLEX); lv_obj_set_size(next_up_container_, lv_pct(100), lv_disp_get_ver_res(NULL)); lv_obj_set_flex_flow(next_up_container_, LV_FLEX_FLOW_COLUMN); @@ -295,12 +295,12 @@ auto Playing::ApplyNextUp(const std::vector& tracks) -> void { } auto Playing::OnFocusAboveFold() -> void { - lv_obj_scroll_to_y(root_, 0, LV_ANIM_ON); + lv_obj_scroll_to_y(content_, 0, LV_ANIM_ON); } auto Playing::OnFocusBelowFold() -> void { - if (lv_obj_get_scroll_y(root_) < lv_obj_get_y(next_up_header_)) { - lv_obj_scroll_to_y(root_, lv_obj_get_y(next_up_header_), LV_ANIM_ON); + if (lv_obj_get_scroll_y(content_) < lv_obj_get_y(next_up_header_)) { + lv_obj_scroll_to_y(content_, lv_obj_get_y(next_up_header_), LV_ANIM_ON); } } diff --git a/src/ui/screen_track_browser.cpp b/src/ui/screen_track_browser.cpp index 4a39578e..c534b423 100644 --- a/src/ui/screen_track_browser.cpp +++ b/src/ui/screen_track_browser.cpp @@ -69,14 +69,13 @@ TrackBrowser::TrackBrowser( loading_page_(move(initial_page)), initial_page_(), current_pages_() { - lv_obj_set_layout(root_, LV_LAYOUT_FLEX); - lv_obj_set_size(root_, lv_pct(100), lv_pct(100)); - lv_obj_set_flex_flow(root_, LV_FLEX_FLOW_COLUMN); - lv_obj_set_flex_align(root_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, + lv_obj_set_layout(content_, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(content_, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(content_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); // The default scrollbar is deceptive because we load in items progressively. - lv_obj_set_scrollbar_mode(root_, LV_SCROLLBAR_MODE_OFF); + lv_obj_set_scrollbar_mode(content_, LV_SCROLLBAR_MODE_OFF); // Wrapping behaves in surprising ways, again due to progressing loading. lv_group_set_wrap(group_, false); @@ -84,10 +83,10 @@ TrackBrowser::TrackBrowser( .show_back_button = true, .title = title, }; - auto top_bar = CreateTopBar(root_, config); + auto top_bar = CreateTopBar(content_, config); back_button_ = top_bar->button(); - list_ = lv_list_create(root_); + list_ = lv_list_create(content_); lv_obj_set_width(list_, lv_pct(100)); lv_obj_set_flex_grow(list_, 1); lv_obj_center(list_); diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index 1f83477d..8b2d3a3c 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -5,13 +5,18 @@ */ #include "ui_fsm.hpp" + #include -#include "audio_events.hpp" + #include "core/lv_obj.h" +#include "misc/lv_gc.h" + +#include "audio_events.hpp" #include "display.hpp" #include "event_queue.hpp" #include "gpios.hpp" #include "lvgl_task.hpp" +#include "modal_confirm.hpp" #include "relative_wheel.hpp" #include "screen.hpp" #include "screen_menu.hpp" @@ -23,6 +28,7 @@ #include "touchwheel.hpp" #include "track_queue.hpp" #include "ui_events.hpp" +#include "widget_top_bar.hpp" namespace ui { @@ -40,6 +46,7 @@ std::weak_ptr UiState::sDb; std::stack> UiState::sScreens; std::shared_ptr UiState::sCurrentScreen; +std::shared_ptr UiState::sCurrentModal; auto UiState::Init(drivers::IGpios* gpio_expander, audio::TrackQueue* queue) -> bool { @@ -75,6 +82,7 @@ void UiState::PushScreen(std::shared_ptr screen) { sScreens.push(sCurrentScreen); } sCurrentScreen = screen; + UpdateTopBar(); } void UiState::PopScreen() { @@ -83,12 +91,28 @@ void UiState::PopScreen() { } sCurrentScreen = sScreens.top(); sScreens.pop(); + UpdateTopBar(); } void UiState::react(const system_fsm::KeyLockChanged& ev) { sDisplay->SetDisplayOn(ev.falling); } +void UiState::react(const system_fsm::BatteryPercentChanged&) { + UpdateTopBar(); +} + +void UiState::UpdateTopBar() { + widgets::TopBar::State state{ + .playback_state = widgets::TopBar::PlaybackState::kIdle, + .battery_percent = 50, + .is_charging = true, + }; + if (sCurrentScreen) { + sCurrentScreen->UpdateTopBar(state); + } +} + namespace states { void Splash::exit() {