diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-11-24 15:13:10 +1100 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-11-24 15:13:10 +1100 |
| commit | 7c6eb2997cbba350e7384151e13659271508e08f (patch) | |
| tree | b6f95a4843521e69b24cbf4c126d84442d19fc23 /src/ui/screen_track_browser.cpp | |
| parent | 230721cd6271f3239b42e1d2471f8db15bebd712 (diff) | |
| download | tangara-fw-7c6eb2997cbba350e7384151e13659271508e08f.tar.gz | |
Migrate 'now playing' screen to lua
Diffstat (limited to 'src/ui/screen_track_browser.cpp')
| -rw-r--r-- | src/ui/screen_track_browser.cpp | 431 |
1 files changed, 0 insertions, 431 deletions
diff --git a/src/ui/screen_track_browser.cpp b/src/ui/screen_track_browser.cpp deleted file mode 100644 index c7b035ad..00000000 --- a/src/ui/screen_track_browser.cpp +++ /dev/null @@ -1,431 +0,0 @@ -/* - * Copyright 2023 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include <algorithm> -#include <memory> - -#include "core/lv_obj.h" -#include "core/lv_obj_scroll.h" -#include "core/lv_obj_tree.h" -#include "database.hpp" -#include "event_queue.hpp" -#include "extra/layouts/flex/lv_flex.h" -#include "font/lv_symbol_def.h" -#include "lvgl.h" -#include "misc/lv_anim.h" -#include "misc/lv_color.h" -#include "model_top_bar.hpp" - -#include "core/lv_event.h" -#include "esp_log.h" - -#include "core/lv_group.h" -#include "core/lv_obj_pos.h" -#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 "misc/lv_area.h" -#include "screen_track_browser.hpp" -#include "source.hpp" -#include "themes.hpp" -#include "track_queue.hpp" -#include "ui_events.hpp" -#include "ui_fsm.hpp" -#include "widget_top_bar.hpp" -#include "widgets/lv_label.h" - -[[maybe_unused]] static constexpr char kTag[] = "browser"; - -static constexpr int kMaxPages = 4; -static constexpr int kPageBuffer = 6; - -namespace ui { -namespace screens { - -static void item_click_cb(lv_event_t* ev) { - if (ev->user_data == NULL) { - return; - } - TrackBrowser* instance = reinterpret_cast<TrackBrowser*>(ev->user_data); - instance->OnItemClicked(ev); -} - -static void item_select_cb(lv_event_t* ev) { - if (ev->user_data == NULL) { - return; - } - TrackBrowser* instance = reinterpret_cast<TrackBrowser*>(ev->user_data); - instance->OnItemSelected(ev); -} - -TrackBrowser::TrackBrowser( - models::TopBar& top_bar_model, - audio::TrackQueue& queue, - std::weak_ptr<database::Database> db, - const std::pmr::vector<std::pmr::string>& crumbs, - std::future<database::Result<database::IndexRecord>*>&& initial_page) - : queue_(queue), - db_(db), - play_button_(nullptr), - enqueue_button_(nullptr), - list_(nullptr), - loading_indicator_(nullptr), - breadcrumbs_(crumbs), - loading_pos_(END), - loading_page_(move(initial_page)), - initial_page_(), - current_pages_() { - 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_CENTER, LV_FLEX_ALIGN_CENTER, - LV_FLEX_ALIGN_CENTER); - - widgets::TopBar::Configuration config{ - .show_back_button = true, - .title = breadcrumbs_[0], - }; - auto top_bar = CreateTopBar(content_, config, top_bar_model); - back_button_ = top_bar->button(); - - lv_obj_t* scrollable = lv_obj_create(content_); - lv_obj_set_width(scrollable, lv_pct(100)); - lv_obj_set_flex_grow(scrollable, 1); - lv_obj_set_layout(scrollable, LV_LAYOUT_FLEX); - lv_obj_set_flex_flow(scrollable, LV_FLEX_FLOW_COLUMN); - lv_obj_set_flex_align(scrollable, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, - LV_FLEX_ALIGN_START); - - if (crumbs.size() > 1) { - lv_obj_t* header = lv_obj_create(scrollable); - lv_obj_set_size(header, lv_pct(100), LV_SIZE_CONTENT); - lv_obj_set_layout(header, LV_LAYOUT_FLEX); - lv_obj_set_flex_flow(header, LV_FLEX_FLOW_COLUMN); - lv_obj_set_flex_align(header, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, - LV_FLEX_ALIGN_START); - - lv_obj_set_style_pad_left(header, 4, LV_PART_MAIN); - lv_obj_set_style_pad_right(header, 4, LV_PART_MAIN); - - lv_obj_t* spacer = lv_obj_create(header); - lv_obj_set_size(spacer, 1, 2); - - for (size_t i = 1; i < crumbs.size(); i++) { - lv_obj_t* crumb = lv_label_create(header); - lv_label_set_text(crumb, crumbs[i].c_str()); - - spacer = lv_obj_create(header); - lv_obj_set_size(spacer, 1, 2); - } - - spacer = lv_obj_create(header); - lv_obj_set_size(spacer, 1, 2); - - lv_obj_t* buttons_container = lv_obj_create(header); - lv_obj_set_width(buttons_container, lv_pct(100)); - lv_obj_set_height(buttons_container, LV_SIZE_CONTENT); - lv_obj_set_layout(buttons_container, LV_LAYOUT_FLEX); - lv_obj_set_flex_flow(buttons_container, LV_FLEX_FLOW_ROW); - lv_obj_set_flex_align(buttons_container, LV_FLEX_ALIGN_END, - LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - - lv_obj_t* label; - - play_button_ = lv_btn_create(buttons_container); - label = lv_label_create(play_button_); - lv_label_set_text(label, "Play all"); - lv_group_add_obj(group_, play_button_); - themes::Theme::instance()->ApplyStyle(play_button_, - themes::Style::kButtonPrimary); - - lv_bind(play_button_, LV_EVENT_CLICKED, [&](lv_obj_t*) { - if (!initial_page_) { - return; - } - queue_.Clear(); - queue_.IncludeNext(playlist::CreateSourceFromResults(db_, initial_page_)); - events::Ui().Dispatch(internal::ShowNowPlaying{}); - }); - - if (queue_.GetCurrent()) { - spacer = lv_obj_create(buttons_container); - lv_obj_set_size(spacer, 4, 1); - - enqueue_button_ = lv_btn_create(buttons_container); - label = lv_label_create(enqueue_button_); - lv_label_set_text(label, "Enqueue"); - lv_group_add_obj(group_, enqueue_button_); - themes::Theme::instance()->ApplyStyle(enqueue_button_, - themes::Style::kButtonPrimary); - - lv_bind(enqueue_button_, LV_EVENT_CLICKED, [&](lv_obj_t*) { - if (!initial_page_) { - return; - } - queue_.IncludeNext( - playlist::CreateSourceFromResults(db_, initial_page_)); - }); - } - - lv_obj_set_style_border_width(header, 1, LV_PART_MAIN); - lv_obj_set_style_border_color(header, lv_color_black(), LV_PART_MAIN); - lv_obj_set_style_border_side(header, LV_BORDER_SIDE_BOTTOM, LV_PART_MAIN); - - spacer = lv_obj_create(header); - lv_obj_set_size(spacer, 1, 4); - - lv_obj_set_style_border_width(header, 1, LV_PART_MAIN); - lv_obj_set_style_border_color( - header, lv_palette_lighten(LV_PALETTE_GREY, 3), LV_PART_MAIN); - } - - list_ = lv_list_create(scrollable); - lv_obj_set_size(list_, lv_pct(100), LV_SIZE_CONTENT); - - // The default scrollbar is deceptive because we load in items progressively. - // TODO/FIXME: this doesn't actually turn off the scrollbar, it seems. - lv_obj_set_scrollbar_mode(content_, LV_SCROLLBAR_MODE_OFF); - // Wrapping behaves in surprising ways, again due to progressing loading. - lv_group_set_wrap(group_, false); -} - -auto TrackBrowser::Tick() -> void { - if (!loading_page_) { - return; - } - if (!loading_page_->valid()) { - // TODO(jacqueline): error case. - return; - } - if (loading_page_->wait_for(std::chrono::seconds(0)) == - std::future_status::ready) { - std::shared_ptr<database::Result<database::IndexRecord>> result{ - loading_page_->get()}; - AddResults(loading_pos_.value_or(END), result); - - loading_page_.reset(); - loading_pos_.reset(); - } -} - -auto TrackBrowser::OnItemSelected(lv_event_t* ev) -> void { - auto index = GetItemIndex(lv_event_get_target(ev)); - if (!index) { - return; - } - if (index < kPageBuffer) { - FetchNewPage(START); - return; - } - if (index > GetNumRecords() - kPageBuffer) { - FetchNewPage(END); - return; - } -} - -auto TrackBrowser::OnItemClicked(lv_event_t* ev) -> void { - auto res = GetItemIndex(lv_event_get_target(ev)); - if (!res) { - return; - } - - auto index = *res; - for (const auto& page : current_pages_) { - for (std::size_t i = 0; i < page->values().size(); i++) { - if (index == 0) { - auto text = page->values()[i]->text(); - auto crumbs = breadcrumbs_; - crumbs.push_back(text.value()); - events::Ui().Dispatch(internal::RecordSelected{ - .show_menu = ev->code == LV_EVENT_LONG_PRESSED, - .new_crumbs = crumbs, - .initial_page = initial_page_, - .page = page, - .record = i, - }); - return; - } - index--; - } - } -} - -auto TrackBrowser::AddLoadingIndictor(Position pos) -> void { - if (loading_indicator_) { - return; - } - loading_indicator_ = lv_list_add_text(list_, "Loading..."); - if (pos == START) { - lv_obj_move_to_index(loading_indicator_, 0); - } -} - -auto TrackBrowser::AddResults( - Position pos, - std::shared_ptr<database::Result<database::IndexRecord>> results) -> void { - if (loading_indicator_ != nullptr) { - lv_obj_del(loading_indicator_); - loading_indicator_ = nullptr; - } - - if (initial_page_ == nullptr) { - initial_page_ = results; - } - - auto fn = [&](const std::shared_ptr<database::IndexRecord>& record) { - auto text = record->text(); - if (!text) { - // TODO(jacqueline): Display category-specific text. - text = "[ no data ]"; - } - lv_obj_t* item = lv_list_add_btn(list_, NULL, text->c_str()); - lv_label_set_long_mode(lv_obj_get_child(item, -1), LV_LABEL_LONG_DOT); - lv_obj_add_event_cb(item, item_click_cb, LV_EVENT_CLICKED, this); - lv_obj_add_event_cb(item, item_click_cb, LV_EVENT_LONG_PRESSED, this); - lv_obj_add_event_cb(item, item_select_cb, LV_EVENT_FOCUSED, this); - - if (pos == START) { - lv_obj_move_to_index(item, 0); - } - }; - - lv_obj_t* focused = lv_group_get_focused(group_); - - // Adding objects at the start of the list will artificially scroll the list - // up. Scroll it down by the height we're adding so that the user doesn't - // notice any jank. - if (pos == START) { - int num_to_add = results->values().size(); - // Assuming that all items are the same height, this item's y pos should be - // exactly the height of the new items. - lv_obj_t* representative_item = lv_obj_get_child(list_, num_to_add); - if (representative_item != nullptr) { - int scroll_adjustment = lv_obj_get_y(representative_item); - lv_obj_scroll_by(list_, 0, -scroll_adjustment, LV_ANIM_OFF); - } - } - - switch (pos) { - case START: - std::for_each(results->values().rbegin(), results->values().rend(), fn); - current_pages_.push_front(results); - break; - case END: - std::for_each(results->values().begin(), results->values().end(), fn); - current_pages_.push_back(results); - break; - } - - lv_group_remove_all_objs(group_); - lv_group_add_obj(group_, back_button_); - if (play_button_) { - lv_group_add_obj(group_, play_button_); - } - if (enqueue_button_) { - lv_group_add_obj(group_, enqueue_button_); - } - int num_children = lv_obj_get_child_cnt(list_); - for (int i = 0; i < num_children; i++) { - lv_group_add_obj(group_, lv_obj_get_child(list_, i)); - } - lv_group_focus_obj(focused); -} - -auto TrackBrowser::DropPage(Position pos) -> void { - if (pos == START) { - // Removing objects from the start of the list will artificially scroll the - // list down. Scroll it up by the height we're removing so that the user - // doesn't notice any jank. - int num_to_remove = current_pages_.front()->values().size(); - lv_obj_t* new_top_obj = lv_obj_get_child(list_, num_to_remove); - if (new_top_obj != nullptr) { - int scroll_adjustment = lv_obj_get_y(new_top_obj); - lv_obj_scroll_by(list_, 0, scroll_adjustment, LV_ANIM_OFF); - } - - for (int i = 0; i < current_pages_.front()->values().size(); i++) { - lv_obj_t* item = lv_obj_get_child(list_, 0); - if (item == NULL) { - continue; - } - lv_obj_del(item); - } - current_pages_.pop_front(); - } else if (pos == END) { - for (int i = 0; i < current_pages_.back()->values().size(); i++) { - lv_obj_t* item = lv_obj_get_child(list_, lv_obj_get_child_cnt(list_) - 1); - if (item == NULL) { - continue; - } - lv_group_remove_obj(item); - lv_obj_del(item); - } - current_pages_.pop_back(); - } -} - -auto TrackBrowser::FetchNewPage(Position pos) -> void { - if (loading_page_) { - return; - } - - std::optional<database::Continuation> cont; - switch (pos) { - case START: - cont = current_pages_.front()->prev_page(); - break; - case END: - cont = current_pages_.back()->next_page(); - break; - } - if (!cont) { - return; - } - - auto db = db_.lock(); - if (!db) { - return; - } - - // If we already have a complete set of pages, drop the page that's furthest - // away. - if (current_pages_.size() >= kMaxPages) { - switch (pos) { - case START: - DropPage(END); - break; - case END: - DropPage(START); - break; - } - } - - loading_pos_ = pos; - loading_page_ = db->GetPage<database::IndexRecord>(&cont.value()); -} - -auto TrackBrowser::GetNumRecords() -> std::size_t { - return lv_obj_get_child_cnt(list_) - (loading_indicator_ != nullptr ? 1 : 0); -} - -auto TrackBrowser::GetItemIndex(lv_obj_t* obj) -> std::optional<std::size_t> { - std::size_t child_count = lv_obj_get_child_cnt(list_); - std::size_t index = 0; - for (int i = 0; i < child_count; i++) { - lv_obj_t* child = lv_obj_get_child(list_, i); - if (child == loading_indicator_) { - continue; - } - if (child == obj) { - return index; - } - index++; - } - return {}; -} - -} // namespace screens -} // namespace ui |
