Use databinding for the top bar. It's so nice now!

custom
jacqueline 2 years ago
parent f09ba5ffd5
commit 6a47edcd35
  1. 4
      src/app_console/app_console.cpp
  2. 18
      src/battery/battery.cpp
  3. 5
      src/system_fsm/include/system_events.hpp
  4. 26
      src/ui/include/model_top_bar.hpp
  5. 12
      src/ui/include/screen.hpp
  6. 3
      src/ui/include/screen_menu.hpp
  7. 4
      src/ui/include/screen_playing.hpp
  8. 19
      src/ui/include/screen_settings.hpp
  9. 2
      src/ui/include/screen_track_browser.hpp
  10. 5
      src/ui/include/ui_fsm.hpp
  11. 27
      src/ui/include/widget_top_bar.hpp
  12. 19
      src/ui/screen.cpp
  13. 5
      src/ui/screen_menu.cpp
  14. 6
      src/ui/screen_playing.cpp
  15. 29
      src/ui/screen_settings.cpp
  16. 4
      src/ui/screen_track_browser.cpp
  17. 74
      src/ui/ui_fsm.cpp
  18. 81
      src/ui/widget_top_bar.cpp

@ -336,7 +336,6 @@ void RegisterDbDump() {
esp_console_cmd_register(&cmd); esp_console_cmd_register(&cmd);
} }
#if CONFIG_APPTRACE_ENABLE
int CmdTasks(int argc, char** argv) { int CmdTasks(int argc, char** argv) {
if (!configUSE_TRACE_FACILITY) { if (!configUSE_TRACE_FACILITY) {
std::cout << "configUSE_TRACE_FACILITY must be enabled" << std::endl; std::cout << "configUSE_TRACE_FACILITY must be enabled" << std::endl;
@ -446,7 +445,6 @@ void RegisterTasks() {
.argtable = NULL}; .argtable = NULL};
esp_console_cmd_register(&cmd); esp_console_cmd_register(&cmd);
} }
#endif
int CmdHeaps(int argc, char** argv) { int CmdHeaps(int argc, char** argv) {
static const std::pmr::string usage = "usage: heaps"; static const std::pmr::string usage = "usage: heaps";
@ -638,9 +636,7 @@ auto AppConsole::RegisterExtraComponents() -> void {
RegisterDbTracks(); RegisterDbTracks();
RegisterDbIndex(); RegisterDbIndex();
RegisterDbDump(); RegisterDbDump();
#if CONFIG_APPTRACE_ENABLE
RegisterTasks(); RegisterTasks();
#endif
RegisterHeaps(); RegisterHeaps();

@ -77,15 +77,16 @@ auto Battery::Update() -> void {
*charge_state == ChargeStatus::kChargingFast || *charge_state == ChargeStatus::kChargingFast ||
*charge_state == ChargeStatus::kFullCharge; *charge_state == ChargeStatus::kFullCharge;
if (!state_ || state_->is_charging != is_charging || if (state_ && state_->is_charging == is_charging &&
state_->percent != percent) { state_->percent == percent) {
EmitEvent(); return;
} }
state_ = BatteryState{ state_ = BatteryState{
.percent = percent, .percent = percent,
.is_charging = is_charging, .is_charging = is_charging,
}; };
EmitEvent();
} }
auto Battery::State() -> std::optional<BatteryState> { auto Battery::State() -> std::optional<BatteryState> {
@ -94,8 +95,15 @@ auto Battery::State() -> std::optional<BatteryState> {
} }
auto Battery::EmitEvent() -> void { auto Battery::EmitEvent() -> void {
events::System().Dispatch(system_fsm::BatteryStateChanged{}); auto state = state_;
events::Ui().Dispatch(system_fsm::BatteryStateChanged{}); if (!state) {
return;
}
system_fsm::BatteryStateChanged ev{
.new_state = *state,
};
events::System().Dispatch(ev);
events::Ui().Dispatch(ev);
} }
} // namespace battery } // namespace battery

@ -8,6 +8,7 @@
#include <memory> #include <memory>
#include "battery.hpp"
#include "database.hpp" #include "database.hpp"
#include "service_locator.hpp" #include "service_locator.hpp"
#include "tinyfsm.hpp" #include "tinyfsm.hpp"
@ -54,7 +55,9 @@ struct HasPhonesChanged : tinyfsm::Event {
}; };
struct ChargingStatusChanged : tinyfsm::Event {}; struct ChargingStatusChanged : tinyfsm::Event {};
struct BatteryStateChanged : tinyfsm::Event {}; struct BatteryStateChanged : tinyfsm::Event {
battery::Battery::BatteryState new_state;
};
struct BluetoothDevicesChanged : tinyfsm::Event {}; struct BluetoothDevicesChanged : tinyfsm::Event {};

@ -0,0 +1,26 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* 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::Battery::BatteryState> battery_state;
// Shared with the Playback model
bindey::property<bool>& is_playing;
bindey::property<std::optional<database::TrackId>>& current_track;
};
} // namespace models
} // namespace ui

@ -16,6 +16,7 @@
#include "core/lv_obj_tree.h" #include "core/lv_obj_tree.h"
#include "event_binding.hpp" #include "event_binding.hpp"
#include "lvgl.h" #include "lvgl.h"
#include "model_top_bar.hpp"
#include "nod/nod.hpp" #include "nod/nod.hpp"
#include "widget_top_bar.hpp" #include "widget_top_bar.hpp"
@ -37,8 +38,6 @@ class Screen {
*/ */
virtual auto Tick() -> void {} virtual auto Tick() -> void {}
auto UpdateTopBar(const widgets::TopBar::State& state) -> void;
auto root() -> lv_obj_t* { return root_; } auto root() -> lv_obj_t* { return root_; }
auto content() -> lv_obj_t* { return content_; } auto content() -> lv_obj_t* { return content_; }
@ -52,8 +51,9 @@ class Screen {
} }
protected: protected:
auto CreateTopBar(lv_obj_t* parent, const widgets::TopBar::Configuration&) auto CreateTopBar(lv_obj_t* parent,
-> widgets::TopBar*; const widgets::TopBar::Configuration&,
models::TopBar& model) -> widgets::TopBar*;
std::pmr::vector<bindey::scoped_binding> data_bindings_; std::pmr::vector<bindey::scoped_binding> data_bindings_;
std::pmr::vector<std::unique_ptr<EventBinding>> event_bindings_; std::pmr::vector<std::unique_ptr<EventBinding>> event_bindings_;
@ -78,7 +78,9 @@ class Screen {
class MenuScreen : public Screen { class MenuScreen : public Screen {
public: 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 } // namespace ui

@ -12,6 +12,7 @@
#include "index.hpp" #include "index.hpp"
#include "lvgl.h" #include "lvgl.h"
#include "model_top_bar.hpp"
#include "screen.hpp" #include "screen.hpp"
#include "screen_settings.hpp" #include "screen_settings.hpp"
@ -20,7 +21,7 @@ namespace screens {
class Menu : public MenuScreen { class Menu : public MenuScreen {
public: public:
explicit Menu(std::vector<database::IndexInfo> indexes); explicit Menu(models::TopBar&, std::vector<database::IndexInfo> indexes);
~Menu(); ~Menu();
private: private:

@ -18,6 +18,7 @@
#include "database.hpp" #include "database.hpp"
#include "future_fetcher.hpp" #include "future_fetcher.hpp"
#include "model_playback.hpp" #include "model_playback.hpp"
#include "model_top_bar.hpp"
#include "screen.hpp" #include "screen.hpp"
#include "track.hpp" #include "track.hpp"
#include "track_queue.hpp" #include "track_queue.hpp"
@ -31,7 +32,8 @@ namespace screens {
*/ */
class Playing : public Screen { class Playing : public Screen {
public: public:
explicit Playing(models::Playback& playback_model, explicit Playing(models::TopBar&,
models::Playback& playback_model,
std::weak_ptr<database::Database> db, std::weak_ptr<database::Database> db,
audio::TrackQueue& queue); audio::TrackQueue& queue);
~Playing(); ~Playing();

@ -18,6 +18,7 @@
#include "index.hpp" #include "index.hpp"
#include "lvgl.h" #include "lvgl.h"
#include "model_top_bar.hpp"
#include "nvs.hpp" #include "nvs.hpp"
#include "screen.hpp" #include "screen.hpp"
@ -26,12 +27,12 @@ namespace screens {
class Settings : public MenuScreen { class Settings : public MenuScreen {
public: public:
Settings(); Settings(models::TopBar&);
}; };
class Bluetooth : public MenuScreen { class Bluetooth : public MenuScreen {
public: public:
Bluetooth(drivers::Bluetooth& bt, drivers::NvsStorage& nvs); Bluetooth(models::TopBar&, drivers::Bluetooth& bt, drivers::NvsStorage& nvs);
auto ChangeEnabledState(bool enabled) -> void; auto ChangeEnabledState(bool enabled) -> void;
auto RefreshDevicesList() -> void; auto RefreshDevicesList() -> void;
@ -53,7 +54,7 @@ class Bluetooth : public MenuScreen {
class Headphones : public MenuScreen { class Headphones : public MenuScreen {
public: public:
Headphones(drivers::NvsStorage& nvs); Headphones(models::TopBar&, drivers::NvsStorage& nvs);
auto ChangeMaxVolume(uint8_t index) -> void; auto ChangeMaxVolume(uint8_t index) -> void;
auto ChangeCustomVolume(int8_t diff) -> void; auto ChangeCustomVolume(int8_t diff) -> void;
@ -71,7 +72,9 @@ class Headphones : public MenuScreen {
class Appearance : public MenuScreen { class Appearance : public MenuScreen {
public: public:
Appearance(drivers::NvsStorage& nvs, drivers::Display& display); Appearance(models::TopBar&,
drivers::NvsStorage& nvs,
drivers::Display& display);
auto ChangeBrightness(uint_fast8_t) -> void; auto ChangeBrightness(uint_fast8_t) -> void;
auto CommitBrightness() -> void; auto CommitBrightness() -> void;
@ -86,22 +89,22 @@ class Appearance : public MenuScreen {
class InputMethod : public MenuScreen { class InputMethod : public MenuScreen {
public: public:
InputMethod(); InputMethod(models::TopBar&);
}; };
class Storage : public MenuScreen { class Storage : public MenuScreen {
public: public:
Storage(); Storage(models::TopBar&);
}; };
class FirmwareUpdate : public MenuScreen { class FirmwareUpdate : public MenuScreen {
public: public:
FirmwareUpdate(); FirmwareUpdate(models::TopBar&);
}; };
class About : public MenuScreen { class About : public MenuScreen {
public: public:
About(); About(models::TopBar&);
}; };
} // namespace screens } // namespace screens

@ -14,6 +14,7 @@
#include "lvgl.h" #include "lvgl.h"
#include "database.hpp" #include "database.hpp"
#include "model_top_bar.hpp"
#include "screen.hpp" #include "screen.hpp"
namespace ui { namespace ui {
@ -22,6 +23,7 @@ namespace screens {
class TrackBrowser : public Screen { class TrackBrowser : public Screen {
public: public:
TrackBrowser( TrackBrowser(
models::TopBar&,
std::weak_ptr<database::Database> db, std::weak_ptr<database::Database> db,
const std::pmr::string& title, const std::pmr::string& title,
std::future<database::Result<database::IndexRecord>*>&& initial_page); std::future<database::Result<database::IndexRecord>*>&& initial_page);

@ -17,6 +17,7 @@
#include "gpios.hpp" #include "gpios.hpp"
#include "lvgl_task.hpp" #include "lvgl_task.hpp"
#include "model_playback.hpp" #include "model_playback.hpp"
#include "model_top_bar.hpp"
#include "nvs.hpp" #include "nvs.hpp"
#include "relative_wheel.hpp" #include "relative_wheel.hpp"
#include "screen_playing.hpp" #include "screen_playing.hpp"
@ -82,7 +83,6 @@ 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 std::unique_ptr<UiTask> sTask; static std::unique_ptr<UiTask> sTask;
static std::shared_ptr<system_fsm::ServiceLocator> sServices; static std::shared_ptr<system_fsm::ServiceLocator> sServices;
@ -94,8 +94,7 @@ class UiState : public tinyfsm::Fsm<UiState> {
static std::shared_ptr<Modal> sCurrentModal; static std::shared_ptr<Modal> sCurrentModal;
static models::Playback sPlaybackModel; static models::Playback sPlaybackModel;
static models::TopBar sTopBarModel;
static bindey::property<battery::Battery::BatteryState> sPropBatteryState;
}; };
namespace states { namespace states {

@ -9,9 +9,11 @@
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include "bindey/binding.h"
#include "lvgl.h" #include "lvgl.h"
#include "memory_resource.hpp" #include "memory_resource.hpp"
#include "model_top_bar.hpp"
namespace ui { namespace ui {
@ -24,33 +26,18 @@ class TopBar {
std::pmr::string title; std::pmr::string title;
}; };
enum class PlaybackState { explicit TopBar(lv_obj_t* parent,
kIdle, const Configuration& config,
kPaused, models::TopBar& model);
kPlaying,
};
struct State {
PlaybackState playback_state;
uint_fast8_t battery_percent;
bool is_charging;
};
explicit TopBar(lv_obj_t* parent, const Configuration& config);
auto root() -> lv_obj_t* { return container_; } auto root() -> lv_obj_t* { return container_; }
auto button() -> lv_obj_t* { return back_button_; } auto button() -> lv_obj_t* { return back_button_; }
auto Update(const State&) -> void;
private: private:
lv_obj_t* container_; std::vector<bindey::scoped_binding> bindings_;
lv_obj_t* container_;
lv_obj_t* back_button_; lv_obj_t* back_button_;
lv_obj_t* title_;
lv_obj_t* playback_;
lv_obj_t* battery_;
lv_obj_t* charging_;
}; };
} // namespace widgets } // namespace widgets

@ -12,6 +12,7 @@
#include "core/lv_obj_tree.h" #include "core/lv_obj_tree.h"
#include "misc/lv_area.h" #include "misc/lv_area.h"
#include "misc/lv_color.h" #include "misc/lv_color.h"
#include "model_top_bar.hpp"
#include "widget_top_bar.hpp" #include "widget_top_bar.hpp"
namespace ui { namespace ui {
@ -40,24 +41,20 @@ Screen::~Screen() {
lv_obj_del(root_); 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, auto Screen::CreateTopBar(lv_obj_t* parent,
const widgets::TopBar::Configuration& config) const widgets::TopBar::Configuration& config,
-> widgets::TopBar* { models::TopBar& model) -> widgets::TopBar* {
assert(top_bar_ == nullptr); assert(top_bar_ == nullptr);
top_bar_ = std::make_unique<widgets::TopBar>(parent, config); top_bar_ = std::make_unique<widgets::TopBar>(parent, config, model);
if (top_bar_->button()) { if (top_bar_->button()) {
lv_group_add_obj(group_, top_bar_->button()); lv_group_add_obj(group_, top_bar_->button());
} }
return top_bar_.get(); 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() { : Screen() {
lv_group_set_wrap(group_, false); 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, .show_back_button = show_back_button,
.title = title.c_str(), .title = title.c_str(),
}; };
CreateTopBar(content_, config); CreateTopBar(content_, config, top_bar_model);
content_ = lv_obj_create(content_); content_ = lv_obj_create(content_);
lv_obj_set_flex_grow(content_, 1); lv_obj_set_flex_grow(content_, 1);

@ -18,6 +18,7 @@
#include "hal/lv_hal_disp.h" #include "hal/lv_hal_disp.h"
#include "index.hpp" #include "index.hpp"
#include "misc/lv_area.h" #include "misc/lv_area.h"
#include "model_top_bar.hpp"
#include "ui_events.hpp" #include "ui_events.hpp"
#include "ui_fsm.hpp" #include "ui_fsm.hpp"
#include "widget_top_bar.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}); events::Ui().Dispatch(internal::IndexSelected{.index = *index});
} }
Menu::Menu(std::vector<database::IndexInfo> indexes) Menu::Menu(models::TopBar& top_bar, std::vector<database::IndexInfo> indexes)
: MenuScreen(" ", false), indexes_(indexes) { : MenuScreen(top_bar, " ", false), indexes_(indexes) {
lv_obj_t* list = lv_list_create(content_); lv_obj_t* list = lv_list_create(content_);
lv_obj_set_size(list, lv_pct(100), lv_pct(100)); lv_obj_set_size(list, lv_pct(100), lv_pct(100));

@ -37,6 +37,7 @@
#include "misc/lv_color.h" #include "misc/lv_color.h"
#include "misc/lv_txt.h" #include "misc/lv_txt.h"
#include "model_playback.hpp" #include "model_playback.hpp"
#include "model_top_bar.hpp"
#include "track.hpp" #include "track.hpp"
#include "ui_events.hpp" #include "ui_events.hpp"
#include "ui_fsm.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; return button;
} }
Playing::Playing(models::Playback& playback_model, Playing::Playing(models::TopBar& top_bar_model,
models::Playback& playback_model,
std::weak_ptr<database::Database> db, std::weak_ptr<database::Database> db,
audio::TrackQueue& queue) audio::TrackQueue& queue)
: db_(db), : db_(db),
@ -158,7 +160,7 @@ Playing::Playing(models::Playback& playback_model,
.show_back_button = true, .show_back_button = true,
.title = "Now Playing", .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_t* info_container = lv_obj_create(above_fold_container);
lv_obj_set_layout(info_container, LV_LAYOUT_FLEX); lv_obj_set_layout(info_container, LV_LAYOUT_FLEX);

@ -29,6 +29,7 @@
#include "index.hpp" #include "index.hpp"
#include "misc/lv_anim.h" #include "misc/lv_anim.h"
#include "misc/lv_area.h" #include "misc/lv_area.h"
#include "model_top_bar.hpp"
#include "nvs.hpp" #include "nvs.hpp"
#include "screen.hpp" #include "screen.hpp"
#include "ui_events.hpp" #include "ui_events.hpp"
@ -64,7 +65,7 @@ static void sub_menu(lv_obj_t* list,
reinterpret_cast<void*>(static_cast<uintptr_t>(page))); reinterpret_cast<void*>(static_cast<uintptr_t>(page)));
} }
Settings::Settings() : MenuScreen("Settings") { Settings::Settings(models::TopBar& bar) : MenuScreen(bar, "Settings") {
lv_obj_t* list = lv_list_create(content_); lv_obj_t* list = lv_list_create(content_);
lv_obj_set_size(list, lv_pct(100), lv_pct(100)); 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)); instance->OnDeviceSelected(lv_obj_get_index(ev->target));
} }
Bluetooth::Bluetooth(drivers::Bluetooth& bt, drivers::NvsStorage& nvs) Bluetooth::Bluetooth(models::TopBar& bar,
: MenuScreen("Bluetooth"), bt_(bt), nvs_(nvs) { 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_container = settings_container(content_);
lv_obj_t* toggle_label = lv_label_create(toggle_container); lv_obj_t* toggle_label = lv_label_create(toggle_container);
lv_label_set_text(toggle_label, "Enable"); lv_label_set_text(toggle_label, "Enable");
@ -268,8 +271,8 @@ static void decrease_vol_limit_cb(lv_event_t* ev) {
instance->ChangeCustomVolume(-2); instance->ChangeCustomVolume(-2);
} }
Headphones::Headphones(drivers::NvsStorage& nvs) Headphones::Headphones(models::TopBar& bar, drivers::NvsStorage& nvs)
: MenuScreen("Headphones"), nvs_(nvs), custom_limit_(0) { : MenuScreen(bar, "Headphones"), nvs_(nvs), custom_limit_(0) {
uint16_t reference = drivers::wm8523::kLineLevelReferenceVolume; uint16_t reference = drivers::wm8523::kLineLevelReferenceVolume;
index_to_level_.push_back(reference - (10 * 4)); index_to_level_.push_back(reference - (10 * 4));
index_to_level_.push_back(reference + (6 * 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) + "%"; return std::to_string(percent) + "%";
} }
Appearance::Appearance(drivers::NvsStorage& nvs, drivers::Display& display) Appearance::Appearance(models::TopBar& bar,
: MenuScreen("Appearance"), nvs_(nvs), display_(display) { 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_container = settings_container(content_);
lv_obj_t* toggle_label = lv_label_create(toggle_container); lv_obj_t* toggle_label = lv_label_create(toggle_container);
lv_obj_set_flex_grow(toggle_label, 1); lv_obj_set_flex_grow(toggle_label, 1);
@ -417,7 +422,8 @@ auto Appearance::CommitBrightness() -> void {
nvs_.ScreenBrightness(current_brightness_); 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_obj_t* wheel_label = lv_label_create(content_);
lv_label_set_text(wheel_label, "What does the wheel do?"); lv_label_set_text(wheel_label, "What does the wheel do?");
lv_obj_t* wheel_dropdown = lv_dropdown_create(content_); 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); 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_, "Storage Capacity:", "32 GiB");
label_pair(content_, "Currently Used:", "6 GiB"); label_pair(content_, "Currently Used:", "6 GiB");
label_pair(content_, "DB Size:", "1.2 MiB"); label_pair(content_, "DB Size:", "1.2 MiB");
@ -446,7 +452,8 @@ Storage::Storage() : MenuScreen("Storage") {
lv_group_add_obj(group_, reset_btn); 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_, "ESP32 FW:", "vIDKLOL");
label_pair(content_, "SAMD21 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); 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_obj_t* label = lv_label_create(content_);
lv_label_set_text(label, "Some licenses or whatever"); lv_label_set_text(label, "Some licenses or whatever");
} }

@ -16,6 +16,7 @@
#include "font/lv_symbol_def.h" #include "font/lv_symbol_def.h"
#include "lvgl.h" #include "lvgl.h"
#include "misc/lv_anim.h" #include "misc/lv_anim.h"
#include "model_top_bar.hpp"
#include "screen_menu.hpp" #include "screen_menu.hpp"
#include "core/lv_event.h" #include "core/lv_event.h"
@ -59,6 +60,7 @@ static void item_select_cb(lv_event_t* ev) {
} }
TrackBrowser::TrackBrowser( TrackBrowser::TrackBrowser(
models::TopBar& top_bar_model,
std::weak_ptr<database::Database> db, std::weak_ptr<database::Database> db,
const std::pmr::string& title, const std::pmr::string& title,
std::future<database::Result<database::IndexRecord>*>&& initial_page) std::future<database::Result<database::IndexRecord>*>&& initial_page)
@ -83,7 +85,7 @@ TrackBrowser::TrackBrowser(
.show_back_button = true, .show_back_button = true,
.title = title, .title = title,
}; };
auto top_bar = CreateTopBar(content_, config); auto top_bar = CreateTopBar(content_, config, top_bar_model);
back_button_ = top_bar->button(); back_button_ = top_bar->button();
list_ = lv_list_create(content_); list_ = lv_list_create(content_);

@ -54,8 +54,9 @@ std::shared_ptr<Screen> UiState::sCurrentScreen;
std::shared_ptr<Modal> UiState::sCurrentModal; std::shared_ptr<Modal> UiState::sCurrentModal;
models::Playback UiState::sPlaybackModel; models::Playback UiState::sPlaybackModel;
models::TopBar UiState::sTopBarModel{{},
bindey::property<battery::Battery::BatteryState> UiState::sPropBatteryState; UiState::sPlaybackModel.is_playing,
UiState::sPlaybackModel.current_track};
auto UiState::InitBootSplash(drivers::IGpios& gpios) -> bool { auto UiState::InitBootSplash(drivers::IGpios& gpios) -> bool {
// Init LVGL first, since the display driver registers itself with LVGL. // Init LVGL first, since the display driver registers itself with LVGL.
@ -75,7 +76,6 @@ 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() {
@ -84,7 +84,6 @@ 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) {
@ -93,14 +92,8 @@ void UiState::react(const system_fsm::KeyLockChanged& ev) {
: std::shared_ptr<TouchWheelEncoder>()); : std::shared_ptr<TouchWheelEncoder>());
} }
void UiState::react(const system_fsm::BatteryStateChanged&) { void UiState::react(const system_fsm::BatteryStateChanged& ev) {
if (!sServices) { sTopBarModel.battery_state.set(ev.new_state);
return;
}
auto state = sServices->battery().State();
if (state) {
sPropBatteryState.set(*state);
}
} }
void UiState::react(const audio::PlaybackStarted&) { void UiState::react(const audio::PlaybackStarted&) {
@ -117,34 +110,11 @@ void UiState::react(const audio::PlaybackUpdate& ev) {
} }
void UiState::react(const audio::QueueUpdate&) { void UiState::react(const audio::QueueUpdate&) {
ESP_LOGI(kTag, "current changed!");
auto& queue = sServices->track_queue(); auto& queue = sServices->track_queue();
sPlaybackModel.current_track.set(queue.GetCurrent()); sPlaybackModel.current_track.set(queue.GetCurrent());
sPlaybackModel.upcoming_tracks.set(queue.GetUpcoming(10)); 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<audio::states::Playback>();
widgets::TopBar::State state{
.playback_state = widgets::TopBar::PlaybackState::kIdle,
.battery_percent = static_cast<uint_fast8_t>(
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 { namespace states {
void Splash::exit() { void Splash::exit() {
@ -242,7 +212,8 @@ void Browse::entry() {
if (!db) { if (!db) {
return; return;
} }
sCurrentScreen = std::make_shared<screens::Menu>(db->GetIndexes()); sCurrentScreen =
std::make_shared<screens::Menu>(sTopBarModel, db->GetIndexes());
sBrowseFirstEntry = false; sBrowseFirstEntry = false;
} }
} }
@ -253,7 +224,8 @@ void Browse::react(const system_fsm::StorageMounted& ev) {
if (!db) { if (!db) {
return; return;
} }
sCurrentScreen = std::make_shared<screens::Menu>(db->GetIndexes()); sCurrentScreen =
std::make_shared<screens::Menu>(sTopBarModel, db->GetIndexes());
sBrowseFirstEntry = false; sBrowseFirstEntry = false;
} }
} }
@ -267,31 +239,32 @@ void Browse::react(const internal::ShowSettingsPage& ev) {
std::shared_ptr<screens::Bluetooth> bt_screen; std::shared_ptr<screens::Bluetooth> bt_screen;
switch (ev.page) { switch (ev.page) {
case internal::ShowSettingsPage::Page::kRoot: case internal::ShowSettingsPage::Page::kRoot:
screen.reset(new screens::Settings()); screen.reset(new screens::Settings(sTopBarModel));
break; break;
case internal::ShowSettingsPage::Page::kBluetooth: case internal::ShowSettingsPage::Page::kBluetooth:
bt_screen = std::make_shared<screens::Bluetooth>(sServices->bluetooth(), bt_screen = std::make_shared<screens::Bluetooth>(
sServices->nvs()); sTopBarModel, sServices->bluetooth(), sServices->nvs());
screen = bt_screen; screen = bt_screen;
bluetooth_screen_ = bt_screen; bluetooth_screen_ = bt_screen;
break; break;
case internal::ShowSettingsPage::Page::kHeadphones: case internal::ShowSettingsPage::Page::kHeadphones:
screen.reset(new screens::Headphones(sServices->nvs())); screen.reset(new screens::Headphones(sTopBarModel, sServices->nvs()));
break; break;
case internal::ShowSettingsPage::Page::kAppearance: case internal::ShowSettingsPage::Page::kAppearance:
screen.reset(new screens::Appearance(sServices->nvs(), *sDisplay)); screen.reset(
new screens::Appearance(sTopBarModel, sServices->nvs(), *sDisplay));
break; break;
case internal::ShowSettingsPage::Page::kInput: case internal::ShowSettingsPage::Page::kInput:
screen.reset(new screens::InputMethod()); screen.reset(new screens::InputMethod(sTopBarModel));
break; break;
case internal::ShowSettingsPage::Page::kStorage: case internal::ShowSettingsPage::Page::kStorage:
screen.reset(new screens::Storage()); screen.reset(new screens::Storage(sTopBarModel));
break; break;
case internal::ShowSettingsPage::Page::kFirmwareUpdate: case internal::ShowSettingsPage::Page::kFirmwareUpdate:
screen.reset(new screens::FirmwareUpdate()); screen.reset(new screens::FirmwareUpdate(sTopBarModel));
break; break;
case internal::ShowSettingsPage::Page::kAbout: case internal::ShowSettingsPage::Page::kAbout:
screen.reset(new screens::About()); screen.reset(new screens::About(sTopBarModel));
break; break;
} }
if (screen) { if (screen) {
@ -323,7 +296,7 @@ void Browse::react(const internal::RecordSelected& ev) {
auto query = db->GetPage(&cont.value()); auto query = db->GetPage(&cont.value());
std::pmr::string title = record->text().value_or("TODO"); std::pmr::string title = record->text().value_or("TODO");
PushScreen(std::make_shared<screens::TrackBrowser>( PushScreen(std::make_shared<screens::TrackBrowser>(
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()); ESP_LOGI(kTag, "selected index %s", ev.index.name.c_str());
auto query = db->GetTracksByIndex(ev.index, kRecordsPerPage); auto query = db->GetTracksByIndex(ev.index, kRecordsPerPage);
PushScreen(std::make_shared<screens::TrackBrowser>( PushScreen(std::make_shared<screens::TrackBrowser>(
sServices->database(), ev.index.name, std::move(query))); sTopBarModel, sServices->database(), ev.index.name, std::move(query)));
} }
void Browse::react(const internal::BackPressed& ev) { void Browse::react(const internal::BackPressed& ev) {
@ -354,8 +327,9 @@ static std::shared_ptr<screens::Playing> sPlayingScreen;
void Playing::entry() { void Playing::entry() {
ESP_LOGI(kTag, "push playing screen"); ESP_LOGI(kTag, "push playing screen");
sPlayingScreen.reset(new screens::Playing( sPlayingScreen.reset(new screens::Playing(sTopBarModel, sPlaybackModel,
sPlaybackModel, sServices->database(), sServices->track_queue())); sServices->database(),
sServices->track_queue()));
PushScreen(sPlayingScreen); PushScreen(sPlayingScreen);
} }

@ -5,13 +5,16 @@
*/ */
#include "widget_top_bar.hpp" #include "widget_top_bar.hpp"
#include "battery.hpp"
#include "core/lv_group.h" #include "core/lv_group.h"
#include "core/lv_obj.h" #include "core/lv_obj.h"
#include "event_queue.hpp" #include "event_queue.hpp"
#include "extra/layouts/flex/lv_flex.h" #include "extra/layouts/flex/lv_flex.h"
#include "font/lv_symbol_def.h" #include "font/lv_symbol_def.h"
#include "font_symbols.hpp" #include "font_symbols.hpp"
#include "model_top_bar.hpp"
#include "themes.hpp" #include "themes.hpp"
#include "track.hpp"
#include "ui_events.hpp" #include "ui_events.hpp"
#include "ui_fsm.hpp" #include "ui_fsm.hpp"
#include "widgets/lv_img.h" #include "widgets/lv_img.h"
@ -34,7 +37,9 @@ static void back_click_cb(lv_event_t* ev) {
events::Ui().Dispatch(internal::BackPressed{}); 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); container_ = lv_obj_create(parent);
lv_obj_set_size(container_, lv_pct(100), 20); lv_obj_set_size(container_, lv_pct(100), 20);
lv_obj_set_flex_flow(container_, LV_FLEX_FLOW_ROW); 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; 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_label_set_text(title_, config.title.c_str());
lv_obj_set_flex_grow(title_, 1); lv_obj_set_flex_grow(title_, 1);
playback_ = lv_img_create(container_); lv_obj_t* playback = lv_img_create(container_);
battery_ = lv_img_create(container_);
charging_ = lv_label_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<database::TrackId>& 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 { lv_obj_t* battery = lv_img_create(container_);
switch (state.playback_state) { lv_obj_t* charging = lv_label_create(container_);
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;
}
if (state.is_charging) { bindings_.push_back(model.battery_state.onChangedAndNow(
lv_label_set_text(charging_, "+"); [=](const battery::Battery::BatteryState& state) {
} else { if (state.is_charging) {
lv_label_set_text(charging_, ""); lv_label_set_text(charging, "+");
} } else {
lv_label_set_text(charging, "");
}
if (state.battery_percent >= 95) { if (state.percent >= 95) {
lv_img_set_src(battery_, &kIconBatteryFull); lv_img_set_src(battery, &kIconBatteryFull);
} else if (state.battery_percent >= 75) { } else if (state.percent >= 75) {
lv_img_set_src(battery_, &kIconBattery80); lv_img_set_src(battery, &kIconBattery80);
} else if (state.battery_percent >= 55) { } else if (state.percent >= 55) {
lv_img_set_src(battery_, &kIconBattery60); lv_img_set_src(battery, &kIconBattery60);
} else if (state.battery_percent >= 35) { } else if (state.percent >= 35) {
lv_img_set_src(battery_, &kIconBattery40); lv_img_set_src(battery, &kIconBattery40);
} else if (state.battery_percent >= 15) { } else if (state.percent >= 15) {
lv_img_set_src(battery_, &kIconBattery20); lv_img_set_src(battery, &kIconBattery20);
} else { } else {
lv_img_set_src(battery_, &kIconBatteryEmpty); lv_img_set_src(battery, &kIconBatteryEmpty);
} }
}));
themes::Theme::instance()->ApplyStyle(container_, themes::Style::kTopBar);
} }
} // namespace widgets } // namespace widgets

Loading…
Cancel
Save