From 6a47edcd35884095946f761fa3aa2367c7c26442 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 28 Sep 2023 10:43:48 +1000 Subject: [PATCH] Use databinding for the top bar. It's so nice now! --- src/app_console/app_console.cpp | 4 -- src/battery/battery.cpp | 18 ++++-- src/system_fsm/include/system_events.hpp | 5 +- src/ui/include/model_top_bar.hpp | 26 ++++++++ src/ui/include/screen.hpp | 12 ++-- src/ui/include/screen_menu.hpp | 3 +- src/ui/include/screen_playing.hpp | 4 +- src/ui/include/screen_settings.hpp | 19 +++--- src/ui/include/screen_track_browser.hpp | 2 + src/ui/include/ui_fsm.hpp | 5 +- src/ui/include/widget_top_bar.hpp | 27 ++------ src/ui/screen.cpp | 19 +++--- src/ui/screen_menu.cpp | 5 +- src/ui/screen_playing.cpp | 6 +- src/ui/screen_settings.cpp | 29 +++++---- src/ui/screen_track_browser.cpp | 4 +- src/ui/ui_fsm.cpp | 74 +++++++--------------- src/ui/widget_top_bar.cpp | 81 +++++++++++++----------- 18 files changed, 181 insertions(+), 162 deletions(-) create mode 100644 src/ui/include/model_top_bar.hpp diff --git a/src/app_console/app_console.cpp b/src/app_console/app_console.cpp index 83406650..04f1866d 100644 --- a/src/app_console/app_console.cpp +++ b/src/app_console/app_console.cpp @@ -336,7 +336,6 @@ void RegisterDbDump() { esp_console_cmd_register(&cmd); } -#if CONFIG_APPTRACE_ENABLE int CmdTasks(int argc, char** argv) { if (!configUSE_TRACE_FACILITY) { std::cout << "configUSE_TRACE_FACILITY must be enabled" << std::endl; @@ -446,7 +445,6 @@ void RegisterTasks() { .argtable = NULL}; esp_console_cmd_register(&cmd); } -#endif int CmdHeaps(int argc, char** argv) { static const std::pmr::string usage = "usage: heaps"; @@ -638,9 +636,7 @@ auto AppConsole::RegisterExtraComponents() -> void { RegisterDbTracks(); RegisterDbIndex(); RegisterDbDump(); -#if CONFIG_APPTRACE_ENABLE RegisterTasks(); -#endif RegisterHeaps(); diff --git a/src/battery/battery.cpp b/src/battery/battery.cpp index 9ac6ebf1..92185db7 100644 --- a/src/battery/battery.cpp +++ b/src/battery/battery.cpp @@ -77,15 +77,16 @@ auto Battery::Update() -> void { *charge_state == ChargeStatus::kChargingFast || *charge_state == ChargeStatus::kFullCharge; - if (!state_ || state_->is_charging != is_charging || - state_->percent != percent) { - EmitEvent(); + if (state_ && state_->is_charging == is_charging && + state_->percent == percent) { + return; } state_ = BatteryState{ .percent = percent, .is_charging = is_charging, }; + EmitEvent(); } auto Battery::State() -> std::optional { @@ -94,8 +95,15 @@ auto Battery::State() -> std::optional { } auto Battery::EmitEvent() -> void { - events::System().Dispatch(system_fsm::BatteryStateChanged{}); - events::Ui().Dispatch(system_fsm::BatteryStateChanged{}); + auto state = state_; + if (!state) { + return; + } + system_fsm::BatteryStateChanged ev{ + .new_state = *state, + }; + events::System().Dispatch(ev); + events::Ui().Dispatch(ev); } } // namespace battery diff --git a/src/system_fsm/include/system_events.hpp b/src/system_fsm/include/system_events.hpp index 9cd75d4d..7b21dbb5 100644 --- a/src/system_fsm/include/system_events.hpp +++ b/src/system_fsm/include/system_events.hpp @@ -8,6 +8,7 @@ #include +#include "battery.hpp" #include "database.hpp" #include "service_locator.hpp" #include "tinyfsm.hpp" @@ -54,7 +55,9 @@ struct HasPhonesChanged : tinyfsm::Event { }; struct ChargingStatusChanged : tinyfsm::Event {}; -struct BatteryStateChanged : tinyfsm::Event {}; +struct BatteryStateChanged : tinyfsm::Event { + battery::Battery::BatteryState new_state; +}; struct BluetoothDevicesChanged : tinyfsm::Event {}; diff --git a/src/ui/include/model_top_bar.hpp b/src/ui/include/model_top_bar.hpp new file mode 100644 index 00000000..c0f148f3 --- /dev/null +++ b/src/ui/include/model_top_bar.hpp @@ -0,0 +1,26 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include "battery.hpp" +#include "bindey/property.h" + +#include "track.hpp" + +namespace ui { +namespace models { + +struct TopBar { + bindey::property battery_state; + + // Shared with the Playback model + bindey::property& is_playing; + bindey::property>& current_track; +}; + +} // namespace models +} // namespace ui diff --git a/src/ui/include/screen.hpp b/src/ui/include/screen.hpp index ac7b19f8..e9eaeeb0 100644 --- a/src/ui/include/screen.hpp +++ b/src/ui/include/screen.hpp @@ -16,6 +16,7 @@ #include "core/lv_obj_tree.h" #include "event_binding.hpp" #include "lvgl.h" +#include "model_top_bar.hpp" #include "nod/nod.hpp" #include "widget_top_bar.hpp" @@ -37,8 +38,6 @@ class Screen { */ virtual auto Tick() -> void {} - auto UpdateTopBar(const widgets::TopBar::State& state) -> void; - auto root() -> lv_obj_t* { return root_; } auto content() -> lv_obj_t* { return content_; } @@ -52,8 +51,9 @@ class Screen { } protected: - auto CreateTopBar(lv_obj_t* parent, const widgets::TopBar::Configuration&) - -> widgets::TopBar*; + auto CreateTopBar(lv_obj_t* parent, + const widgets::TopBar::Configuration&, + models::TopBar& model) -> widgets::TopBar*; std::pmr::vector data_bindings_; std::pmr::vector> event_bindings_; @@ -78,7 +78,9 @@ class Screen { class MenuScreen : public Screen { public: - MenuScreen(const std::pmr::string& title, bool show_back_button = true); + MenuScreen(models::TopBar&, + const std::pmr::string& title, + bool show_back_button = true); }; } // namespace ui diff --git a/src/ui/include/screen_menu.hpp b/src/ui/include/screen_menu.hpp index be2a9493..a83346f6 100644 --- a/src/ui/include/screen_menu.hpp +++ b/src/ui/include/screen_menu.hpp @@ -12,6 +12,7 @@ #include "index.hpp" #include "lvgl.h" +#include "model_top_bar.hpp" #include "screen.hpp" #include "screen_settings.hpp" @@ -20,7 +21,7 @@ namespace screens { class Menu : public MenuScreen { public: - explicit Menu(std::vector indexes); + explicit Menu(models::TopBar&, std::vector indexes); ~Menu(); private: diff --git a/src/ui/include/screen_playing.hpp b/src/ui/include/screen_playing.hpp index fff9cc35..185c55cc 100644 --- a/src/ui/include/screen_playing.hpp +++ b/src/ui/include/screen_playing.hpp @@ -18,6 +18,7 @@ #include "database.hpp" #include "future_fetcher.hpp" #include "model_playback.hpp" +#include "model_top_bar.hpp" #include "screen.hpp" #include "track.hpp" #include "track_queue.hpp" @@ -31,7 +32,8 @@ namespace screens { */ class Playing : public Screen { public: - explicit Playing(models::Playback& playback_model, + explicit Playing(models::TopBar&, + models::Playback& playback_model, std::weak_ptr db, audio::TrackQueue& queue); ~Playing(); diff --git a/src/ui/include/screen_settings.hpp b/src/ui/include/screen_settings.hpp index 4e1936a2..1a4672ed 100644 --- a/src/ui/include/screen_settings.hpp +++ b/src/ui/include/screen_settings.hpp @@ -18,6 +18,7 @@ #include "index.hpp" #include "lvgl.h" +#include "model_top_bar.hpp" #include "nvs.hpp" #include "screen.hpp" @@ -26,12 +27,12 @@ namespace screens { class Settings : public MenuScreen { public: - Settings(); + Settings(models::TopBar&); }; class Bluetooth : public MenuScreen { public: - Bluetooth(drivers::Bluetooth& bt, drivers::NvsStorage& nvs); + Bluetooth(models::TopBar&, drivers::Bluetooth& bt, drivers::NvsStorage& nvs); auto ChangeEnabledState(bool enabled) -> void; auto RefreshDevicesList() -> void; @@ -53,7 +54,7 @@ class Bluetooth : public MenuScreen { class Headphones : public MenuScreen { public: - Headphones(drivers::NvsStorage& nvs); + Headphones(models::TopBar&, drivers::NvsStorage& nvs); auto ChangeMaxVolume(uint8_t index) -> void; auto ChangeCustomVolume(int8_t diff) -> void; @@ -71,7 +72,9 @@ class Headphones : public MenuScreen { class Appearance : public MenuScreen { public: - Appearance(drivers::NvsStorage& nvs, drivers::Display& display); + Appearance(models::TopBar&, + drivers::NvsStorage& nvs, + drivers::Display& display); auto ChangeBrightness(uint_fast8_t) -> void; auto CommitBrightness() -> void; @@ -86,22 +89,22 @@ class Appearance : public MenuScreen { class InputMethod : public MenuScreen { public: - InputMethod(); + InputMethod(models::TopBar&); }; class Storage : public MenuScreen { public: - Storage(); + Storage(models::TopBar&); }; class FirmwareUpdate : public MenuScreen { public: - FirmwareUpdate(); + FirmwareUpdate(models::TopBar&); }; class About : public MenuScreen { public: - About(); + About(models::TopBar&); }; } // namespace screens diff --git a/src/ui/include/screen_track_browser.hpp b/src/ui/include/screen_track_browser.hpp index fdeb3afe..719306f0 100644 --- a/src/ui/include/screen_track_browser.hpp +++ b/src/ui/include/screen_track_browser.hpp @@ -14,6 +14,7 @@ #include "lvgl.h" #include "database.hpp" +#include "model_top_bar.hpp" #include "screen.hpp" namespace ui { @@ -22,6 +23,7 @@ namespace screens { class TrackBrowser : public Screen { public: TrackBrowser( + models::TopBar&, std::weak_ptr db, const std::pmr::string& title, std::future*>&& initial_page); diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index cb3e651c..24f0c270 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -17,6 +17,7 @@ #include "gpios.hpp" #include "lvgl_task.hpp" #include "model_playback.hpp" +#include "model_top_bar.hpp" #include "nvs.hpp" #include "relative_wheel.hpp" #include "screen_playing.hpp" @@ -82,7 +83,6 @@ class UiState : public tinyfsm::Fsm { protected: void PushScreen(std::shared_ptr); void PopScreen(); - void UpdateTopBar(); static std::unique_ptr sTask; static std::shared_ptr sServices; @@ -94,8 +94,7 @@ class UiState : public tinyfsm::Fsm { static std::shared_ptr sCurrentModal; static models::Playback sPlaybackModel; - - static bindey::property sPropBatteryState; + static models::TopBar sTopBarModel; }; namespace states { diff --git a/src/ui/include/widget_top_bar.hpp b/src/ui/include/widget_top_bar.hpp index 1a2c826a..b240188c 100644 --- a/src/ui/include/widget_top_bar.hpp +++ b/src/ui/include/widget_top_bar.hpp @@ -9,9 +9,11 @@ #include #include +#include "bindey/binding.h" #include "lvgl.h" #include "memory_resource.hpp" +#include "model_top_bar.hpp" namespace ui { @@ -24,33 +26,18 @@ class TopBar { std::pmr::string title; }; - enum class PlaybackState { - kIdle, - kPaused, - kPlaying, - }; - - struct State { - PlaybackState playback_state; - uint_fast8_t battery_percent; - bool is_charging; - }; - - explicit TopBar(lv_obj_t* parent, const Configuration& config); + explicit TopBar(lv_obj_t* parent, + const Configuration& config, + models::TopBar& model); auto root() -> lv_obj_t* { return container_; } auto button() -> lv_obj_t* { return back_button_; } - auto Update(const State&) -> void; - private: - lv_obj_t* container_; + std::vector bindings_; + lv_obj_t* container_; lv_obj_t* back_button_; - lv_obj_t* title_; - lv_obj_t* playback_; - lv_obj_t* battery_; - lv_obj_t* charging_; }; } // namespace widgets diff --git a/src/ui/screen.cpp b/src/ui/screen.cpp index 48bffff7..bdc30fcd 100644 --- a/src/ui/screen.cpp +++ b/src/ui/screen.cpp @@ -12,6 +12,7 @@ #include "core/lv_obj_tree.h" #include "misc/lv_area.h" #include "misc/lv_color.h" +#include "model_top_bar.hpp" #include "widget_top_bar.hpp" namespace ui { @@ -40,24 +41,20 @@ Screen::~Screen() { lv_obj_del(root_); } -auto Screen::UpdateTopBar(const widgets::TopBar::State& state) -> void { - if (top_bar_) { - top_bar_->Update(state); - } -} - auto Screen::CreateTopBar(lv_obj_t* parent, - const widgets::TopBar::Configuration& config) - -> widgets::TopBar* { + const widgets::TopBar::Configuration& config, + models::TopBar& model) -> widgets::TopBar* { assert(top_bar_ == nullptr); - top_bar_ = std::make_unique(parent, config); + top_bar_ = std::make_unique(parent, config, model); if (top_bar_->button()) { lv_group_add_obj(group_, top_bar_->button()); } return top_bar_.get(); } -MenuScreen::MenuScreen(const std::pmr::string& title, bool show_back_button) +MenuScreen::MenuScreen(models::TopBar& top_bar_model, + const std::pmr::string& title, + bool show_back_button) : Screen() { lv_group_set_wrap(group_, false); @@ -70,7 +67,7 @@ MenuScreen::MenuScreen(const std::pmr::string& title, bool show_back_button) .show_back_button = show_back_button, .title = title.c_str(), }; - CreateTopBar(content_, config); + CreateTopBar(content_, config, top_bar_model); content_ = lv_obj_create(content_); lv_obj_set_flex_grow(content_, 1); diff --git a/src/ui/screen_menu.cpp b/src/ui/screen_menu.cpp index 3d36c017..44f49b32 100644 --- a/src/ui/screen_menu.cpp +++ b/src/ui/screen_menu.cpp @@ -18,6 +18,7 @@ #include "hal/lv_hal_disp.h" #include "index.hpp" #include "misc/lv_area.h" +#include "model_top_bar.hpp" #include "ui_events.hpp" #include "ui_fsm.hpp" #include "widget_top_bar.hpp" @@ -45,8 +46,8 @@ static void index_click_cb(lv_event_t* ev) { events::Ui().Dispatch(internal::IndexSelected{.index = *index}); } -Menu::Menu(std::vector indexes) - : MenuScreen(" ", false), indexes_(indexes) { +Menu::Menu(models::TopBar& top_bar, std::vector indexes) + : MenuScreen(top_bar, " ", false), indexes_(indexes) { lv_obj_t* list = lv_list_create(content_); lv_obj_set_size(list, lv_pct(100), lv_pct(100)); diff --git a/src/ui/screen_playing.cpp b/src/ui/screen_playing.cpp index 547bcf98..54e5feaf 100644 --- a/src/ui/screen_playing.cpp +++ b/src/ui/screen_playing.cpp @@ -37,6 +37,7 @@ #include "misc/lv_color.h" #include "misc/lv_txt.h" #include "model_playback.hpp" +#include "model_top_bar.hpp" #include "track.hpp" #include "ui_events.hpp" #include "ui_fsm.hpp" @@ -101,7 +102,8 @@ auto Playing::next_up_label(lv_obj_t* parent, const std::pmr::string& text) return button; } -Playing::Playing(models::Playback& playback_model, +Playing::Playing(models::TopBar& top_bar_model, + models::Playback& playback_model, std::weak_ptr db, audio::TrackQueue& queue) : db_(db), @@ -158,7 +160,7 @@ Playing::Playing(models::Playback& playback_model, .show_back_button = true, .title = "Now Playing", }; - CreateTopBar(above_fold_container, config); + CreateTopBar(above_fold_container, config, top_bar_model); lv_obj_t* info_container = lv_obj_create(above_fold_container); lv_obj_set_layout(info_container, LV_LAYOUT_FLEX); diff --git a/src/ui/screen_settings.cpp b/src/ui/screen_settings.cpp index a480f920..a2dc4296 100644 --- a/src/ui/screen_settings.cpp +++ b/src/ui/screen_settings.cpp @@ -29,6 +29,7 @@ #include "index.hpp" #include "misc/lv_anim.h" #include "misc/lv_area.h" +#include "model_top_bar.hpp" #include "nvs.hpp" #include "screen.hpp" #include "ui_events.hpp" @@ -64,7 +65,7 @@ static void sub_menu(lv_obj_t* list, reinterpret_cast(static_cast(page))); } -Settings::Settings() : MenuScreen("Settings") { +Settings::Settings(models::TopBar& bar) : MenuScreen(bar, "Settings") { lv_obj_t* list = lv_list_create(content_); lv_obj_set_size(list, lv_pct(100), lv_pct(100)); @@ -113,8 +114,10 @@ static auto select_device_cb(lv_event_t* ev) { instance->OnDeviceSelected(lv_obj_get_index(ev->target)); } -Bluetooth::Bluetooth(drivers::Bluetooth& bt, drivers::NvsStorage& nvs) - : MenuScreen("Bluetooth"), bt_(bt), nvs_(nvs) { +Bluetooth::Bluetooth(models::TopBar& bar, + drivers::Bluetooth& bt, + drivers::NvsStorage& nvs) + : MenuScreen(bar, "Bluetooth"), bt_(bt), nvs_(nvs) { lv_obj_t* toggle_container = settings_container(content_); lv_obj_t* toggle_label = lv_label_create(toggle_container); lv_label_set_text(toggle_label, "Enable"); @@ -268,8 +271,8 @@ static void decrease_vol_limit_cb(lv_event_t* ev) { instance->ChangeCustomVolume(-2); } -Headphones::Headphones(drivers::NvsStorage& nvs) - : MenuScreen("Headphones"), nvs_(nvs), custom_limit_(0) { +Headphones::Headphones(models::TopBar& bar, drivers::NvsStorage& nvs) + : MenuScreen(bar, "Headphones"), nvs_(nvs), custom_limit_(0) { uint16_t reference = drivers::wm8523::kLineLevelReferenceVolume; index_to_level_.push_back(reference - (10 * 4)); index_to_level_.push_back(reference + (6 * 4)); @@ -377,8 +380,10 @@ static auto brightness_str(uint_fast8_t percent) -> std::string { return std::to_string(percent) + "%"; } -Appearance::Appearance(drivers::NvsStorage& nvs, drivers::Display& display) - : MenuScreen("Appearance"), nvs_(nvs), display_(display) { +Appearance::Appearance(models::TopBar& bar, + drivers::NvsStorage& nvs, + drivers::Display& display) + : MenuScreen(bar, "Appearance"), nvs_(nvs), display_(display) { lv_obj_t* toggle_container = settings_container(content_); lv_obj_t* toggle_label = lv_label_create(toggle_container); lv_obj_set_flex_grow(toggle_label, 1); @@ -417,7 +422,8 @@ auto Appearance::CommitBrightness() -> void { nvs_.ScreenBrightness(current_brightness_); } -InputMethod::InputMethod() : MenuScreen("Input Method") { +InputMethod::InputMethod(models::TopBar& bar) + : MenuScreen(bar, "Input Method") { lv_obj_t* wheel_label = lv_label_create(content_); lv_label_set_text(wheel_label, "What does the wheel do?"); lv_obj_t* wheel_dropdown = lv_dropdown_create(content_); @@ -431,7 +437,7 @@ InputMethod::InputMethod() : MenuScreen("Input Method") { lv_group_add_obj(group_, buttons_dropdown); } -Storage::Storage() : MenuScreen("Storage") { +Storage::Storage(models::TopBar& bar) : MenuScreen(bar, "Storage") { label_pair(content_, "Storage Capacity:", "32 GiB"); label_pair(content_, "Currently Used:", "6 GiB"); label_pair(content_, "DB Size:", "1.2 MiB"); @@ -446,7 +452,8 @@ Storage::Storage() : MenuScreen("Storage") { lv_group_add_obj(group_, reset_btn); } -FirmwareUpdate::FirmwareUpdate() : MenuScreen("Firmware Update") { +FirmwareUpdate::FirmwareUpdate(models::TopBar& bar) + : MenuScreen(bar, "Firmware Update") { label_pair(content_, "ESP32 FW:", "vIDKLOL"); label_pair(content_, "SAMD21 FW:", "vIDKLOL"); @@ -461,7 +468,7 @@ FirmwareUpdate::FirmwareUpdate() : MenuScreen("Firmware Update") { lv_group_add_obj(group_, flash_samd_btn); } -About::About() : MenuScreen("About") { +About::About(models::TopBar& bar) : MenuScreen(bar, "About") { lv_obj_t* label = lv_label_create(content_); lv_label_set_text(label, "Some licenses or whatever"); } diff --git a/src/ui/screen_track_browser.cpp b/src/ui/screen_track_browser.cpp index 8d1fe653..ba27ad5e 100644 --- a/src/ui/screen_track_browser.cpp +++ b/src/ui/screen_track_browser.cpp @@ -16,6 +16,7 @@ #include "font/lv_symbol_def.h" #include "lvgl.h" #include "misc/lv_anim.h" +#include "model_top_bar.hpp" #include "screen_menu.hpp" #include "core/lv_event.h" @@ -59,6 +60,7 @@ static void item_select_cb(lv_event_t* ev) { } TrackBrowser::TrackBrowser( + models::TopBar& top_bar_model, std::weak_ptr db, const std::pmr::string& title, std::future*>&& initial_page) @@ -83,7 +85,7 @@ TrackBrowser::TrackBrowser( .show_back_button = true, .title = title, }; - auto top_bar = CreateTopBar(content_, config); + auto top_bar = CreateTopBar(content_, config, top_bar_model); back_button_ = top_bar->button(); list_ = lv_list_create(content_); diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index d7bb9bb7..f66e3e8a 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -54,8 +54,9 @@ std::shared_ptr UiState::sCurrentScreen; std::shared_ptr UiState::sCurrentModal; models::Playback UiState::sPlaybackModel; - -bindey::property UiState::sPropBatteryState; +models::TopBar UiState::sTopBarModel{{}, + UiState::sPlaybackModel.is_playing, + UiState::sPlaybackModel.current_track}; auto UiState::InitBootSplash(drivers::IGpios& gpios) -> bool { // Init LVGL first, since the display driver registers itself with LVGL. @@ -75,7 +76,6 @@ void UiState::PushScreen(std::shared_ptr screen) { sScreens.push(sCurrentScreen); } sCurrentScreen = screen; - UpdateTopBar(); } void UiState::PopScreen() { @@ -84,7 +84,6 @@ void UiState::PopScreen() { } sCurrentScreen = sScreens.top(); sScreens.pop(); - UpdateTopBar(); } void UiState::react(const system_fsm::KeyLockChanged& ev) { @@ -93,14 +92,8 @@ void UiState::react(const system_fsm::KeyLockChanged& ev) { : std::shared_ptr()); } -void UiState::react(const system_fsm::BatteryStateChanged&) { - if (!sServices) { - return; - } - auto state = sServices->battery().State(); - if (state) { - sPropBatteryState.set(*state); - } +void UiState::react(const system_fsm::BatteryStateChanged& ev) { + sTopBarModel.battery_state.set(ev.new_state); } void UiState::react(const audio::PlaybackStarted&) { @@ -117,34 +110,11 @@ void UiState::react(const audio::PlaybackUpdate& ev) { } void UiState::react(const audio::QueueUpdate&) { - ESP_LOGI(kTag, "current changed!"); auto& queue = sServices->track_queue(); sPlaybackModel.current_track.set(queue.GetCurrent()); sPlaybackModel.upcoming_tracks.set(queue.GetUpcoming(10)); } -void UiState::UpdateTopBar() { - auto battery_state = sServices->battery().State(); - bool has_queue = sServices->track_queue().GetCurrent().has_value(); - bool is_playing = audio::AudioState::is_in_state(); - - widgets::TopBar::State state{ - .playback_state = widgets::TopBar::PlaybackState::kIdle, - .battery_percent = static_cast( - battery_state.has_value() ? battery_state->percent : 100), - .is_charging = !battery_state.has_value() || battery_state->is_charging, - }; - - if (has_queue) { - state.playback_state = is_playing ? widgets::TopBar::PlaybackState::kPlaying - : widgets::TopBar::PlaybackState::kPaused; - } - - if (sCurrentScreen) { - sCurrentScreen->UpdateTopBar(state); - } -} - namespace states { void Splash::exit() { @@ -242,7 +212,8 @@ void Browse::entry() { if (!db) { return; } - sCurrentScreen = std::make_shared(db->GetIndexes()); + sCurrentScreen = + std::make_shared(sTopBarModel, db->GetIndexes()); sBrowseFirstEntry = false; } } @@ -253,7 +224,8 @@ void Browse::react(const system_fsm::StorageMounted& ev) { if (!db) { return; } - sCurrentScreen = std::make_shared(db->GetIndexes()); + sCurrentScreen = + std::make_shared(sTopBarModel, db->GetIndexes()); sBrowseFirstEntry = false; } } @@ -267,31 +239,32 @@ void Browse::react(const internal::ShowSettingsPage& ev) { std::shared_ptr bt_screen; switch (ev.page) { case internal::ShowSettingsPage::Page::kRoot: - screen.reset(new screens::Settings()); + screen.reset(new screens::Settings(sTopBarModel)); break; case internal::ShowSettingsPage::Page::kBluetooth: - bt_screen = std::make_shared(sServices->bluetooth(), - sServices->nvs()); + bt_screen = std::make_shared( + sTopBarModel, sServices->bluetooth(), sServices->nvs()); screen = bt_screen; bluetooth_screen_ = bt_screen; break; case internal::ShowSettingsPage::Page::kHeadphones: - screen.reset(new screens::Headphones(sServices->nvs())); + screen.reset(new screens::Headphones(sTopBarModel, sServices->nvs())); break; case internal::ShowSettingsPage::Page::kAppearance: - screen.reset(new screens::Appearance(sServices->nvs(), *sDisplay)); + screen.reset( + new screens::Appearance(sTopBarModel, sServices->nvs(), *sDisplay)); break; case internal::ShowSettingsPage::Page::kInput: - screen.reset(new screens::InputMethod()); + screen.reset(new screens::InputMethod(sTopBarModel)); break; case internal::ShowSettingsPage::Page::kStorage: - screen.reset(new screens::Storage()); + screen.reset(new screens::Storage(sTopBarModel)); break; case internal::ShowSettingsPage::Page::kFirmwareUpdate: - screen.reset(new screens::FirmwareUpdate()); + screen.reset(new screens::FirmwareUpdate(sTopBarModel)); break; case internal::ShowSettingsPage::Page::kAbout: - screen.reset(new screens::About()); + screen.reset(new screens::About(sTopBarModel)); break; } if (screen) { @@ -323,7 +296,7 @@ void Browse::react(const internal::RecordSelected& ev) { auto query = db->GetPage(&cont.value()); std::pmr::string title = record->text().value_or("TODO"); PushScreen(std::make_shared( - sServices->database(), title, std::move(query))); + sTopBarModel, sServices->database(), title, std::move(query))); } } @@ -336,7 +309,7 @@ void Browse::react(const internal::IndexSelected& ev) { ESP_LOGI(kTag, "selected index %s", ev.index.name.c_str()); auto query = db->GetTracksByIndex(ev.index, kRecordsPerPage); PushScreen(std::make_shared( - sServices->database(), ev.index.name, std::move(query))); + sTopBarModel, sServices->database(), ev.index.name, std::move(query))); } void Browse::react(const internal::BackPressed& ev) { @@ -354,8 +327,9 @@ static std::shared_ptr sPlayingScreen; void Playing::entry() { ESP_LOGI(kTag, "push playing screen"); - sPlayingScreen.reset(new screens::Playing( - sPlaybackModel, sServices->database(), sServices->track_queue())); + sPlayingScreen.reset(new screens::Playing(sTopBarModel, sPlaybackModel, + sServices->database(), + sServices->track_queue())); PushScreen(sPlayingScreen); } diff --git a/src/ui/widget_top_bar.cpp b/src/ui/widget_top_bar.cpp index c38f4fe2..ba9ee5cb 100644 --- a/src/ui/widget_top_bar.cpp +++ b/src/ui/widget_top_bar.cpp @@ -5,13 +5,16 @@ */ #include "widget_top_bar.hpp" +#include "battery.hpp" #include "core/lv_group.h" #include "core/lv_obj.h" #include "event_queue.hpp" #include "extra/layouts/flex/lv_flex.h" #include "font/lv_symbol_def.h" #include "font_symbols.hpp" +#include "model_top_bar.hpp" #include "themes.hpp" +#include "track.hpp" #include "ui_events.hpp" #include "ui_fsm.hpp" #include "widgets/lv_img.h" @@ -34,7 +37,9 @@ static void back_click_cb(lv_event_t* ev) { events::Ui().Dispatch(internal::BackPressed{}); } -TopBar::TopBar(lv_obj_t* parent, const Configuration& config) { +TopBar::TopBar(lv_obj_t* parent, + const Configuration& config, + models::TopBar& model) { container_ = lv_obj_create(parent); lv_obj_set_size(container_, lv_pct(100), 20); lv_obj_set_flex_flow(container_, LV_FLEX_FLOW_ROW); @@ -54,49 +59,51 @@ TopBar::TopBar(lv_obj_t* parent, const Configuration& config) { back_button_ = nullptr; } - title_ = lv_label_create(container_); + lv_obj_t* title_ = lv_label_create(container_); lv_label_set_text(title_, config.title.c_str()); lv_obj_set_flex_grow(title_, 1); - playback_ = lv_img_create(container_); - battery_ = lv_img_create(container_); - charging_ = lv_label_create(container_); + lv_obj_t* playback = lv_img_create(container_); - themes::Theme::instance()->ApplyStyle(container_, themes::Style::kTopBar); -} + bindings_.push_back(model.is_playing.onChangedAndNow([=](bool is_playing) { + lv_img_set_src(playback, is_playing ? &kIconPlay : &kIconPause); + })); + bindings_.push_back(model.current_track.onChangedAndNow( + [=](const std::optional& id) { + if (id) { + lv_obj_clear_flag(playback, LV_OBJ_FLAG_HIDDEN); + } else { + lv_obj_add_flag(playback, LV_OBJ_FLAG_HIDDEN); + } + })); -auto TopBar::Update(const State& state) -> void { - switch (state.playback_state) { - case PlaybackState::kIdle: - lv_img_set_src(playback_, NULL); - break; - case PlaybackState::kPaused: - lv_img_set_src(playback_, &kIconPause); - break; - case PlaybackState::kPlaying: - lv_img_set_src(playback_, &kIconPlay); - break; - } + lv_obj_t* battery = lv_img_create(container_); + lv_obj_t* charging = lv_label_create(container_); - if (state.is_charging) { - lv_label_set_text(charging_, "+"); - } else { - lv_label_set_text(charging_, ""); - } + bindings_.push_back(model.battery_state.onChangedAndNow( + [=](const battery::Battery::BatteryState& state) { + if (state.is_charging) { + lv_label_set_text(charging, "+"); + } else { + lv_label_set_text(charging, ""); + } - if (state.battery_percent >= 95) { - lv_img_set_src(battery_, &kIconBatteryFull); - } else if (state.battery_percent >= 75) { - lv_img_set_src(battery_, &kIconBattery80); - } else if (state.battery_percent >= 55) { - lv_img_set_src(battery_, &kIconBattery60); - } else if (state.battery_percent >= 35) { - lv_img_set_src(battery_, &kIconBattery40); - } else if (state.battery_percent >= 15) { - lv_img_set_src(battery_, &kIconBattery20); - } else { - lv_img_set_src(battery_, &kIconBatteryEmpty); - } + if (state.percent >= 95) { + lv_img_set_src(battery, &kIconBatteryFull); + } else if (state.percent >= 75) { + lv_img_set_src(battery, &kIconBattery80); + } else if (state.percent >= 55) { + lv_img_set_src(battery, &kIconBattery60); + } else if (state.percent >= 35) { + lv_img_set_src(battery, &kIconBattery40); + } else if (state.percent >= 15) { + lv_img_set_src(battery, &kIconBattery20); + } else { + lv_img_set_src(battery, &kIconBatteryEmpty); + } + })); + + themes::Theme::instance()->ApplyStyle(container_, themes::Style::kTopBar); } } // namespace widgets