diff options
Diffstat (limited to 'src/ui')
| -rw-r--r-- | src/ui/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/ui/event_binding.cpp | 23 | ||||
| -rw-r--r-- | src/ui/include/event_binding.hpp | 30 | ||||
| -rw-r--r-- | src/ui/include/model_playback.hpp | 26 | ||||
| -rw-r--r-- | src/ui/include/screen.hpp | 14 | ||||
| -rw-r--r-- | src/ui/include/screen_playing.hpp | 33 | ||||
| -rw-r--r-- | src/ui/include/ui_fsm.hpp | 23 | ||||
| -rw-r--r-- | src/ui/screen_playing.cpp | 214 | ||||
| -rw-r--r-- | src/ui/screen_track_browser.cpp | 4 | ||||
| -rw-r--r-- | src/ui/ui_fsm.cpp | 63 |
10 files changed, 264 insertions, 169 deletions
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 906e9e1f..e331d96f 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -7,10 +7,11 @@ idf_component_register( "wheel_encoder.cpp" "screen_track_browser.cpp" "screen_playing.cpp" "themes.cpp" "widget_top_bar.cpp" "screen.cpp" "screen_onboarding.cpp" "modal_progress.cpp" "modal.cpp" "modal_confirm.cpp" "screen_settings.cpp" + "event_binding.cpp" "splash.c" "font_fusion.c" "font_symbols.c" "icons/battery_empty.c" "icons/battery_full.c" "icons/battery_20.c" "icons/battery_40.c" "icons/battery_60.c" "icons/battery_80.c" "icons/play.c" "icons/pause.c" "icons/bluetooth.c" INCLUDE_DIRS "include" - REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer" "battery") + REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer" "battery" "bindey") target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) diff --git a/src/ui/event_binding.cpp b/src/ui/event_binding.cpp new file mode 100644 index 00000000..ed15ccfb --- /dev/null +++ b/src/ui/event_binding.cpp @@ -0,0 +1,23 @@ +/* + * 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 new file mode 100644 index 00000000..19514db4 --- /dev/null +++ b/src/ui/include/event_binding.hpp @@ -0,0 +1,30 @@ +/* + * 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/model_playback.hpp b/src/ui/include/model_playback.hpp new file mode 100644 index 00000000..f932dcfd --- /dev/null +++ b/src/ui/include/model_playback.hpp @@ -0,0 +1,26 @@ +/* + * 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/screen.hpp b/src/ui/include/screen.hpp index 76251a72..ac7b19f8 100644 --- a/src/ui/include/screen.hpp +++ b/src/ui/include/screen.hpp @@ -8,11 +8,15 @@ #include <memory> #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 "nod/nod.hpp" #include "widget_top_bar.hpp" namespace ui { @@ -51,6 +55,16 @@ class Screen { auto CreateTopBar(lv_obj_t* parent, const widgets::TopBar::Configuration&) -> 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_; diff --git a/src/ui/include/screen_playing.hpp b/src/ui/include/screen_playing.hpp index 2e29130c..fff9cc35 100644 --- a/src/ui/include/screen_playing.hpp +++ b/src/ui/include/screen_playing.hpp @@ -11,10 +11,13 @@ #include <memory> #include <vector> +#include "bindey/property.h" +#include "esp_log.h" #include "lvgl.h" #include "database.hpp" #include "future_fetcher.hpp" +#include "model_playback.hpp" #include "screen.hpp" #include "track.hpp" #include "track_queue.hpp" @@ -28,48 +31,36 @@ namespace screens { */ class Playing : public Screen { public: - explicit Playing(std::weak_ptr<database::Database> db, + explicit Playing(models::Playback& playback_model, + std::weak_ptr<database::Database> db, audio::TrackQueue& queue); ~Playing(); auto Tick() -> void override; - // Callbacks invoked by the UI state machine in response to audio events. - - auto OnTrackUpdate() -> void; - auto OnPlaybackUpdate(uint32_t, uint32_t) -> void; - auto OnQueueUpdate() -> void; - auto OnFocusAboveFold() -> void; auto OnFocusBelowFold() -> void; + Playing(const Playing&) = delete; + Playing& operator=(const Playing&) = delete; + private: auto control_button(lv_obj_t* parent, char* icon) -> lv_obj_t*; auto next_up_label(lv_obj_t* parent, const std::pmr::string& text) -> lv_obj_t*; - auto BindTrack(const database::Track& track) -> void; - auto ApplyNextUp(const std::vector<database::Track>& tracks) -> void; - std::weak_ptr<database::Database> db_; audio::TrackQueue& queue_; - std::optional<database::Track> track_; - std::vector<database::Track> next_tracks_; + bindey::property<std::shared_ptr<database::Track>> current_track_; + bindey::property<std::vector<std::shared_ptr<database::Track>>> next_tracks_; - std::unique_ptr<database::FutureFetcher<std::optional<database::Track>>> + std::unique_ptr<database::FutureFetcher<std::shared_ptr<database::Track>>> new_track_; std::unique_ptr< - database::FutureFetcher<std::vector<std::optional<database::Track>>>> + database::FutureFetcher<std::vector<std::shared_ptr<database::Track>>>> new_next_tracks_; - lv_obj_t* artist_label_; - lv_obj_t* album_label_; - lv_obj_t* title_label_; - - lv_obj_t* scrubber_; - lv_obj_t* play_pause_control_; - lv_obj_t* next_up_header_; lv_obj_t* next_up_label_; lv_obj_t* next_up_hint_; diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index 9980dac6..cb3e651c 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -7,13 +7,16 @@ #pragma once #include <stdint.h> +#include <sys/_stdint.h> #include <memory> #include <stack> #include "audio_events.hpp" #include "battery.hpp" +#include "bindey/property.h" #include "gpios.hpp" #include "lvgl_task.hpp" +#include "model_playback.hpp" #include "nvs.hpp" #include "relative_wheel.hpp" #include "screen_playing.hpp" @@ -27,6 +30,7 @@ #include "storage.hpp" #include "system_events.hpp" #include "touchwheel.hpp" +#include "track.hpp" #include "track_queue.hpp" #include "ui_events.hpp" #include "wheel_encoder.hpp" @@ -49,11 +53,11 @@ class UiState : public tinyfsm::Fsm<UiState> { /* Fallback event handler. Does nothing. */ void react(const tinyfsm::Event& ev) {} - virtual void react(const system_fsm::BatteryStateChanged&); - virtual void react(const audio::PlaybackStarted&); - virtual void react(const audio::PlaybackFinished&); - virtual void react(const audio::PlaybackUpdate&) {} - virtual void react(const audio::QueueUpdate&) {} + void react(const system_fsm::BatteryStateChanged&); + void react(const audio::PlaybackStarted&); + void react(const audio::PlaybackFinished&); + void react(const audio::PlaybackUpdate&); + void react(const audio::QueueUpdate&); virtual void react(const system_fsm::KeyLockChanged&); @@ -88,6 +92,10 @@ class UiState : public tinyfsm::Fsm<UiState> { static std::stack<std::shared_ptr<Screen>> sScreens; static std::shared_ptr<Screen> sCurrentScreen; static std::shared_ptr<Modal> sCurrentModal; + + static models::Playback sPlaybackModel; + + static bindey::property<battery::Battery::BatteryState> sPropBatteryState; }; namespace states { @@ -96,7 +104,6 @@ class Splash : public UiState { public: void exit() override; void react(const system_fsm::BootComplete&) override; - void react(const system_fsm::BatteryStateChanged&) override{}; using UiState::react; }; @@ -140,10 +147,6 @@ class Playing : public UiState { void react(const internal::BackPressed&) override; - void react(const audio::PlaybackStarted&) override; - void react(const audio::PlaybackUpdate&) override; - void react(const audio::PlaybackFinished&) override; - void react(const audio::QueueUpdate&) override; using UiState::react; }; diff --git a/src/ui/screen_playing.cpp b/src/ui/screen_playing.cpp index bd55924d..547bcf98 100644 --- a/src/ui/screen_playing.cpp +++ b/src/ui/screen_playing.cpp @@ -9,6 +9,7 @@ #include <memory> #include "audio_events.hpp" +#include "bindey/binding.h" #include "core/lv_event.h" #include "core/lv_obj.h" #include "core/lv_obj_scroll.h" @@ -35,6 +36,7 @@ #include "misc/lv_area.h" #include "misc/lv_color.h" #include "misc/lv_txt.h" +#include "model_playback.hpp" #include "track.hpp" #include "ui_events.hpp" #include "ui_fsm.hpp" @@ -46,8 +48,6 @@ namespace ui { namespace screens { -static constexpr std::size_t kMaxUpcoming = 10; - static void above_fold_focus_cb(lv_event_t* ev) { if (ev->user_data == NULL) { return; @@ -64,10 +64,6 @@ static void below_fold_focus_cb(lv_event_t* ev) { instance->OnFocusBelowFold(); } -static void play_pause_cb(lv_event_t* ev) { - events::Audio().Dispatch(audio::TogglePlayPause{}); -} - static lv_style_t scrubber_style; auto info_label(lv_obj_t* parent) -> lv_obj_t* { @@ -105,13 +101,42 @@ auto Playing::next_up_label(lv_obj_t* parent, const std::pmr::string& text) return button; } -Playing::Playing(std::weak_ptr<database::Database> db, audio::TrackQueue& queue) +Playing::Playing(models::Playback& playback_model, + std::weak_ptr<database::Database> db, + audio::TrackQueue& queue) : db_(db), queue_(queue), - track_(), + current_track_(), next_tracks_(), new_track_(), new_next_tracks_() { + data_bindings_.emplace_back(playback_model.current_track.onChangedAndNow( + [=, this](const std::optional<database::TrackId>& id) { + if (!id) { + return; + } + if (current_track_.get() && current_track_.get()->data().id() == *id) { + return; + } + auto db = db_.lock(); + if (!db) { + return; + } + new_track_.reset( + new database::FutureFetcher<std::shared_ptr<database::Track>>( + db->GetTrack(*id))); + })); + data_bindings_.emplace_back(playback_model.upcoming_tracks.onChangedAndNow( + [=, this](const std::vector<database::TrackId>& ids) { + auto db = db_.lock(); + if (!db) { + return; + } + new_next_tracks_.reset(new database::FutureFetcher< + std::vector<std::shared_ptr<database::Track>>>( + db->GetBulkTracks(ids))); + })); + lv_obj_set_layout(content_, LV_LAYOUT_FLEX); lv_group_set_wrap(group_, false); @@ -143,20 +168,40 @@ Playing::Playing(std::weak_ptr<database::Database> db, audio::TrackQueue& queue) lv_obj_set_flex_align(info_container, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - artist_label_ = info_label(info_container); - album_label_ = info_label(info_container); - title_label_ = info_label(info_container); + lv_obj_t* artist_label = info_label(info_container); + lv_obj_t* album_label = info_label(info_container); + lv_obj_t* title_label = info_label(info_container); - scrubber_ = lv_slider_create(above_fold_container); - lv_obj_set_size(scrubber_, lv_pct(100), 5); - lv_slider_set_range(scrubber_, 0, 100); - lv_slider_set_value(scrubber_, 0, LV_ANIM_OFF); + data_bindings_.emplace_back(current_track_.onChangedAndNow( + [=](const std::shared_ptr<database::Track>& t) { + if (!t) { + return; + } + lv_label_set_text( + artist_label, + t->tags().at(database::Tag::kArtist).value_or("").c_str()); + lv_label_set_text( + album_label, + t->tags().at(database::Tag::kAlbum).value_or("").c_str()); + lv_label_set_text(title_label, t->TitleOrFilename().c_str()); + })); + + lv_obj_t* scrubber = lv_slider_create(above_fold_container); + lv_obj_set_size(scrubber, lv_pct(100), 5); lv_style_init(&scrubber_style); lv_style_set_bg_color(&scrubber_style, lv_color_black()); - lv_obj_add_style(scrubber_, &scrubber_style, LV_PART_INDICATOR); + lv_obj_add_style(scrubber, &scrubber_style, LV_PART_INDICATOR); - lv_group_add_obj(group_, scrubber_); + lv_group_add_obj(group_, scrubber); + + data_bindings_.emplace_back( + playback_model.current_track_duration.onChangedAndNow([=](uint32_t d) { + lv_slider_set_range(scrubber, 0, std::max<uint32_t>(1, d)); + })); + data_bindings_.emplace_back( + playback_model.current_track_position.onChangedAndNow( + [=](uint32_t p) { lv_slider_set_value(scrubber, p, LV_ANIM_OFF); })); lv_obj_t* controls_container = lv_obj_create(above_fold_container); lv_obj_set_size(controls_container, lv_pct(100), 20); @@ -164,15 +209,25 @@ Playing::Playing(std::weak_ptr<database::Database> db, audio::TrackQueue& queue) lv_obj_set_flex_align(controls_container, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - play_pause_control_ = control_button(controls_container, LV_SYMBOL_PLAY); - lv_obj_add_event_cb(play_pause_control_, play_pause_cb, LV_EVENT_CLICKED, - NULL); - lv_group_add_obj(group_, play_pause_control_); + lv_obj_t* play_pause_control = + control_button(controls_container, LV_SYMBOL_PLAY); + lv_group_add_obj(group_, play_pause_control); + lv_bind(play_pause_control, LV_EVENT_CLICKED, [=](lv_obj_t*) { + events::Audio().Dispatch(audio::TogglePlayPause{}); + }); + + lv_obj_t* track_prev = control_button(controls_container, LV_SYMBOL_PREV); + lv_group_add_obj(group_, track_prev); + lv_bind(track_prev, LV_EVENT_CLICKED, [=](lv_obj_t*) { queue_.Previous(); }); + + lv_obj_t* track_next = control_button(controls_container, LV_SYMBOL_NEXT); + lv_group_add_obj(group_, track_next); + lv_bind(track_next, LV_EVENT_CLICKED, [=](lv_obj_t*) { queue_.Next(); }); + + lv_obj_t* shuffle = control_button(controls_container, LV_SYMBOL_SHUFFLE); + lv_group_add_obj(group_, shuffle); + // lv_bind(shuffle, LV_EVENT_CLICKED, [=](lv_obj_t*) { queue_ }); - lv_group_add_obj(group_, control_button(controls_container, LV_SYMBOL_PREV)); - lv_group_add_obj(group_, control_button(controls_container, LV_SYMBOL_NEXT)); - lv_group_add_obj(group_, - control_button(controls_container, LV_SYMBOL_SHUFFLE)); lv_group_add_obj(group_, control_button(controls_container, LV_SYMBOL_LOOP)); next_up_header_ = lv_obj_create(above_fold_container); @@ -198,111 +253,56 @@ Playing::Playing(std::weak_ptr<database::Database> db, audio::TrackQueue& queue) lv_obj_set_flex_align(next_up_container_, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - OnTrackUpdate(); - OnQueueUpdate(); -} - -Playing::~Playing() {} + data_bindings_.emplace_back(next_tracks_.onChangedAndNow( + [=](const std::vector<std::shared_ptr<database::Track>>& tracks) { + // TODO(jacqueline): Do a proper diff to maintain selection. + int children = lv_obj_get_child_cnt(next_up_container_); + while (children > 0) { + lv_obj_del(lv_obj_get_child(next_up_container_, 0)); + children--; + } -auto Playing::OnTrackUpdate() -> void { - auto current = queue_.GetCurrent(); - if (!current) { - return; - } - if (track_ && track_->data().id() == *current) { - return; - } - auto db = db_.lock(); - if (!db) { - return; - } - new_track_.reset(new database::FutureFetcher<std::optional<database::Track>>( - db->GetTrack(*current))); -} + if (tracks.empty()) { + lv_label_set_text(next_up_label_, "Nothing queued"); + lv_label_set_text(next_up_hint_, ""); + return; + } else { + lv_label_set_text(next_up_label_, "Next up"); + lv_label_set_text(next_up_hint_, ""); + } -auto Playing::OnPlaybackUpdate(uint32_t pos_seconds, uint32_t new_duration) - -> void { - if (!track_) { - return; - } - lv_slider_set_range(scrubber_, 0, new_duration); - lv_slider_set_value(scrubber_, pos_seconds, LV_ANIM_ON); + for (const auto& track : tracks) { + lv_group_add_obj(group_, next_up_label(next_up_container_, + track->TitleOrFilename())); + } + })); } -auto Playing::OnQueueUpdate() -> void { - OnTrackUpdate(); - auto current = queue_.GetUpcoming(kMaxUpcoming); - auto db = db_.lock(); - if (!db) { - return; - } - new_next_tracks_.reset( - new database::FutureFetcher<std::vector<std::optional<database::Track>>>( - db->GetBulkTracks(current))); -} +Playing::~Playing() {} auto Playing::Tick() -> void { if (new_track_ && new_track_->Finished()) { auto res = new_track_->Result(); new_track_.reset(); - if (res && *res) { - BindTrack(**res); + if (res) { + current_track_(*res); } } if (new_next_tracks_ && new_next_tracks_->Finished()) { auto res = new_next_tracks_->Result(); new_next_tracks_.reset(); if (res) { - std::vector<database::Track> filtered; + std::vector<std::shared_ptr<database::Track>> filtered; for (const auto& t : *res) { if (t) { - filtered.push_back(*t); + filtered.push_back(t); } } - ApplyNextUp(filtered); + next_tracks_.set(filtered); } } } -auto Playing::BindTrack(const database::Track& t) -> void { - track_ = t; - - lv_label_set_text(artist_label_, - t.tags().at(database::Tag::kArtist).value_or("").c_str()); - lv_label_set_text(album_label_, - t.tags().at(database::Tag::kAlbum).value_or("").c_str()); - lv_label_set_text(title_label_, t.TitleOrFilename().c_str()); - - std::optional<int> duration = t.tags().duration; - lv_slider_set_range(scrubber_, 0, duration.value_or(1)); - lv_slider_set_value(scrubber_, 0, LV_ANIM_OFF); -} - -auto Playing::ApplyNextUp(const std::vector<database::Track>& tracks) -> void { - // TODO(jacqueline): Do a proper diff to maintain selection. - int children = lv_obj_get_child_cnt(next_up_container_); - while (children > 0) { - lv_obj_del(lv_obj_get_child(next_up_container_, 0)); - children--; - } - - next_tracks_ = tracks; - - if (next_tracks_.empty()) { - lv_label_set_text(next_up_label_, "Nothing queued"); - lv_label_set_text(next_up_hint_, ""); - return; - } else { - lv_label_set_text(next_up_label_, "Next up"); - lv_label_set_text(next_up_hint_, ""); - } - - for (const auto& track : next_tracks_) { - lv_group_add_obj( - group_, next_up_label(next_up_container_, track.TitleOrFilename())); - } -} - auto Playing::OnFocusAboveFold() -> void { lv_obj_scroll_to_y(content_, 0, LV_ANIM_ON); } diff --git a/src/ui/screen_track_browser.cpp b/src/ui/screen_track_browser.cpp index 6cd92a04..8d1fe653 100644 --- a/src/ui/screen_track_browser.cpp +++ b/src/ui/screen_track_browser.cpp @@ -170,8 +170,8 @@ auto TrackBrowser::AddResults( initial_page_ = results; } - auto fn = [&](const database::IndexRecord& record) { - auto text = record.text(); + auto fn = [&](const std::shared_ptr<database::IndexRecord>& record) { + auto text = record->text(); if (!text) { // TODO(jacqueline): Display category-specific text. text = "[ no data ]"; diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index fa4939f3..d7bb9bb7 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -19,6 +19,7 @@ #include "gpios.hpp" #include "lvgl_task.hpp" #include "modal_confirm.hpp" +#include "model_playback.hpp" #include "nvs.hpp" #include "relative_wheel.hpp" #include "screen.hpp" @@ -52,6 +53,10 @@ std::stack<std::shared_ptr<Screen>> UiState::sScreens; std::shared_ptr<Screen> UiState::sCurrentScreen; std::shared_ptr<Modal> UiState::sCurrentModal; +models::Playback UiState::sPlaybackModel; + +bindey::property<battery::Battery::BatteryState> UiState::sPropBatteryState; + auto UiState::InitBootSplash(drivers::IGpios& gpios) -> bool { // Init LVGL first, since the display driver registers itself with LVGL. lv_init(); @@ -89,15 +94,33 @@ void UiState::react(const system_fsm::KeyLockChanged& ev) { } void UiState::react(const system_fsm::BatteryStateChanged&) { - UpdateTopBar(); + if (!sServices) { + return; + } + auto state = sServices->battery().State(); + if (state) { + sPropBatteryState.set(*state); + } } void UiState::react(const audio::PlaybackStarted&) { - UpdateTopBar(); + sPlaybackModel.is_playing.set(true); } void UiState::react(const audio::PlaybackFinished&) { - UpdateTopBar(); + sPlaybackModel.is_playing.set(false); +} + +void UiState::react(const audio::PlaybackUpdate& ev) { + sPlaybackModel.current_track_duration.set(ev.seconds_total); + sPlaybackModel.current_track_position.set(ev.seconds_elapsed); +} + +void UiState::react(const audio::QueueUpdate&) { + ESP_LOGI(kTag, "current changed!"); + auto& queue = sServices->track_queue(); + sPlaybackModel.current_track.set(queue.GetCurrent()); + sPlaybackModel.upcoming_tracks.set(queue.GetUpcoming(10)); } void UiState::UpdateTopBar() { @@ -283,21 +306,22 @@ void Browse::react(const internal::RecordSelected& ev) { } auto record = ev.page->values().at(ev.record); - if (record.track()) { - ESP_LOGI(kTag, "selected track '%s'", record.text()->c_str()); + if (record->track()) { + ESP_LOGI(kTag, "selected track '%s'", record->text()->c_str()); auto& queue = sServices->track_queue(); queue.Clear(); queue.IncludeLast(std::make_shared<playlist::IndexRecordSource>( sServices->database(), ev.initial_page, 0, ev.page, ev.record)); + ESP_LOGI(kTag, "transit to playing"); transit<Playing>(); } else { - ESP_LOGI(kTag, "selected record '%s'", record.text()->c_str()); - auto cont = record.Expand(kRecordsPerPage); + ESP_LOGI(kTag, "selected record '%s'", record->text()->c_str()); + auto cont = record->Expand(kRecordsPerPage); if (!cont) { return; } 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>( sServices->database(), title, std::move(query))); } @@ -329,8 +353,9 @@ void Browse::react(const system_fsm::BluetoothDevicesChanged&) { static std::shared_ptr<screens::Playing> sPlayingScreen; void Playing::entry() { - sPlayingScreen.reset( - new screens::Playing(sServices->database(), sServices->track_queue())); + ESP_LOGI(kTag, "push playing screen"); + sPlayingScreen.reset(new screens::Playing( + sPlaybackModel, sServices->database(), sServices->track_queue())); PushScreen(sPlayingScreen); } @@ -339,24 +364,6 @@ void Playing::exit() { PopScreen(); } -void Playing::react(const audio::PlaybackStarted& ev) { - UpdateTopBar(); - sPlayingScreen->OnTrackUpdate(); -} - -void Playing::react(const audio::PlaybackFinished& ev) { - UpdateTopBar(); - sPlayingScreen->OnTrackUpdate(); -} - -void Playing::react(const audio::PlaybackUpdate& ev) { - sPlayingScreen->OnPlaybackUpdate(ev.seconds_elapsed, ev.seconds_total); -} - -void Playing::react(const audio::QueueUpdate& ev) { - sPlayingScreen->OnQueueUpdate(); -} - void Playing::react(const internal::BackPressed& ev) { transit<Browse>(); } |
