summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2024-01-15 12:31:20 +1100
committerjacqueline <me@jacqueline.id.au>2024-01-15 12:31:20 +1100
commit7cdcd44e0ca10ebdc796638190ed1d9b45d99ef0 (patch)
tree637b43848d17c9dbdc1688cb4733eb235f223e37 /src/ui
parent0e04eb918ec976017276306181282769d8896c83 (diff)
downloadtangara-fw-7cdcd44e0ca10ebdc796638190ed1d9b45d99ef0.tar.gz
Begin migration of remaining screens to Lua
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/CMakeLists.txt6
-rw-r--r--src/ui/event_binding.cpp23
-rw-r--r--src/ui/include/event_binding.hpp30
-rw-r--r--src/ui/include/modal.hpp10
-rw-r--r--src/ui/include/modal_confirm.hpp29
-rw-r--r--src/ui/include/modal_progress.hpp34
-rw-r--r--src/ui/include/model_playback.hpp26
-rw-r--r--src/ui/include/model_top_bar.hpp26
-rw-r--r--src/ui/include/screen.hpp35
-rw-r--r--src/ui/include/screen_settings.hpp116
-rw-r--r--src/ui/include/ui_events.hpp15
-rw-r--r--src/ui/include/ui_fsm.hpp104
-rw-r--r--src/ui/include/widget_top_bar.hpp45
-rw-r--r--src/ui/lvgl_task.cpp4
-rw-r--r--src/ui/modal.cpp4
-rw-r--r--src/ui/modal_confirm.cpp76
-rw-r--r--src/ui/modal_progress.cpp67
-rw-r--r--src/ui/screen.cpp41
-rw-r--r--src/ui/screen_settings.cpp575
-rw-r--r--src/ui/ui_fsm.cpp337
-rw-r--r--src/ui/widget_top_bar.cpp61
21 files changed, 160 insertions, 1504 deletions
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt
index 0d9de5a4..6d45fc9f 100644
--- a/src/ui/CMakeLists.txt
+++ b/src/ui/CMakeLists.txt
@@ -4,10 +4,8 @@
idf_component_register(
SRCS "lvgl_task.cpp" "ui_fsm.cpp" "screen_splash.cpp" "encoder_input.cpp"
- "themes.cpp" "widget_top_bar.cpp" "screen.cpp" "modal_progress.cpp"
- "modal.cpp" "modal_confirm.cpp" "screen_settings.cpp" "event_binding.cpp"
- "screen_lua.cpp"
+ "themes.cpp" "screen.cpp" "modal.cpp" "screen_lua.cpp"
"splash.c" "font_fusion_12.c" "font_fusion_10.c"
INCLUDE_DIRS "include"
- REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer" "battery" "bindey" "lua" "luavgl" "esp_app_format")
+ REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer" "battery" "lua" "luavgl" "esp_app_format")
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/ui/event_binding.cpp b/src/ui/event_binding.cpp
deleted file mode 100644
index ed15ccfb..00000000
--- a/src/ui/event_binding.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#include "event_binding.hpp"
-
-#include "core/lv_event.h"
-
-namespace ui {
-
-static auto event_cb(lv_event_t* ev) -> void {
- EventBinding* binding =
- static_cast<EventBinding*>(lv_event_get_user_data(ev));
- binding->signal()(lv_event_get_target(ev));
-}
-
-EventBinding::EventBinding(lv_obj_t* obj, lv_event_code_t ev) {
- lv_obj_add_event_cb(obj, event_cb, ev, this);
-}
-
-} // namespace ui
diff --git a/src/ui/include/event_binding.hpp b/src/ui/include/event_binding.hpp
deleted file mode 100644
index 19514db4..00000000
--- a/src/ui/include/event_binding.hpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include "lvgl.h"
-
-#include "core/lv_event.h"
-#include "core/lv_obj.h"
-#include "nod/nod.hpp"
-
-namespace ui {
-
-class EventBinding {
- public:
- EventBinding(lv_obj_t* obj, lv_event_code_t ev);
-
- auto signal() -> nod::signal<void(lv_obj_t*)>& { return signal_; }
-
- private:
- lv_obj_t* obj_;
- nod::signal<void(lv_obj_t*)> signal_;
-};
-
-} // namespace ui
diff --git a/src/ui/include/modal.hpp b/src/ui/include/modal.hpp
index 61e52cdf..6b7e792e 100644
--- a/src/ui/include/modal.hpp
+++ b/src/ui/include/modal.hpp
@@ -12,7 +12,6 @@
#include "core/lv_obj.h"
#include "core/lv_obj_tree.h"
#include "lvgl.h"
-#include "widget_top_bar.hpp"
#include "screen.hpp"
@@ -30,15 +29,6 @@ class Modal {
lv_obj_t* const root_;
lv_group_t* const group_;
- std::pmr::vector<std::unique_ptr<EventBinding>> event_bindings_;
-
- template <typename T>
- auto lv_bind(lv_obj_t* obj, lv_event_code_t ev, T fn) -> void {
- auto binding = std::make_unique<EventBinding>(obj, ev);
- binding->signal().connect(fn);
- event_bindings_.push_back(std::move(binding));
- }
-
private:
Screen* host_;
};
diff --git a/src/ui/include/modal_confirm.hpp b/src/ui/include/modal_confirm.hpp
deleted file mode 100644
index 29d80041..00000000
--- a/src/ui/include/modal_confirm.hpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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::pmr::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
deleted file mode 100644
index 2ccb671a..00000000
--- a/src/ui/include/modal_progress.hpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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::pmr::string title, std::pmr::string subtitle = "");
-
- void title(const std::pmr::string&);
- void subtitle(const std::pmr::string&);
-
- private:
- lv_obj_t* container_;
- lv_obj_t* title_;
- lv_obj_t* subtitle_;
-};
-
-} // namespace modals
-} // namespace ui
diff --git a/src/ui/include/model_playback.hpp b/src/ui/include/model_playback.hpp
deleted file mode 100644
index f932dcfd..00000000
--- a/src/ui/include/model_playback.hpp
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#pragma once
-
-#include "bindey/property.h"
-
-#include "track.hpp"
-
-namespace ui {
-namespace models {
-
-struct Playback {
- bindey::property<bool> is_playing;
- bindey::property<std::optional<database::TrackId>> current_track;
- bindey::property<std::vector<database::TrackId>> upcoming_tracks;
-
- bindey::property<uint32_t> current_track_position;
- bindey::property<uint32_t> current_track_duration;
-};
-
-} // namespace models
-} // namespace ui \ No newline at end of file
diff --git a/src/ui/include/model_top_bar.hpp b/src/ui/include/model_top_bar.hpp
deleted file mode 100644
index c0f148f3..00000000
--- a/src/ui/include/model_top_bar.hpp
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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
diff --git a/src/ui/include/screen.hpp b/src/ui/include/screen.hpp
index 4fe0a3b7..60939660 100644
--- a/src/ui/include/screen.hpp
+++ b/src/ui/include/screen.hpp
@@ -10,15 +10,10 @@
#include <optional>
#include <vector>
-#include "bindey/binding.h"
#include "core/lv_group.h"
#include "core/lv_obj.h"
#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"
namespace ui {
@@ -32,12 +27,6 @@ class Screen {
Screen();
virtual ~Screen();
- /*
- * Called periodically to allow the screen to update itself, e.g. to handle
- * std::futures that are still loading in.
- */
- virtual auto Tick() -> void {}
-
auto root() -> lv_obj_t* { return root_; }
auto content() -> lv_obj_t* { return content_; }
auto alert() -> lv_obj_t* { return alert_; }
@@ -52,20 +41,6 @@ class Screen {
}
protected:
- auto CreateTopBar(lv_obj_t* parent,
- const widgets::TopBar::Configuration&,
- models::TopBar& model) -> widgets::TopBar*;
-
- std::pmr::vector<bindey::scoped_binding> data_bindings_;
- std::pmr::vector<std::unique_ptr<EventBinding>> event_bindings_;
-
- template <typename T>
- auto lv_bind(lv_obj_t* obj, lv_event_code_t ev, T fn) -> void {
- auto binding = std::make_unique<EventBinding>(obj, ev);
- binding->signal().connect(fn);
- event_bindings_.push_back(std::move(binding));
- }
-
lv_obj_t* const root_;
lv_obj_t* content_;
lv_obj_t* modal_content_;
@@ -73,16 +48,6 @@ class Screen {
lv_group_t* const group_;
lv_group_t* modal_group_;
-
- private:
- std::unique_ptr<widgets::TopBar> top_bar_;
-};
-
-class MenuScreen : public Screen {
- public:
- MenuScreen(models::TopBar&,
- const std::pmr::string& title,
- bool show_back_button = true);
};
} // namespace ui
diff --git a/src/ui/include/screen_settings.hpp b/src/ui/include/screen_settings.hpp
deleted file mode 100644
index 7402f9f9..00000000
--- a/src/ui/include/screen_settings.hpp
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#pragma once
-
-#include <stdint.h>
-#include <cstdint>
-#include <list>
-#include <memory>
-#include <vector>
-
-#include "bluetooth.hpp"
-#include "bluetooth_types.hpp"
-#include "display.hpp"
-#include "index.hpp"
-#include "lvgl.h"
-
-#include "model_top_bar.hpp"
-#include "nvs.hpp"
-#include "samd.hpp"
-#include "screen.hpp"
-
-namespace ui {
-namespace screens {
-
-class Settings : public MenuScreen {
- public:
- Settings(models::TopBar&);
-};
-
-class Bluetooth : public MenuScreen {
- public:
- Bluetooth(models::TopBar&, drivers::Bluetooth& bt, drivers::NvsStorage& nvs);
- ~Bluetooth();
-
- auto ChangeEnabledState(bool enabled) -> void;
- auto RefreshDevicesList() -> void;
- auto OnDeviceSelected(ssize_t index) -> void;
-
- private:
- auto RemoveAllDevices() -> void;
- auto AddPreferredDevice(const drivers::bluetooth::Device&) -> void;
- auto AddDevice(const drivers::bluetooth::Device&) -> void;
-
- drivers::Bluetooth& bt_;
- drivers::NvsStorage& nvs_;
-
- lv_obj_t* devices_list_;
- lv_obj_t* preferred_device_;
-
- std::list<drivers::bluetooth::mac_addr_t> macs_in_list_;
-};
-
-class Headphones : public MenuScreen {
- public:
- Headphones(models::TopBar&, drivers::NvsStorage& nvs);
-
- auto ChangeMaxVolume(uint8_t index) -> void;
- auto ChangeCustomVolume(int8_t diff) -> void;
-
- private:
- auto UpdateCustomVol(uint16_t) -> void;
-
- drivers::NvsStorage& nvs_;
- lv_obj_t* custom_vol_container_;
- lv_obj_t* custom_vol_label_;
-
- std::vector<uint16_t> index_to_level_;
- uint16_t custom_limit_;
-};
-
-class Appearance : public MenuScreen {
- public:
- Appearance(models::TopBar&,
- drivers::NvsStorage& nvs,
- drivers::Display& display);
-
- auto ChangeBrightness(uint_fast8_t) -> void;
- auto CommitBrightness() -> void;
-
- private:
- drivers::NvsStorage& nvs_;
- drivers::Display& display_;
-
- lv_obj_t* current_brightness_label_;
- uint_fast8_t current_brightness_;
-};
-
-class InputMethod : public MenuScreen {
- public:
- InputMethod(models::TopBar&, drivers::NvsStorage& nvs);
-
- private:
- drivers::NvsStorage& nvs_;
-};
-
-class Storage : public MenuScreen {
- public:
- Storage(models::TopBar&);
-};
-
-class FirmwareUpdate : public MenuScreen {
- public:
- FirmwareUpdate(models::TopBar&, drivers::Samd&);
-};
-
-class About : public MenuScreen {
- public:
- About(models::TopBar&);
-};
-
-} // namespace screens
-} // namespace ui
diff --git a/src/ui/include/ui_events.hpp b/src/ui/include/ui_events.hpp
index 59be7606..07b18dea 100644
--- a/src/ui/include/ui_events.hpp
+++ b/src/ui/include/ui_events.hpp
@@ -35,21 +35,6 @@ struct ReindexDatabase : tinyfsm::Event {};
struct BackPressed : tinyfsm::Event {};
struct ShowNowPlaying : tinyfsm::Event {};
-struct ShowSettingsPage : tinyfsm::Event {
- enum class Page {
- kRoot,
- kBluetooth,
- kHeadphones,
- kAppearance,
- kInput,
- kStorage,
- kFirmwareUpdate,
- kAbout,
- } page;
-};
-struct OnboardingNavigate : tinyfsm::Event {
- bool forwards;
-};
struct ModalConfirmPressed : tinyfsm::Event {};
struct ModalCancelPressed : tinyfsm::Event {};
diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp
index 33f9eac4..0b883ee0 100644
--- a/src/ui/include/ui_fsm.hpp
+++ b/src/ui/include/ui_fsm.hpp
@@ -12,26 +12,21 @@
#include "audio_events.hpp"
#include "battery.hpp"
-#include "bindey/property.h"
#include "db_events.hpp"
+#include "display.hpp"
+#include "encoder_input.hpp"
#include "gpios.hpp"
#include "lua_thread.hpp"
#include "lvgl_task.hpp"
-#include "model_playback.hpp"
-#include "model_top_bar.hpp"
+#include "modal.hpp"
#include "nvs.hpp"
#include "property.hpp"
#include "relative_wheel.hpp"
-#include "screen_settings.hpp"
-#include "service_locator.hpp"
-#include "tinyfsm.hpp"
-
-#include "display.hpp"
-#include "encoder_input.hpp"
-#include "modal.hpp"
#include "screen.hpp"
+#include "service_locator.hpp"
#include "storage.hpp"
#include "system_events.hpp"
+#include "tinyfsm.hpp"
#include "touchwheel.hpp"
#include "track.hpp"
#include "track_queue.hpp"
@@ -60,20 +55,21 @@ class UiState : public tinyfsm::Fsm<UiState> {
virtual void react(const audio::PlaybackFinished&);
virtual void react(const audio::PlaybackUpdate&);
virtual void react(const audio::QueueUpdate&);
- virtual void react(const audio::VolumeChanged&){};
+
+ virtual void react(const audio::VolumeChanged&);
+ virtual void react(const audio::VolumeBalanceChanged&);
+ virtual void react(const audio::VolumeLimitChanged&);
virtual void react(const system_fsm::KeyLockChanged&);
virtual void react(const OnLuaError&) {}
virtual void react(const internal::BackPressed&) {}
- virtual void react(const internal::ShowSettingsPage&){};
virtual void react(const internal::ModalCancelPressed&) {
sCurrentModal.reset();
}
virtual void react(const internal::ModalConfirmPressed&) {
sCurrentModal.reset();
}
- virtual void react(const internal::OnboardingNavigate&) {}
void react(const internal::ControlSchemeChanged&);
virtual void react(const internal::ReindexDatabase&){};
@@ -102,10 +98,29 @@ class UiState : public tinyfsm::Fsm<UiState> {
static std::shared_ptr<Modal> sCurrentModal;
static std::shared_ptr<lua::LuaThread> sLua;
- static std::weak_ptr<screens::Bluetooth> bluetooth_screen_;
+ static lua::Property sBatteryPct;
+ static lua::Property sBatteryMv;
+ static lua::Property sBatteryCharging;
- static models::Playback sPlaybackModel;
- static models::TopBar sTopBarModel;
+ static lua::Property sBluetoothEnabled;
+ static lua::Property sBluetoothConnected;
+
+ static lua::Property sPlaybackPlaying;
+
+ static lua::Property sPlaybackTrack;
+ static lua::Property sPlaybackPosition;
+
+ static lua::Property sQueuePosition;
+ static lua::Property sQueueSize;
+ static lua::Property sQueueRepeat;
+ static lua::Property sQueueRandom;
+
+ static lua::Property sVolumeCurrentPct;
+ static lua::Property sVolumeCurrentDb;
+ static lua::Property sVolumeLeftBias;
+ static lua::Property sVolumeLimit;
+
+ static lua::Property sDisplayBrightness;
};
namespace states {
@@ -113,8 +128,10 @@ namespace states {
class Splash : public UiState {
public:
void exit() override;
+
void react(const system_fsm::BootComplete&) override;
void react(const system_fsm::StorageMounted&) override;
+
using UiState::react;
};
@@ -124,15 +141,6 @@ class Lua : public UiState {
void exit() override;
void react(const OnLuaError&) override;
-
- void react(const internal::ShowSettingsPage&) override;
-
- void react(const system_fsm::BatteryStateChanged&) override;
- void react(const audio::QueueUpdate&) override;
- void react(const audio::PlaybackStarted&) override;
- void react(const audio::PlaybackUpdate&) override;
- void react(const audio::PlaybackFinished&) override;
- void react(const audio::VolumeChanged&) override;
void react(const internal::BackPressed&) override;
using UiState::react;
@@ -147,54 +155,8 @@ class Lua : public UiState {
auto SetPlaying(const lua::LuaValue&) -> bool;
auto SetRandom(const lua::LuaValue&) -> bool;
auto SetRepeat(const lua::LuaValue&) -> bool;
-
- std::shared_ptr<lua::Property> battery_pct_;
- std::shared_ptr<lua::Property> battery_mv_;
- std::shared_ptr<lua::Property> battery_charging_;
-
- std::shared_ptr<lua::Property> bluetooth_en_;
-
- std::shared_ptr<lua::Property> playback_playing_;
- std::shared_ptr<lua::Property> playback_track_;
- std::shared_ptr<lua::Property> playback_position_;
-
- std::shared_ptr<lua::Property> queue_position_;
- std::shared_ptr<lua::Property> queue_size_;
- std::shared_ptr<lua::Property> queue_repeat_;
- std::shared_ptr<lua::Property> queue_random_;
-
- std::shared_ptr<lua::Property> volume_current_pct_;
- std::shared_ptr<lua::Property> volume_current_db_;
-};
-
-class Browse : public UiState {
- public:
- void entry() override;
-
- void react(const internal::BackPressed&) override;
-
- void react(const internal::ShowSettingsPage&) override;
- void react(const internal::ReindexDatabase&) override;
-
- void react(const system_fsm::BluetoothDevicesChanged&) override;
-
- using UiState::react;
};
-class Indexing : public UiState {
- public:
- void entry() override;
- void exit() override;
-
- void react(const database::event::UpdateStarted&) override;
- void react(const database::event::UpdateProgress&) override;
- void react(const database::event::UpdateFinished&) override;
-
- using UiState::react;
-};
-
-class FatalError : public UiState {};
-
} // namespace states
} // namespace ui
diff --git a/src/ui/include/widget_top_bar.hpp b/src/ui/include/widget_top_bar.hpp
deleted file mode 100644
index b240188c..00000000
--- a/src/ui/include/widget_top_bar.hpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#pragma once
-
-#include <cstdint>
-#include <string>
-
-#include "bindey/binding.h"
-#include "lvgl.h"
-
-#include "memory_resource.hpp"
-#include "model_top_bar.hpp"
-
-namespace ui {
-
-namespace widgets {
-
-class TopBar {
- public:
- struct Configuration {
- bool show_back_button;
- std::pmr::string title;
- };
-
- 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_; }
-
- private:
- std::vector<bindey::scoped_binding> bindings_;
-
- lv_obj_t* container_;
- lv_obj_t* back_button_;
-};
-
-} // namespace widgets
-
-} // namespace ui
diff --git a/src/ui/lvgl_task.cpp b/src/ui/lvgl_task.cpp
index 96f9bdf5..f0184766 100644
--- a/src/ui/lvgl_task.cpp
+++ b/src/ui/lvgl_task.cpp
@@ -81,10 +81,6 @@ auto UiTask::Main() -> void {
lv_group_set_focus_cb(current_group, &group_focus_cb);
}
- if (current_screen_) {
- current_screen_->Tick();
- }
-
TickType_t delay = lv_timer_handler();
vTaskDelay(pdMS_TO_TICKS(std::clamp<TickType_t>(delay, 0, 100)));
}
diff --git a/src/ui/modal.cpp b/src/ui/modal.cpp
index 3cab85f5..88f6d3ef 100644
--- a/src/ui/modal.cpp
+++ b/src/ui/modal.cpp
@@ -5,8 +5,9 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
+#include "modal.hpp"
+
#include "misc/lv_color.h"
-#include "modal_progress.hpp"
#include "core/lv_event.h"
#include "esp_log.h"
@@ -24,7 +25,6 @@
#include "themes.hpp"
#include "ui_events.hpp"
#include "ui_fsm.hpp"
-#include "widget_top_bar.hpp"
#include "widgets/lv_label.h"
namespace ui {
diff --git a/src/ui/modal_confirm.cpp b/src/ui/modal_confirm.cpp
deleted file mode 100644
index dfb1b1eb..00000000
--- a/src/ui/modal_confirm.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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::pmr::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
deleted file mode 100644
index d3d3e9b9..00000000
--- a/src/ui/modal_progress.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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::pmr::string title_text,
- std::pmr::string subtitle_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);
-
- title_ = lv_label_create(root_);
- lv_obj_set_size(title_, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
-
- subtitle_ = lv_label_create(root_);
- lv_obj_set_size(subtitle_, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
-
- lv_obj_t* spinner = lv_spinner_create(root_, 3000, 45);
- lv_obj_set_size(spinner, 16, 16);
-
- title(title_text);
- subtitle(subtitle_text);
-}
-
-void Progress::title(const std::pmr::string& s) {
- lv_label_set_text(title_, s.c_str());
-}
-
-void Progress::subtitle(const std::pmr::string& s) {
- if (s.empty()) {
- lv_obj_add_flag(subtitle_, LV_OBJ_FLAG_HIDDEN);
- } else {
- lv_obj_clear_flag(subtitle_, LV_OBJ_FLAG_HIDDEN);
- lv_label_set_text(subtitle_, s.c_str());
- }
-}
-
-} // namespace modals
-} // namespace ui
diff --git a/src/ui/screen.cpp b/src/ui/screen.cpp
index 9ac5ec0e..3e4f8e42 100644
--- a/src/ui/screen.cpp
+++ b/src/ui/screen.cpp
@@ -13,8 +13,6 @@
#include "hal/lv_hal_disp.h"
#include "misc/lv_area.h"
#include "misc/lv_color.h"
-#include "model_top_bar.hpp"
-#include "widget_top_bar.hpp"
namespace ui {
@@ -49,43 +47,4 @@ Screen::~Screen() {
lv_obj_del(root_);
}
-auto Screen::CreateTopBar(lv_obj_t* parent,
- const widgets::TopBar::Configuration& config,
- models::TopBar& model) -> widgets::TopBar* {
- assert(top_bar_ == nullptr);
- top_bar_ = std::make_unique<widgets::TopBar>(parent, config, model);
- if (top_bar_->button()) {
- lv_group_add_obj(group_, top_bar_->button());
- }
- return top_bar_.get();
-}
-
-MenuScreen::MenuScreen(models::TopBar& top_bar_model,
- const std::pmr::string& title,
- bool show_back_button)
- : Screen() {
- lv_group_set_wrap(group_, false);
-
- 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_START, LV_FLEX_ALIGN_CENTER,
- LV_FLEX_ALIGN_CENTER);
-
- widgets::TopBar::Configuration config{
- .show_back_button = show_back_button,
- .title = title.c_str(),
- };
- CreateTopBar(content_, config, top_bar_model);
-
- content_ = lv_obj_create(content_);
- lv_obj_set_flex_grow(content_, 1);
- lv_obj_set_width(content_, lv_pct(100));
- 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_START, LV_FLEX_ALIGN_START,
- LV_FLEX_ALIGN_START);
-
- lv_obj_set_style_pad_all(content_, 4, LV_PART_MAIN);
-}
-
} // namespace ui
diff --git a/src/ui/screen_settings.cpp b/src/ui/screen_settings.cpp
deleted file mode 100644
index 3f4c2c46..00000000
--- a/src/ui/screen_settings.cpp
+++ /dev/null
@@ -1,575 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#include "screen_settings.hpp"
-#include <stdint.h>
-#include <string>
-
-#include "audio_events.hpp"
-#include "bluetooth.hpp"
-#include "bluetooth_types.hpp"
-#include "core/lv_event.h"
-#include "core/lv_obj.h"
-#include "core/lv_obj_tree.h"
-#include "display.hpp"
-#include "esp_app_desc.h"
-#include "esp_log.h"
-
-#include "core/lv_group.h"
-#include "core/lv_obj_pos.h"
-#include "event_queue.hpp"
-#include "extra/layouts/flex/lv_flex.h"
-#include "extra/widgets/list/lv_list.h"
-#include "extra/widgets/menu/lv_menu.h"
-#include "extra/widgets/spinbox/lv_spinbox.h"
-#include "extra/widgets/spinner/lv_spinner.h"
-#include "hal/lv_hal_disp.h"
-#include "index.hpp"
-#include "lv_api_map.h"
-#include "misc/lv_anim.h"
-#include "misc/lv_area.h"
-#include "model_top_bar.hpp"
-#include "nvs.hpp"
-#include "samd.hpp"
-#include "screen.hpp"
-#include "themes.hpp"
-#include "ui_events.hpp"
-#include "ui_fsm.hpp"
-#include "widget_top_bar.hpp"
-#include "widgets/lv_bar.h"
-#include "widgets/lv_btn.h"
-#include "widgets/lv_dropdown.h"
-#include "widgets/lv_label.h"
-#include "widgets/lv_slider.h"
-#include "widgets/lv_switch.h"
-#include "wm8523.hpp"
-
-namespace ui {
-namespace screens {
-
-using Page = internal::ShowSettingsPage::Page;
-
-static void open_sub_menu_cb(lv_event_t* e) {
- Page next_page = static_cast<Page>(reinterpret_cast<uintptr_t>(e->user_data));
- events::Ui().Dispatch(internal::ShowSettingsPage{
- .page = next_page,
- });
-}
-
-static void sub_menu(lv_obj_t* list,
- lv_group_t* group,
- const std::pmr::string& text,
- Page page) {
- lv_obj_t* item = lv_list_add_btn(list, NULL, text.c_str());
- lv_group_add_obj(group, item);
- lv_obj_add_event_cb(item, open_sub_menu_cb, LV_EVENT_CLICKED,
- reinterpret_cast<void*>(static_cast<uintptr_t>(page)));
-}
-
-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));
-
- themes::Theme::instance()->ApplyStyle(lv_list_add_text(list, "Audio"),
- themes::Style::kMenuSubheadFirst);
- sub_menu(list, group_, "Bluetooth", Page::kBluetooth);
- sub_menu(list, group_, "Headphones", Page::kHeadphones);
-
- themes::Theme::instance()->ApplyStyle(lv_list_add_text(list, "Interface"),
- themes::Style::kMenuSubhead);
- sub_menu(list, group_, "Appearance", Page::kAppearance);
- sub_menu(list, group_, "Input Method", Page::kInput);
-
- themes::Theme::instance()->ApplyStyle(lv_list_add_text(list, "System"),
- themes::Style::kMenuSubhead);
- sub_menu(list, group_, "Storage", Page::kStorage);
- sub_menu(list, group_, "Firmware Update", Page::kFirmwareUpdate);
- sub_menu(list, group_, "About", Page::kAbout);
-}
-
-static auto settings_container(lv_obj_t* parent) -> lv_obj_t* {
- lv_obj_t* res = lv_obj_create(parent);
- lv_obj_set_layout(res, LV_LAYOUT_FLEX);
- lv_obj_set_size(res, lv_pct(100), LV_SIZE_CONTENT);
- lv_obj_set_flex_flow(res, LV_FLEX_FLOW_ROW);
- lv_obj_set_flex_align(res, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER,
- LV_FLEX_ALIGN_START);
- return res;
-}
-
-static auto label_pair(lv_obj_t* parent,
- const std::pmr::string& left,
- const std::pmr::string& right) -> lv_obj_t* {
- lv_obj_t* container = settings_container(parent);
- lv_obj_t* left_label = lv_label_create(container);
- lv_label_set_text(left_label, left.c_str());
- lv_obj_t* right_label = lv_label_create(container);
- lv_label_set_text(right_label, right.c_str());
- return right_label;
-}
-
-static auto toggle_bt_cb(lv_event_t* ev) {
- Bluetooth* instance = reinterpret_cast<Bluetooth*>(ev->user_data);
- instance->ChangeEnabledState(lv_obj_has_state(ev->target, LV_STATE_CHECKED));
-}
-
-static auto select_device_cb(lv_event_t* ev) {
- Bluetooth* instance = reinterpret_cast<Bluetooth*>(ev->user_data);
- instance->OnDeviceSelected(lv_obj_get_index(ev->target));
-}
-
-static auto remove_preferred_cb(lv_event_t* ev) {
- Bluetooth* instance = reinterpret_cast<Bluetooth*>(ev->user_data);
- instance->OnDeviceSelected(-1);
-}
-
-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");
- lv_obj_set_flex_grow(toggle_label, 1);
- lv_obj_t* toggle = lv_switch_create(toggle_container);
- lv_group_add_obj(group_, toggle);
-
- if (bt.IsEnabled()) {
- lv_obj_add_state(toggle, LV_STATE_CHECKED);
- }
-
- lv_obj_add_event_cb(toggle, toggle_bt_cb, LV_EVENT_VALUE_CHANGED, this);
-
- lv_obj_t* devices_label = lv_label_create(content_);
- lv_label_set_text(devices_label, "Devices");
-
- devices_list_ = lv_list_create(content_);
- RefreshDevicesList();
- bt_.SetDeviceDiscovery(true);
-}
-
-Bluetooth::~Bluetooth() {
- bt_.SetDeviceDiscovery(false);
-}
-
-auto Bluetooth::ChangeEnabledState(bool enabled) -> void {
- if (enabled) {
- events::System().RunOnTask([&]() { bt_.Enable(); });
- nvs_.OutputMode(drivers::NvsStorage::Output::kBluetooth);
- } else {
- events::System().RunOnTask([&]() { bt_.Disable(); });
- nvs_.OutputMode(drivers::NvsStorage::Output::kHeadphones);
- }
- events::Audio().Dispatch(audio::OutputModeChanged{});
- RefreshDevicesList();
-}
-
-auto Bluetooth::RefreshDevicesList() -> void {
- if (!bt_.IsEnabled()) {
- // Bluetooth is disabled, so we just clear the list.
- RemoveAllDevices();
- return;
- }
-
- auto devices = bt_.KnownDevices();
- std::optional<drivers::bluetooth::mac_addr_t> preferred_device =
- nvs_.PreferredBluetoothDevice();
-
- // If the user's current selection is within the devices list, then we need
- // to be careful not to rearrange the list items underneath them.
- lv_obj_t* current_selection = lv_group_get_focused(group_);
- bool is_in_list = current_selection != NULL &&
- lv_obj_get_parent(current_selection) == devices_list_;
-
- if (!is_in_list) {
- // The user isn't in the list! We can blow everything away and recreate it
- // without issues.
- RemoveAllDevices();
-
- // First look to see if the user's preferred device is in the list. If it
- // is, we hoist it up to the top of the list.
- if (preferred_device) {
- for (size_t i = 0; i < devices.size(); i++) {
- if (devices[i].address == *preferred_device) {
- AddPreferredDevice(devices[i]);
- devices.erase(devices.begin() + i);
- break;
- }
- }
- }
-
- // The rest of the list is already sorted by signal strength.
- for (const auto& device : devices) {
- AddDevice(device);
- }
- } else {
- // The user's selection is within the device list. We need to work out
- // which devices are new, then add them to the end.
- for (const auto& mac : macs_in_list_) {
- auto pos = std::find_if(
- devices.begin(), devices.end(),
- [&mac](const auto& device) { return device.address == mac; });
-
- if (pos != devices.end()) {
- devices.erase(pos);
- }
- }
-
- // The remaining list is now just the new devices.
- for (const auto& device : devices) {
- if (preferred_device && device.address == *preferred_device) {
- AddPreferredDevice(device);
- } else {
- AddDevice(device);
- }
- }
- }
-}
-
-auto Bluetooth::RemoveAllDevices() -> void {
- while (lv_obj_get_child_cnt(devices_list_) > 0) {
- lv_obj_del(lv_obj_get_child(devices_list_, 0));
- }
- macs_in_list_.clear();
- preferred_device_ = nullptr;
-}
-
-auto Bluetooth::AddPreferredDevice(const drivers::bluetooth::Device& dev)
- -> void {
- preferred_device_ = lv_list_add_btn(devices_list_, NULL, dev.name.c_str());
- lv_obj_t* remove = lv_btn_create(preferred_device_);
- lv_obj_t* remove_icon = lv_label_create(remove);
- lv_label_set_text(remove_icon, "x");
- lv_group_add_obj(group_, remove);
-
- macs_in_list_.push_back(dev.address);
-}
-
-auto Bluetooth::AddDevice(const drivers::bluetooth::Device& dev) -> void {
- lv_obj_t* item = lv_list_add_btn(devices_list_, NULL, dev.name.c_str());
- lv_group_add_obj(group_, item);
- lv_obj_add_event_cb(item, select_device_cb, LV_EVENT_CLICKED, this);
- macs_in_list_.push_back(dev.address);
-}
-
-auto Bluetooth::OnDeviceSelected(ssize_t index) -> void {
- if (index == -1) {
- events::System().RunOnTask([=]() {
- nvs_.PreferredBluetoothDevice({});
- bt_.SetPreferredDevice({});
- });
- RefreshDevicesList();
- return;
- }
-
- // Tell the bluetooth driver that our preference changed.
- auto it = macs_in_list_.begin();
- std::advance(it, index);
- events::System().RunOnTask([=]() { bt_.SetPreferredDevice(*it); });
-
- // Update which devices are selectable.
- if (preferred_device_) {
- lv_group_add_obj(group_, preferred_device_);
- // Bubble the newly added object up to its visible position in the list.
- size_t pos = lv_obj_get_index(preferred_device_);
- while (pos > 0) {
- lv_group_swap_obj(preferred_device_,
- lv_obj_get_child(devices_list_, pos - 1));
- pos--;
- }
- }
-
- preferred_device_ = lv_obj_get_child(devices_list_, index);
- lv_group_remove_obj(preferred_device_);
-}
-
-static void change_vol_limit_cb(lv_event_t* ev) {
- int selected_index = lv_dropdown_get_selected(ev->target);
- Headphones* instance = reinterpret_cast<Headphones*>(ev->user_data);
- instance->ChangeMaxVolume(selected_index);
-}
-
-static void increase_vol_limit_cb(lv_event_t* ev) {
- Headphones* instance = reinterpret_cast<Headphones*>(ev->user_data);
- instance->ChangeCustomVolume(2);
-}
-
-static void decrease_vol_limit_cb(lv_event_t* ev) {
- Headphones* instance = reinterpret_cast<Headphones*>(ev->user_data);
- instance->ChangeCustomVolume(-2);
-}
-
-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));
- index_to_level_.push_back(reference + (9.5 * 4));
-
- lv_obj_t* vol_label = lv_label_create(content_);
- lv_label_set_text(vol_label, "Volume Limit");
- lv_obj_t* vol_dropdown = lv_dropdown_create(content_);
- lv_obj_set_width(vol_dropdown, lv_pct(100));
- lv_dropdown_set_options(
- vol_dropdown,
- "Line Level (-10 dB)\nCD Level (+6 dB)\nMaximum (+10dB)\nCustom");
- lv_group_add_obj(group_, vol_dropdown);
- themes::Theme::instance()->ApplyStyle(lv_dropdown_get_list(vol_dropdown),
- themes::Style::kPopup);
-
- uint16_t level = nvs.AmpMaxVolume();
- for (int i = 0; i < index_to_level_.size() + 1; i++) {
- if (i == index_to_level_.size() || index_to_level_[i] == level) {
- lv_dropdown_set_selected(vol_dropdown, i);
- break;
- }
- }
-
- lv_obj_add_event_cb(vol_dropdown, change_vol_limit_cb, LV_EVENT_VALUE_CHANGED,
- this);
-
- custom_vol_container_ = settings_container(content_);
-
- lv_obj_t* decrease_btn = lv_btn_create(custom_vol_container_);
- lv_obj_t* btn_label = lv_label_create(decrease_btn);
- lv_label_set_text(btn_label, "-");
- lv_obj_add_event_cb(decrease_btn, decrease_vol_limit_cb, LV_EVENT_CLICKED,
- this);
-
- custom_vol_label_ = lv_label_create(custom_vol_container_);
- UpdateCustomVol(level);
-
- lv_obj_t* increase_btn = lv_btn_create(custom_vol_container_);
- btn_label = lv_label_create(increase_btn);
- lv_label_set_text(btn_label, "+");
- lv_obj_add_event_cb(increase_btn, increase_vol_limit_cb, LV_EVENT_CLICKED,
- this);
-
- if (lv_dropdown_get_selected(vol_dropdown) != index_to_level_.size()) {
- lv_obj_add_flag(custom_vol_container_, LV_OBJ_FLAG_HIDDEN);
- }
-
- lv_obj_t* spacer = lv_obj_create(content_);
- lv_obj_set_size(spacer, 1, 4);
-
- lv_obj_t* balance_label = lv_label_create(content_);
- lv_label_set_text(balance_label, "Left/Right Balance");
-
- spacer = lv_obj_create(content_);
- lv_obj_set_size(spacer, 1, 4);
-
- lv_obj_t* balance = lv_slider_create(content_);
- lv_obj_set_size(balance, lv_pct(100), 5);
- lv_slider_set_range(balance, -10, 10);
- lv_slider_set_value(balance, 0, LV_ANIM_OFF);
- lv_slider_set_mode(balance, LV_SLIDER_MODE_SYMMETRICAL);
- lv_group_add_obj(group_, balance);
- lv_obj_t* current_balance_label = lv_label_create(content_);
- lv_label_set_text(current_balance_label, "0dB");
- lv_obj_set_size(current_balance_label, lv_pct(100), LV_SIZE_CONTENT);
-
- lv_obj_move_foreground(lv_dropdown_get_list(vol_dropdown));
-}
-
-auto Headphones::ChangeMaxVolume(uint8_t index) -> void {
- if (index >= index_to_level_.size()) {
- lv_obj_clear_flag(custom_vol_container_, LV_OBJ_FLAG_HIDDEN);
- return;
- }
- auto vol = index_to_level_[index];
- lv_obj_add_flag(custom_vol_container_, LV_OBJ_FLAG_HIDDEN);
- UpdateCustomVol(vol);
- events::Audio().Dispatch(audio::ChangeMaxVolume{.new_max = vol});
-}
-
-auto Headphones::ChangeCustomVolume(int8_t diff) -> void {
- UpdateCustomVol(custom_limit_ + diff);
-}
-
-auto Headphones::UpdateCustomVol(uint16_t level) -> void {
- custom_limit_ = level;
- int16_t db = (static_cast<int32_t>(level) -
- drivers::wm8523::kLineLevelReferenceVolume) /
- 4;
- int16_t db_parts = (static_cast<int32_t>(level) -
- drivers::wm8523::kLineLevelReferenceVolume) %
- 4;
-
- std::ostringstream builder;
- if (db >= 0) {
- builder << "+";
- }
- builder << db << ".";
- builder << (db_parts * 100 / 4);
- builder << " dBV";
-
- lv_label_set_text(custom_vol_label_, builder.str().c_str());
-}
-
-static void change_brightness_cb(lv_event_t* ev) {
- Appearance* instance = reinterpret_cast<Appearance*>(ev->user_data);
- instance->ChangeBrightness(lv_slider_get_value(ev->target));
-}
-
-static void release_brightness_cb(lv_event_t* ev) {
- Appearance* instance = reinterpret_cast<Appearance*>(ev->user_data);
- instance->CommitBrightness();
-}
-
-static auto brightness_str(uint_fast8_t percent) -> std::string {
- return std::to_string(percent) + "%";
-}
-
-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);
- lv_label_set_text(toggle_label, "Dark Mode");
- lv_obj_t* toggle = lv_switch_create(toggle_container);
- lv_group_add_obj(group_, toggle);
-
- uint_fast8_t initial_brightness = nvs_.ScreenBrightness();
-
- lv_obj_t* brightness_label = lv_label_create(content_);
- lv_label_set_text(brightness_label, "Brightness");
- lv_obj_t* brightness = lv_slider_create(content_);
- lv_obj_set_size(brightness, lv_pct(100), 5);
- lv_slider_set_range(brightness, 10, 100);
- lv_slider_set_value(brightness, initial_brightness, LV_ANIM_OFF);
- lv_group_add_obj(group_, brightness);
- current_brightness_label_ = lv_label_create(content_);
- lv_label_set_text(current_brightness_label_,
- brightness_str(initial_brightness).c_str());
- lv_obj_set_size(current_brightness_label_, lv_pct(100), LV_SIZE_CONTENT);
-
- lv_obj_add_event_cb(brightness, change_brightness_cb, LV_EVENT_VALUE_CHANGED,
- this);
- lv_obj_add_event_cb(brightness, release_brightness_cb, LV_EVENT_RELEASED,
- this);
-}
-
-auto Appearance::ChangeBrightness(uint_fast8_t new_level) -> void {
- current_brightness_ = new_level;
- display_.SetBrightness(new_level);
- lv_label_set_text(current_brightness_label_,
- brightness_str(new_level).c_str());
-}
-
-auto Appearance::CommitBrightness() -> void {
- nvs_.ScreenBrightness(current_brightness_);
-}
-
-InputMethod::InputMethod(models::TopBar& bar, drivers::NvsStorage& nvs)
- : MenuScreen(bar, "Input Method"), nvs_(nvs) {
- lv_obj_t* primary_label = lv_label_create(content_);
- lv_label_set_text(primary_label, "Control scheme");
- lv_obj_t* primary_dropdown = lv_dropdown_create(content_);
- lv_dropdown_set_options(
- primary_dropdown,
- "Side buttons only\nButtons and touch\nD-Pad\nClickwheel");
- lv_group_add_obj(group_, primary_dropdown);
-
- lv_dropdown_set_selected(primary_dropdown,
- static_cast<uint16_t>(nvs.PrimaryInput()));
- themes::Theme::instance()->ApplyStyle(lv_dropdown_get_list(primary_dropdown),
- themes::Style::kPopup);
-
- lv_bind(primary_dropdown, LV_EVENT_VALUE_CHANGED, [this](lv_obj_t* obj) {
- drivers::NvsStorage::InputModes mode;
- switch (lv_dropdown_get_selected(obj)) {
- case 0:
- mode = drivers::NvsStorage::InputModes::kButtonsOnly;
- break;
- case 1:
- mode = drivers::NvsStorage::InputModes::kButtonsWithWheel;
- break;
- case 2:
- mode = drivers::NvsStorage::InputModes::kDirectionalWheel;
- break;
- case 3:
- mode = drivers::NvsStorage::InputModes::kRotatingWheel;
- break;
- default:
- return;
- }
- nvs_.PrimaryInput(mode);
- events::Ui().Dispatch(internal::ControlSchemeChanged{});
- });
-}
-
-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");
-
- lv_obj_t* usage_bar = lv_bar_create(content_);
- lv_bar_set_range(usage_bar, 0, 32);
- lv_bar_set_value(usage_bar, 6, LV_ANIM_OFF);
-
- lv_obj_t* container = lv_obj_create(content_);
- lv_obj_set_size(container, lv_pct(100), 30);
- lv_obj_set_layout(container, LV_LAYOUT_FLEX);
- lv_obj_set_flex_flow(container, LV_FLEX_FLOW_ROW);
- lv_obj_set_flex_align(container, LV_FLEX_ALIGN_SPACE_EVENLY,
- LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
-
- lv_obj_t* reset_btn = lv_btn_create(container);
- lv_obj_t* reset_label = lv_label_create(reset_btn);
- lv_label_set_text(reset_label, "Update Database");
- lv_group_add_obj(group_, reset_btn);
- themes::Theme::instance()->ApplyStyle(reset_btn,
- themes::Style::kButtonPrimary);
-
- lv_bind(reset_btn, LV_EVENT_CLICKED, [&](lv_obj_t*) {
- events::Ui().Dispatch(internal::ReindexDatabase{});
- });
-}
-
-FirmwareUpdate::FirmwareUpdate(models::TopBar& bar, drivers::Samd& samd)
- : MenuScreen(bar, "Firmware Update") {
- lv_obj_set_flex_align(content_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER,
- LV_FLEX_ALIGN_CENTER);
-
- auto samd_ver = samd.Version();
- label_pair(content_, "SAMD21 FW:", {samd_ver.data(), samd_ver.size()});
-
- lv_obj_t* spacer = lv_obj_create(content_);
- lv_obj_set_size(spacer, 1, 4);
-
- lv_obj_t* flash_esp_btn = lv_btn_create(content_);
- lv_obj_t* flash_esp_label = lv_label_create(flash_esp_btn);
- lv_label_set_text(flash_esp_label, "Update");
- lv_group_add_obj(group_, flash_esp_btn);
- themes::Theme::instance()->ApplyStyle(flash_esp_btn,
- themes::Style::kButtonPrimary);
-
- spacer = lv_obj_create(content_);
- lv_obj_set_size(spacer, 1, 8);
-
- auto desc = esp_app_get_description();
- label_pair(content_, "ESP32 FW:", desc->version);
-
- spacer = lv_obj_create(content_);
- lv_obj_set_size(spacer, 1, 4);
-
- lv_obj_t* flash_samd_btn = lv_btn_create(content_);
- lv_obj_t* flash_samd_label = lv_label_create(flash_samd_btn);
- lv_label_set_text(flash_samd_label, "Update");
- lv_group_add_obj(group_, flash_samd_btn);
- themes::Theme::instance()->ApplyStyle(flash_samd_btn,
- themes::Style::kButtonPrimary);
-}
-
-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");
-}
-
-} // namespace screens
-} // namespace ui
diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp
index 75327a58..adda1c18 100644
--- a/src/ui/ui_fsm.cpp
+++ b/src/ui/ui_fsm.cpp
@@ -33,15 +33,11 @@
#include "event_queue.hpp"
#include "gpios.hpp"
#include "lvgl_task.hpp"
-#include "modal_confirm.hpp"
-#include "modal_progress.hpp"
-#include "model_playback.hpp"
#include "nvs.hpp"
#include "property.hpp"
#include "relative_wheel.hpp"
#include "screen.hpp"
#include "screen_lua.hpp"
-#include "screen_settings.hpp"
#include "screen_splash.hpp"
#include "spiffs.hpp"
#include "storage.hpp"
@@ -66,13 +62,6 @@ std::shared_ptr<Screen> UiState::sCurrentScreen;
std::shared_ptr<Modal> UiState::sCurrentModal;
std::shared_ptr<lua::LuaThread> UiState::sLua;
-std::weak_ptr<screens::Bluetooth> UiState::bluetooth_screen_;
-
-models::Playback UiState::sPlaybackModel;
-models::TopBar UiState::sTopBarModel{{},
- UiState::sPlaybackModel.is_playing,
- UiState::sPlaybackModel.current_track};
-
static TimerHandle_t sAlertTimer;
static lv_obj_t* sAlertContainer;
@@ -80,6 +69,70 @@ static void alert_timer_callback(TimerHandle_t timer) {
events::Ui().Dispatch(internal::DismissAlerts{});
}
+lua::Property UiState::sBatteryPct{0};
+lua::Property UiState::sBatteryMv{0};
+lua::Property UiState::sBatteryCharging{false};
+
+lua::Property UiState::sBluetoothEnabled{false};
+lua::Property UiState::sBluetoothConnected{false};
+
+lua::Property UiState::sPlaybackPlaying{
+ false, [](const lua::LuaValue& val) {
+ bool current_val = std::get<bool>(sPlaybackPlaying.Get());
+ if (!std::holds_alternative<bool>(val)) {
+ return false;
+ }
+ bool new_val = std::get<bool>(val);
+ if (current_val != new_val) {
+ events::Audio().Dispatch(audio::TogglePlayPause{});
+ }
+ return true;
+ }};
+
+lua::Property UiState::sPlaybackTrack{0};
+lua::Property UiState::sPlaybackPosition{0};
+
+lua::Property UiState::sQueuePosition{0};
+lua::Property UiState::sQueueSize{0};
+lua::Property UiState::sQueueRepeat{false};
+lua::Property UiState::sQueueRandom{false};
+
+lua::Property UiState::sVolumeCurrentPct{
+ 0, [](const lua::LuaValue& val) {
+ events::Audio().Dispatch(audio::SetVolume{
+ .percent = {},
+ .db = 0,
+ });
+ return false;
+ }};
+lua::Property UiState::sVolumeCurrentDb{
+ 0, [](const lua::LuaValue& val) {
+ events::Audio().Dispatch(audio::SetVolume{
+ .percent = {},
+ .db = 0,
+ });
+ return false;
+ }};
+lua::Property UiState::sVolumeLeftBias{
+ 0, [](const lua::LuaValue& val) {
+ events::Audio().Dispatch(audio::SetVolumeBalance{
+ .left_bias = 0,
+ });
+ return false;
+ }};
+lua::Property UiState::sVolumeLimit{
+ 0, [](const lua::LuaValue& val) {
+ events::Audio().Dispatch(audio::SetVolumeLimit{
+ .new_limit = 0,
+ });
+ return false;
+ }};
+
+lua::Property UiState::sDisplayBrightness{0, [](const lua::LuaValue& val) {
+ sDisplay->SetBrightness(0);
+ return false;
+ }};
+
auto UiState::InitBootSplash(drivers::IGpios& gpios) -> bool {
// Init LVGL first, since the display driver registers itself with LVGL.
lv_init();
@@ -118,30 +171,59 @@ void UiState::react(const system_fsm::KeyLockChanged& ev) {
sInput->lock(ev.locking);
}
-void UiState::react(const system_fsm::BatteryStateChanged& ev) {
- sTopBarModel.battery_state.set(ev.new_state);
+void UiState::react(const internal::ControlSchemeChanged&) {
+ if (!sInput) {
+ return;
+ }
+ sInput->mode(sServices->nvs().PrimaryInput());
}
-void UiState::react(const audio::PlaybackStarted&) {}
-
-void UiState::react(const audio::PlaybackFinished&) {}
+void UiState::react(const internal::DismissAlerts&) {
+ lv_obj_clean(sAlertContainer);
+}
-void UiState::react(const audio::PlaybackUpdate& ev) {}
+void UiState::react(const system_fsm::BatteryStateChanged& ev) {
+ sBatteryPct.Update(static_cast<int>(ev.new_state.percent));
+ sBatteryMv.Update(static_cast<int>(ev.new_state.millivolts));
+}
void UiState::react(const audio::QueueUpdate&) {
auto& queue = sServices->track_queue();
- sPlaybackModel.current_track.set(queue.current());
-}
+ sQueueSize.Update(static_cast<int>(queue.totalSize()));
-void UiState::react(const internal::ControlSchemeChanged&) {
- if (!sInput) {
- return;
+ int current_pos = queue.currentPosition();
+ if (queue.current()) {
+ current_pos++;
}
- sInput->mode(sServices->nvs().PrimaryInput());
+ sQueuePosition.Update(current_pos);
+ sQueueRandom.Update(queue.random());
+ sQueueRepeat.Update(queue.repeat());
}
-void UiState::react(const internal::DismissAlerts&) {
- lv_obj_clean(sAlertContainer);
+void UiState::react(const audio::PlaybackStarted& ev) {
+ sPlaybackPlaying.Update(true);
+}
+
+void UiState::react(const audio::PlaybackUpdate& ev) {
+ sPlaybackTrack.Update(*ev.track);
+ sPlaybackPosition.Update(static_cast<int>(ev.seconds_elapsed));
+}
+
+void UiState::react(const audio::PlaybackFinished&) {
+ sPlaybackPlaying.Update(false);
+}
+
+void UiState::react(const audio::VolumeChanged& ev) {
+ sVolumeCurrentPct.Update(static_cast<int>(ev.percent));
+ sVolumeCurrentDb.Update(static_cast<int>(ev.db));
+}
+
+void UiState::react(const audio::VolumeBalanceChanged& ev) {
+ sVolumeLeftBias.Update(ev.left_bias);
+}
+
+void UiState::react(const audio::VolumeLimitChanged& ev) {
+ sVolumeLeftBias.Update(ev.new_limit);
}
namespace states {
@@ -185,57 +267,36 @@ void Lua::entry() {
alert_timer_callback);
sAlertContainer = lv_obj_create(sCurrentScreen->alert());
- auto bat =
- sServices->battery().State().value_or(battery::Battery::BatteryState{});
- battery_pct_ =
- std::make_shared<lua::Property>(static_cast<int>(bat.percent));
- battery_mv_ =
- std::make_shared<lua::Property>(static_cast<int>(bat.millivolts));
- battery_charging_ = std::make_shared<lua::Property>(bat.is_charging);
-
- bluetooth_en_ = std::make_shared<lua::Property>(false);
-
- queue_position_ = std::make_shared<lua::Property>(0);
- queue_size_ = std::make_shared<lua::Property>(0);
- queue_repeat_ = std::make_shared<lua::Property>(false);
- queue_random_ = std::make_shared<lua::Property>(false);
-
- playback_playing_ = std::make_shared<lua::Property>(
- false, [&](const lua::LuaValue& val) { return SetPlaying(val); });
- playback_track_ = std::make_shared<lua::Property>();
- playback_position_ = std::make_shared<lua::Property>();
-
- volume_current_pct_ = std::make_shared<lua::Property>(0);
- volume_current_db_ = std::make_shared<lua::Property>(0);
-
sLua.reset(lua::LuaThread::Start(*sServices, sCurrentScreen->content()));
sLua->bridge().AddPropertyModule("power",
{
- {"battery_pct", battery_pct_},
- {"battery_millivolts", battery_mv_},
- {"plugged_in", battery_charging_},
+ {"battery_pct", &sBatteryPct},
+ {"battery_millivolts", &sBatteryMv},
+ {"plugged_in", &sBatteryCharging},
});
sLua->bridge().AddPropertyModule("bluetooth",
{
- {"enabled", bluetooth_en_},
- {"connected", bluetooth_en_},
+ {"enabled", &sBluetoothEnabled},
+ {"connected", &sBluetoothConnected},
});
sLua->bridge().AddPropertyModule("playback",
{
- {"playing", playback_playing_},
- {"track", playback_track_},
- {"position", playback_position_},
+ {"playing", &sPlaybackPlaying},
+ {"track", &sPlaybackTrack},
+ {"position", &sPlaybackPosition},
});
sLua->bridge().AddPropertyModule("queue", {
- {"position", queue_position_},
- {"size", queue_size_},
- {"replay", queue_repeat_},
- {"random", queue_random_},
+ {"position", &sQueuePosition},
+ {"size", &sQueueSize},
+ {"replay", &sQueueRepeat},
+ {"random", &sQueueRandom},
});
sLua->bridge().AddPropertyModule("volume",
{
- {"current_pct", volume_current_pct_},
- {"current_db", volume_current_db_},
+ {"current_pct", &sVolumeCurrentPct},
+ {"current_db", &sVolumeCurrentDb},
+ {"left_bias", &sVolumeLeftBias},
+ {"limit_db", &sVolumeLimit},
});
sLua->bridge().AddPropertyModule(
@@ -288,18 +349,6 @@ auto Lua::PopLuaScreen(lua_State* s) -> int {
return 0;
}
-auto Lua::SetPlaying(const lua::LuaValue& val) -> bool {
- bool current_val = std::get<bool>(playback_playing_->Get());
- if (!std::holds_alternative<bool>(val)) {
- return false;
- }
- bool new_val = std::get<bool>(val);
- if (current_val != new_val) {
- events::Audio().Dispatch(audio::TogglePlayPause{});
- }
- return true;
-}
-
auto Lua::ShowAlert(lua_State* s) -> int {
if (!sCurrentScreen) {
return 0;
@@ -358,150 +407,10 @@ void Lua::react(const OnLuaError& err) {
ESP_LOGE("lua", "%s", err.message.c_str());
}
-void Lua::react(const internal::ShowSettingsPage& ev) {
- PushScreen(std::shared_ptr<Screen>(new screens::Settings(sTopBarModel)));
- transit<Browse>();
-}
-
-void Lua::react(const system_fsm::BatteryStateChanged& ev) {
- battery_pct_->Update(static_cast<int>(ev.new_state.percent));
- battery_mv_->Update(static_cast<int>(ev.new_state.millivolts));
-}
-
-void Lua::react(const audio::QueueUpdate&) {
- auto& queue = sServices->track_queue();
- queue_size_->Update(static_cast<int>(queue.totalSize()));
-
- int current_pos = queue.currentPosition();
- if (queue.current()) {
- current_pos++;
- }
- queue_position_->Update(current_pos);
- queue_random_->Update(queue.random());
- queue_repeat_->Update(queue.repeat());
-}
-
-void Lua::react(const audio::PlaybackStarted& ev) {
- playback_playing_->Update(true);
-}
-
-void Lua::react(const audio::PlaybackUpdate& ev) {
- playback_track_->Update(*ev.track);
- playback_position_->Update(static_cast<int>(ev.seconds_elapsed));
-}
-
-void Lua::react(const audio::PlaybackFinished&) {
- playback_playing_->Update(false);
-}
-
-void Lua::react(const audio::VolumeChanged& ev) {
- volume_current_pct_->Update(static_cast<int>(ev.percent));
- volume_current_db_->Update(static_cast<int>(ev.db));
-}
-
void Lua::react(const internal::BackPressed& ev) {
PopLuaScreen(sLua->state());
}
-void Browse::entry() {}
-
-void Browse::react(const internal::ShowSettingsPage& ev) {
- std::shared_ptr<Screen> screen;
- std::shared_ptr<screens::Bluetooth> bt_screen;
- switch (ev.page) {
- case internal::ShowSettingsPage::Page::kRoot:
- screen.reset(new screens::Settings(sTopBarModel));
- break;
- case internal::ShowSettingsPage::Page::kBluetooth:
- bt_screen = std::make_shared<screens::Bluetooth>(
- sTopBarModel, sServices->bluetooth(), sServices->nvs());
- screen = bt_screen;
- bluetooth_screen_ = bt_screen;
- break;
- case internal::ShowSettingsPage::Page::kHeadphones:
- screen.reset(new screens::Headphones(sTopBarModel, sServices->nvs()));
- break;
- case internal::ShowSettingsPage::Page::kAppearance:
- screen.reset(
- new screens::Appearance(sTopBarModel, sServices->nvs(), *sDisplay));
- break;
- case internal::ShowSettingsPage::Page::kInput:
- screen.reset(new screens::InputMethod(sTopBarModel, sServices->nvs()));
- break;
- case internal::ShowSettingsPage::Page::kStorage:
- screen.reset(new screens::Storage(sTopBarModel));
- break;
- case internal::ShowSettingsPage::Page::kFirmwareUpdate:
- screen.reset(
- new screens::FirmwareUpdate(sTopBarModel, sServices->samd()));
- break;
- case internal::ShowSettingsPage::Page::kAbout:
- screen.reset(new screens::About(sTopBarModel));
- break;
- }
- if (screen) {
- PushScreen(screen);
- }
-}
-
-void Browse::react(const internal::BackPressed& ev) {
- if (PopScreen() == 0) {
- transit<Lua>();
- }
-}
-
-void Browse::react(const system_fsm::BluetoothDevicesChanged&) {
- auto bt = bluetooth_screen_.lock();
- if (bt) {
- bt->RefreshDevicesList();
- }
-}
-
-void Browse::react(const internal::ReindexDatabase& ev) {
- transit<Indexing>();
-}
-
-static std::shared_ptr<modals::Progress> sIndexProgress;
-
-void Indexing::entry() {
- sIndexProgress.reset(new modals::Progress(sCurrentScreen.get(), "Indexing",
- "Preparing database"));
- sCurrentModal = sIndexProgress;
- auto db = sServices->database().lock();
- if (!db) {
- // TODO: Hmm.
- return;
- }
- sServices->bg_worker().Dispatch<void>([=]() { db->updateIndexes(); });
-}
-
-void Indexing::exit() {
- sCurrentModal.reset();
- sIndexProgress.reset();
-}
-
-void Indexing::react(const database::event::UpdateStarted&) {}
-
-void Indexing::react(const database::event::UpdateProgress& ev) {
- std::ostringstream str;
- switch (ev.stage) {
- case database::event::UpdateProgress::Stage::kVerifyingExistingTracks:
- sIndexProgress->title("Verifying");
- str << "Tracks checked: " << ev.val;
- sIndexProgress->subtitle(str.str().c_str());
- break;
- case database::event::UpdateProgress::Stage::kScanningForNewTracks:
- sIndexProgress->title("Scanning");
- str << "Files checked: " << ev.val;
- sIndexProgress->subtitle(str.str().c_str());
- break;
- }
-}
-
-void Indexing::react(const database::event::UpdateFinished&) {
- transit<Browse>();
-}
-
} // namespace states
} // namespace ui
diff --git a/src/ui/widget_top_bar.cpp b/src/ui/widget_top_bar.cpp
deleted file mode 100644
index fbad5548..00000000
--- a/src/ui/widget_top_bar.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#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 "model_top_bar.hpp"
-#include "themes.hpp"
-#include "track.hpp"
-#include "ui_events.hpp"
-#include "ui_fsm.hpp"
-#include "widgets/lv_img.h"
-#include "widgets/lv_label.h"
-
-namespace ui {
-namespace widgets {
-
-static void back_click_cb(lv_event_t* ev) {
- events::Ui().Dispatch(internal::BackPressed{});
-}
-
-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);
- lv_obj_set_flex_align(container_, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER,
- LV_FLEX_ALIGN_END);
- lv_obj_set_style_pad_column(container_, 5, LV_PART_MAIN);
-
- if (config.show_back_button) {
- back_button_ = lv_btn_create(container_);
- lv_obj_set_size(back_button_, LV_SIZE_CONTENT, 12);
- lv_obj_t* button_icon = lv_label_create(back_button_);
- lv_label_set_text(button_icon, "<");
- lv_obj_add_event_cb(back_button_, back_click_cb, LV_EVENT_CLICKED, NULL);
- lv_obj_center(button_icon);
- } else {
- back_button_ = nullptr;
- }
-
- lv_obj_t* title_ = lv_label_create(container_);
- lv_obj_set_height(title_, 17);
- lv_obj_set_flex_grow(title_, 1);
-
- lv_label_set_text(title_, config.title.c_str());
- lv_label_set_long_mode(title_, LV_LABEL_LONG_DOT);
-
- themes::Theme::instance()->ApplyStyle(container_, themes::Style::kTopBar);
-}
-
-} // namespace widgets
-} // namespace ui