Add modal dialog support

custom
jacqueline 2 years ago
parent 079b2b53d4
commit 3b3bc64d19
  1. 1
      src/drivers/touchwheel.cpp
  2. 1
      src/ui/CMakeLists.txt
  3. 37
      src/ui/include/modal.hpp
  4. 29
      src/ui/include/modal_confirm.hpp
  5. 29
      src/ui/include/modal_progress.hpp
  6. 26
      src/ui/include/screen.hpp
  7. 3
      src/ui/include/ui_events.hpp
  8. 11
      src/ui/include/ui_fsm.hpp
  9. 7
      src/ui/lvgl_task.cpp
  10. 57
      src/ui/modal.cpp
  11. 74
      src/ui/modal_confirm.cpp
  12. 46
      src/ui/modal_progress.cpp
  13. 28
      src/ui/screen.cpp
  14. 10
      src/ui/screen_menu.cpp
  15. 20
      src/ui/screen_playing.cpp
  16. 13
      src/ui/screen_track_browser.cpp
  17. 26
      src/ui/ui_fsm.cpp

@ -59,7 +59,6 @@ TouchWheel::TouchWheel() {
for (int i = 5; i < 12; i++) { for (int i = 5; i < 12; i++) {
WriteRegister(Register::KEY_CONTROL_BASE + i, 1); WriteRegister(Register::KEY_CONTROL_BASE + i, 1);
} }
} }
TouchWheel::~TouchWheel() {} TouchWheel::~TouchWheel() {}

@ -6,6 +6,7 @@ idf_component_register(
SRCS "lvgl_task.cpp" "ui_fsm.cpp" "screen_splash.cpp" "screen_menu.cpp" SRCS "lvgl_task.cpp" "ui_fsm.cpp" "screen_splash.cpp" "screen_menu.cpp"
"wheel_encoder.cpp" "screen_track_browser.cpp" "screen_playing.cpp" "wheel_encoder.cpp" "screen_track_browser.cpp" "screen_playing.cpp"
"themes.cpp" "widget_top_bar.cpp" "screen.cpp" "screen_onboarding.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" "splash.c" "font_fusion.c" "font_symbols.c"
INCLUDE_DIRS "include" INCLUDE_DIRS "include"
REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer") REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer")

@ -0,0 +1,37 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#pragma once
#include <memory>
#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

@ -0,0 +1,29 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#pragma once
#include <memory>
#include <vector>
#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

@ -0,0 +1,29 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#pragma once
#include <memory>
#include <vector>
#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

@ -7,6 +7,7 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <optional>
#include "core/lv_group.h" #include "core/lv_group.h"
#include "core/lv_obj.h" #include "core/lv_obj.h"
@ -23,14 +24,8 @@ namespace ui {
*/ */
class Screen { class Screen {
public: public:
Screen() : root_(lv_obj_create(NULL)), group_(lv_group_create()) {} Screen();
virtual ~Screen();
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_);
}
/* /*
* Called periodically to allow the screen to update itself, e.g. to handle * 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 UpdateTopBar(const widgets::TopBar::State& state) -> void;
auto root() -> lv_obj_t* { return root_; } 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: protected:
auto CreateTopBar(lv_obj_t* parent, const widgets::TopBar::Configuration&) auto CreateTopBar(lv_obj_t* parent, const widgets::TopBar::Configuration&)
-> widgets::TopBar*; -> widgets::TopBar*;
lv_obj_t* const root_; lv_obj_t* const root_;
lv_obj_t* const content_;
lv_obj_t* const modal_content_;
lv_group_t* const group_; lv_group_t* const group_;
lv_group_t* modal_group_;
private: private:
std::unique_ptr<widgets::TopBar> top_bar_; std::unique_ptr<widgets::TopBar> top_bar_;

@ -37,6 +37,9 @@ struct IndexSelected : tinyfsm::Event {
struct BackPressed : tinyfsm::Event {}; struct BackPressed : tinyfsm::Event {};
struct ModalConfirmPressed : tinyfsm::Event {};
struct ModalCancelPressed : tinyfsm::Event {};
} // namespace internal } // namespace internal
} // namespace ui } // namespace ui

@ -15,6 +15,7 @@
#include "tinyfsm.hpp" #include "tinyfsm.hpp"
#include "display.hpp" #include "display.hpp"
#include "modal.hpp"
#include "screen.hpp" #include "screen.hpp"
#include "storage.hpp" #include "storage.hpp"
#include "system_events.hpp" #include "system_events.hpp"
@ -40,6 +41,8 @@ class UiState : public tinyfsm::Fsm<UiState> {
/* Fallback event handler. Does nothing. */ /* Fallback event handler. Does nothing. */
void react(const tinyfsm::Event& ev) {} void react(const tinyfsm::Event& ev) {}
void react(const system_fsm::BatteryPercentChanged&);
virtual void react(const audio::PlaybackStarted&) {} virtual void react(const audio::PlaybackStarted&) {}
virtual void react(const audio::PlaybackUpdate&) {} virtual void react(const audio::PlaybackUpdate&) {}
virtual void react(const audio::QueueUpdate&) {} virtual void react(const audio::QueueUpdate&) {}
@ -49,6 +52,12 @@ class UiState : public tinyfsm::Fsm<UiState> {
virtual void react(const internal::RecordSelected&) {} virtual void react(const internal::RecordSelected&) {}
virtual void react(const internal::IndexSelected&) {} virtual void react(const internal::IndexSelected&) {}
virtual void react(const internal::BackPressed&) {} 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::DisplayReady&) {}
virtual void react(const system_fsm::BootComplete&) {} virtual void react(const system_fsm::BootComplete&) {}
@ -57,6 +66,7 @@ class UiState : public tinyfsm::Fsm<UiState> {
protected: protected:
void PushScreen(std::shared_ptr<Screen>); void PushScreen(std::shared_ptr<Screen>);
void PopScreen(); void PopScreen();
void UpdateTopBar();
static drivers::IGpios* sIGpios; static drivers::IGpios* sIGpios;
static audio::TrackQueue* sQueue; static audio::TrackQueue* sQueue;
@ -68,6 +78,7 @@ class UiState : public tinyfsm::Fsm<UiState> {
static std::stack<std::shared_ptr<Screen>> sScreens; static std::stack<std::shared_ptr<Screen>> sScreens;
static std::shared_ptr<Screen> sCurrentScreen; static std::shared_ptr<Screen> sCurrentScreen;
static std::shared_ptr<Modal> sCurrentModal;
}; };
namespace states { namespace states {

@ -35,6 +35,7 @@
#include "misc/lv_color.h" #include "misc/lv_color.h"
#include "misc/lv_style.h" #include "misc/lv_style.h"
#include "misc/lv_timer.h" #include "misc/lv_timer.h"
#include "modal.hpp"
#include "relative_wheel.hpp" #include "relative_wheel.hpp"
#include "tasks.hpp" #include "tasks.hpp"
#include "touchwheel.hpp" #include "touchwheel.hpp"
@ -75,6 +76,7 @@ void LvglMain(std::weak_ptr<drivers::RelativeWheel> weak_touch_wheel,
TouchWheelEncoder encoder(weak_touch_wheel); TouchWheelEncoder encoder(weak_touch_wheel);
std::shared_ptr<Screen> current_screen; std::shared_ptr<Screen> current_screen;
lv_group_t* current_group = nullptr;
auto* events = events::queues::Ui(); auto* events = events::queues::Ui();
while (1) { while (1) {
while (events->Service(0)) { while (events->Service(0)) {
@ -88,6 +90,11 @@ void LvglMain(std::weak_ptr<drivers::RelativeWheel> weak_touch_wheel,
current_screen = screen; 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) { if (current_screen) {
current_screen->Tick(); current_screen->Tick();
} }

@ -0,0 +1,57 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* 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

@ -0,0 +1,74 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* 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

@ -0,0 +1,46 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* 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

@ -8,10 +8,38 @@
#include <memory> #include <memory>
#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" #include "widget_top_bar.hpp"
namespace ui { 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 { auto Screen::UpdateTopBar(const widgets::TopBar::State& state) -> void {
if (top_bar_) { if (top_bar_) {
top_bar_->Update(state); top_bar_->Update(state);

@ -37,18 +37,18 @@ static void item_click_cb(lv_event_t* ev) {
} }
Menu::Menu(std::vector<database::IndexInfo> indexes) : indexes_(indexes) { Menu::Menu(std::vector<database::IndexInfo> indexes) : indexes_(indexes) {
lv_obj_set_layout(root_, LV_LAYOUT_FLEX); lv_obj_set_layout(content_, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(root_, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(content_, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(root_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, lv_obj_set_flex_align(content_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_CENTER); LV_FLEX_ALIGN_CENTER);
widgets::TopBar::Configuration config{ widgets::TopBar::Configuration config{
.show_back_button = false, .show_back_button = false,
.title = "", .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_set_size(list, lv_disp_get_hor_res(NULL), lv_disp_get_ver_res(NULL));
lv_obj_center(list); lv_obj_center(list);

@ -106,17 +106,17 @@ Playing::Playing(std::weak_ptr<database::Database> db, audio::TrackQueue* queue)
next_tracks_(), next_tracks_(),
new_track_(), new_track_(),
new_next_tracks_() { 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_group_set_wrap(group_, false);
lv_obj_set_size(root_, lv_pct(100), LV_SIZE_CONTENT); lv_obj_set_size(content_, lv_pct(100), LV_SIZE_CONTENT);
lv_obj_set_flex_flow(root_, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(content_, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(root_, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, lv_obj_set_flex_align(content_, LV_FLEX_ALIGN_START, 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_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_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); lv_obj_set_flex_flow(above_fold_container, LV_FLEX_FLOW_COLUMN);
@ -182,7 +182,7 @@ Playing::Playing(std::weak_ptr<database::Database> db, audio::TrackQueue* queue)
lv_obj_set_style_text_font(next_up_hint_, &font_symbols, 0); 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)); 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_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_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); lv_obj_set_flex_flow(next_up_container_, LV_FLEX_FLOW_COLUMN);
@ -295,12 +295,12 @@ auto Playing::ApplyNextUp(const std::vector<database::Track>& tracks) -> void {
} }
auto Playing::OnFocusAboveFold() -> 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 { auto Playing::OnFocusBelowFold() -> void {
if (lv_obj_get_scroll_y(root_) < lv_obj_get_y(next_up_header_)) { if (lv_obj_get_scroll_y(content_) < lv_obj_get_y(next_up_header_)) {
lv_obj_scroll_to_y(root_, lv_obj_get_y(next_up_header_), LV_ANIM_ON); lv_obj_scroll_to_y(content_, lv_obj_get_y(next_up_header_), LV_ANIM_ON);
} }
} }

@ -69,14 +69,13 @@ TrackBrowser::TrackBrowser(
loading_page_(move(initial_page)), loading_page_(move(initial_page)),
initial_page_(), initial_page_(),
current_pages_() { current_pages_() {
lv_obj_set_layout(root_, LV_LAYOUT_FLEX); lv_obj_set_layout(content_, LV_LAYOUT_FLEX);
lv_obj_set_size(root_, lv_pct(100), lv_pct(100)); lv_obj_set_flex_flow(content_, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_flow(root_, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_align(content_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER,
lv_obj_set_flex_align(root_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_CENTER); LV_FLEX_ALIGN_CENTER);
// The default scrollbar is deceptive because we load in items progressively. // 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. // Wrapping behaves in surprising ways, again due to progressing loading.
lv_group_set_wrap(group_, false); lv_group_set_wrap(group_, false);
@ -84,10 +83,10 @@ TrackBrowser::TrackBrowser(
.show_back_button = true, .show_back_button = true,
.title = title, .title = title,
}; };
auto top_bar = CreateTopBar(root_, config); auto top_bar = CreateTopBar(content_, config);
back_button_ = top_bar->button(); 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_width(list_, lv_pct(100));
lv_obj_set_flex_grow(list_, 1); lv_obj_set_flex_grow(list_, 1);
lv_obj_center(list_); lv_obj_center(list_);

@ -5,13 +5,18 @@
*/ */
#include "ui_fsm.hpp" #include "ui_fsm.hpp"
#include <memory> #include <memory>
#include "audio_events.hpp"
#include "core/lv_obj.h" #include "core/lv_obj.h"
#include "misc/lv_gc.h"
#include "audio_events.hpp"
#include "display.hpp" #include "display.hpp"
#include "event_queue.hpp" #include "event_queue.hpp"
#include "gpios.hpp" #include "gpios.hpp"
#include "lvgl_task.hpp" #include "lvgl_task.hpp"
#include "modal_confirm.hpp"
#include "relative_wheel.hpp" #include "relative_wheel.hpp"
#include "screen.hpp" #include "screen.hpp"
#include "screen_menu.hpp" #include "screen_menu.hpp"
@ -23,6 +28,7 @@
#include "touchwheel.hpp" #include "touchwheel.hpp"
#include "track_queue.hpp" #include "track_queue.hpp"
#include "ui_events.hpp" #include "ui_events.hpp"
#include "widget_top_bar.hpp"
namespace ui { namespace ui {
@ -40,6 +46,7 @@ std::weak_ptr<database::Database> UiState::sDb;
std::stack<std::shared_ptr<Screen>> UiState::sScreens; std::stack<std::shared_ptr<Screen>> UiState::sScreens;
std::shared_ptr<Screen> UiState::sCurrentScreen; std::shared_ptr<Screen> UiState::sCurrentScreen;
std::shared_ptr<Modal> UiState::sCurrentModal;
auto UiState::Init(drivers::IGpios* gpio_expander, audio::TrackQueue* queue) auto UiState::Init(drivers::IGpios* gpio_expander, audio::TrackQueue* queue)
-> bool { -> bool {
@ -75,6 +82,7 @@ void UiState::PushScreen(std::shared_ptr<Screen> screen) {
sScreens.push(sCurrentScreen); sScreens.push(sCurrentScreen);
} }
sCurrentScreen = screen; sCurrentScreen = screen;
UpdateTopBar();
} }
void UiState::PopScreen() { void UiState::PopScreen() {
@ -83,12 +91,28 @@ void UiState::PopScreen() {
} }
sCurrentScreen = sScreens.top(); sCurrentScreen = sScreens.top();
sScreens.pop(); sScreens.pop();
UpdateTopBar();
} }
void UiState::react(const system_fsm::KeyLockChanged& ev) { void UiState::react(const system_fsm::KeyLockChanged& ev) {
sDisplay->SetDisplayOn(ev.falling); 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 { namespace states {
void Splash::exit() { void Splash::exit() {

Loading…
Cancel
Save