Add modal dialog support
This commit is contained in:
@@ -59,7 +59,6 @@ TouchWheel::TouchWheel() {
|
||||
for (int i = 5; i < 12; i++) {
|
||||
WriteRegister(Register::KEY_CONTROL_BASE + i, 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TouchWheel::~TouchWheel() {}
|
||||
|
||||
@@ -6,6 +6,7 @@ idf_component_register(
|
||||
SRCS "lvgl_task.cpp" "ui_fsm.cpp" "screen_splash.cpp" "screen_menu.cpp"
|
||||
"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"
|
||||
"splash.c" "font_fusion.c" "font_symbols.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer")
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "core/lv_group.h"
|
||||
#include "core/lv_obj.h"
|
||||
#include "core/lv_obj_tree.h"
|
||||
#include "lvgl.h"
|
||||
#include "widget_top_bar.hpp"
|
||||
|
||||
#include "screen.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
class Modal {
|
||||
public:
|
||||
Modal(Screen* host);
|
||||
virtual ~Modal();
|
||||
|
||||
auto root() -> lv_obj_t* { return root_; }
|
||||
auto group() -> lv_group_t* { return group_; }
|
||||
|
||||
protected:
|
||||
lv_obj_t* const root_;
|
||||
lv_group_t* const group_;
|
||||
|
||||
private:
|
||||
Screen* host_;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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::string& title, bool has_cancel);
|
||||
|
||||
private:
|
||||
lv_obj_t* container_;
|
||||
};
|
||||
|
||||
} // namespace modals
|
||||
} // namespace ui
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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::string title);
|
||||
|
||||
private:
|
||||
lv_obj_t* container_;
|
||||
};
|
||||
|
||||
} // namespace modals
|
||||
} // namespace ui
|
||||
@@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "core/lv_group.h"
|
||||
#include "core/lv_obj.h"
|
||||
@@ -23,14 +24,8 @@ namespace ui {
|
||||
*/
|
||||
class Screen {
|
||||
public:
|
||||
Screen() : root_(lv_obj_create(NULL)), group_(lv_group_create()) {}
|
||||
|
||||
virtual ~Screen() {
|
||||
// The group *must* be deleted first. Otherwise, focus events will be
|
||||
// generated whilst deleting the object tree, which causes a big mess.
|
||||
lv_group_del(group_);
|
||||
lv_obj_del(root_);
|
||||
}
|
||||
Screen();
|
||||
virtual ~Screen();
|
||||
|
||||
/*
|
||||
* Called periodically to allow the screen to update itself, e.g. to handle
|
||||
@@ -41,14 +36,27 @@ class Screen {
|
||||
auto UpdateTopBar(const widgets::TopBar::State& state) -> void;
|
||||
|
||||
auto root() -> lv_obj_t* { return root_; }
|
||||
auto group() -> lv_group_t* { return group_; }
|
||||
auto content() -> lv_obj_t* { return content_; }
|
||||
|
||||
auto modal_content() -> lv_obj_t* { return modal_content_; }
|
||||
auto modal_group(lv_group_t* g) -> void { modal_group_ = g; }
|
||||
auto group() -> lv_group_t* {
|
||||
if (modal_group_) {
|
||||
return modal_group_;
|
||||
}
|
||||
return group_;
|
||||
}
|
||||
|
||||
protected:
|
||||
auto CreateTopBar(lv_obj_t* parent, const widgets::TopBar::Configuration&)
|
||||
-> widgets::TopBar*;
|
||||
|
||||
lv_obj_t* const root_;
|
||||
lv_obj_t* const content_;
|
||||
lv_obj_t* const modal_content_;
|
||||
|
||||
lv_group_t* const group_;
|
||||
lv_group_t* modal_group_;
|
||||
|
||||
private:
|
||||
std::unique_ptr<widgets::TopBar> top_bar_;
|
||||
|
||||
@@ -37,6 +37,9 @@ struct IndexSelected : tinyfsm::Event {
|
||||
|
||||
struct BackPressed : tinyfsm::Event {};
|
||||
|
||||
struct ModalConfirmPressed : tinyfsm::Event {};
|
||||
struct ModalCancelPressed : tinyfsm::Event {};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "tinyfsm.hpp"
|
||||
|
||||
#include "display.hpp"
|
||||
#include "modal.hpp"
|
||||
#include "screen.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "system_events.hpp"
|
||||
@@ -40,6 +41,8 @@ class UiState : public tinyfsm::Fsm<UiState> {
|
||||
/* Fallback event handler. Does nothing. */
|
||||
void react(const tinyfsm::Event& ev) {}
|
||||
|
||||
void react(const system_fsm::BatteryPercentChanged&);
|
||||
|
||||
virtual void react(const audio::PlaybackStarted&) {}
|
||||
virtual void react(const audio::PlaybackUpdate&) {}
|
||||
virtual void react(const audio::QueueUpdate&) {}
|
||||
@@ -49,6 +52,12 @@ class UiState : public tinyfsm::Fsm<UiState> {
|
||||
virtual void react(const internal::RecordSelected&) {}
|
||||
virtual void react(const internal::IndexSelected&) {}
|
||||
virtual void react(const internal::BackPressed&) {}
|
||||
virtual void react(const internal::ModalCancelPressed&) {
|
||||
sCurrentModal.reset();
|
||||
}
|
||||
virtual void react(const internal::ModalConfirmPressed&) {
|
||||
sCurrentModal.reset();
|
||||
}
|
||||
|
||||
virtual void react(const system_fsm::DisplayReady&) {}
|
||||
virtual void react(const system_fsm::BootComplete&) {}
|
||||
@@ -57,6 +66,7 @@ class UiState : public tinyfsm::Fsm<UiState> {
|
||||
protected:
|
||||
void PushScreen(std::shared_ptr<Screen>);
|
||||
void PopScreen();
|
||||
void UpdateTopBar();
|
||||
|
||||
static drivers::IGpios* sIGpios;
|
||||
static audio::TrackQueue* sQueue;
|
||||
@@ -68,6 +78,7 @@ 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;
|
||||
};
|
||||
|
||||
namespace states {
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "misc/lv_color.h"
|
||||
#include "misc/lv_style.h"
|
||||
#include "misc/lv_timer.h"
|
||||
#include "modal.hpp"
|
||||
#include "relative_wheel.hpp"
|
||||
#include "tasks.hpp"
|
||||
#include "touchwheel.hpp"
|
||||
@@ -75,6 +76,7 @@ void LvglMain(std::weak_ptr<drivers::RelativeWheel> weak_touch_wheel,
|
||||
TouchWheelEncoder encoder(weak_touch_wheel);
|
||||
|
||||
std::shared_ptr<Screen> current_screen;
|
||||
lv_group_t* current_group = nullptr;
|
||||
auto* events = events::queues::Ui();
|
||||
while (1) {
|
||||
while (events->Service(0)) {
|
||||
@@ -88,6 +90,11 @@ void LvglMain(std::weak_ptr<drivers::RelativeWheel> weak_touch_wheel,
|
||||
current_screen = screen;
|
||||
}
|
||||
|
||||
if (current_screen->group() != current_group) {
|
||||
current_group = current_screen->group();
|
||||
lv_indev_set_group(encoder.registration(), current_group);
|
||||
}
|
||||
|
||||
if (current_screen) {
|
||||
current_screen->Tick();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
|
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
#include "misc/lv_color.h"
|
||||
#include "modal_progress.hpp"
|
||||
|
||||
#include "core/lv_event.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 "screen.hpp"
|
||||
#include "ui_events.hpp"
|
||||
#include "ui_fsm.hpp"
|
||||
#include "widget_top_bar.hpp"
|
||||
#include "widgets/lv_label.h"
|
||||
|
||||
namespace ui {
|
||||
|
||||
Modal::Modal(Screen* host)
|
||||
: root_(lv_obj_create(host->modal_content())),
|
||||
group_(lv_group_create()),
|
||||
host_(host) {
|
||||
lv_obj_set_style_bg_opa(host->modal_content(), LV_OPA_40, 0);
|
||||
lv_obj_set_style_bg_color(host->modal_content(), lv_color_black(), 0);
|
||||
|
||||
lv_obj_set_size(root_, 120, LV_SIZE_CONTENT);
|
||||
lv_obj_center(root_);
|
||||
|
||||
lv_obj_set_style_bg_opa(root_, LV_OPA_COVER, 0);
|
||||
lv_obj_set_style_bg_color(root_, lv_color_white(), 0);
|
||||
|
||||
host_->modal_group(group_);
|
||||
}
|
||||
|
||||
Modal::~Modal() {
|
||||
host_->modal_group(nullptr);
|
||||
lv_obj_set_style_bg_opa(host_->modal_content(), LV_OPA_TRANSP, 0);
|
||||
|
||||
// The group *must* be deleted first. Otherwise, focus events will be
|
||||
// generated whilst deleting the object tree, which causes a big mess.
|
||||
lv_group_del(group_);
|
||||
lv_obj_del(root_);
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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::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
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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::string title_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);
|
||||
|
||||
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* spinner = lv_spinner_create(root_, 3000, 45);
|
||||
lv_obj_set_size(spinner, 16, 16);
|
||||
}
|
||||
|
||||
} // namespace modals
|
||||
} // namespace ui
|
||||
@@ -8,10 +8,38 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "core/lv_obj_pos.h"
|
||||
#include "core/lv_obj_tree.h"
|
||||
#include "misc/lv_area.h"
|
||||
#include "misc/lv_color.h"
|
||||
#include "widget_top_bar.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
Screen::Screen()
|
||||
: root_(lv_obj_create(NULL)),
|
||||
content_(lv_obj_create(root_)),
|
||||
modal_content_(lv_obj_create(root_)),
|
||||
group_(lv_group_create()),
|
||||
modal_group_(nullptr) {
|
||||
lv_obj_set_size(root_, lv_pct(100), lv_pct(100));
|
||||
lv_obj_set_size(content_, lv_pct(100), lv_pct(100));
|
||||
lv_obj_set_size(modal_content_, lv_pct(100), lv_pct(100));
|
||||
lv_obj_center(root_);
|
||||
lv_obj_center(content_);
|
||||
lv_obj_center(modal_content_);
|
||||
|
||||
lv_obj_set_style_bg_opa(modal_content_, LV_OPA_TRANSP, 0);
|
||||
lv_obj_set_style_bg_color(modal_content_, lv_color_black(), 0);
|
||||
}
|
||||
|
||||
Screen::~Screen() {
|
||||
// The group *must* be deleted first. Otherwise, focus events will be
|
||||
// generated whilst deleting the object tree, which causes a big mess.
|
||||
lv_group_del(group_);
|
||||
lv_obj_del(root_);
|
||||
}
|
||||
|
||||
auto Screen::UpdateTopBar(const widgets::TopBar::State& state) -> void {
|
||||
if (top_bar_) {
|
||||
top_bar_->Update(state);
|
||||
|
||||
@@ -37,18 +37,18 @@ static void item_click_cb(lv_event_t* ev) {
|
||||
}
|
||||
|
||||
Menu::Menu(std::vector<database::IndexInfo> indexes) : indexes_(indexes) {
|
||||
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_CENTER, LV_FLEX_ALIGN_CENTER,
|
||||
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 = false,
|
||||
.title = "",
|
||||
};
|
||||
CreateTopBar(root_, config);
|
||||
CreateTopBar(content_, config);
|
||||
|
||||
lv_obj_t* list = lv_list_create(root_);
|
||||
lv_obj_t* list = lv_list_create(content_);
|
||||
lv_obj_set_size(list, lv_disp_get_hor_res(NULL), lv_disp_get_ver_res(NULL));
|
||||
lv_obj_center(list);
|
||||
|
||||
|
||||
+10
-10
@@ -106,17 +106,17 @@ Playing::Playing(std::weak_ptr<database::Database> db, audio::TrackQueue* queue)
|
||||
next_tracks_(),
|
||||
new_track_(),
|
||||
new_next_tracks_() {
|
||||
lv_obj_set_layout(root_, LV_LAYOUT_FLEX);
|
||||
lv_obj_set_layout(content_, LV_LAYOUT_FLEX);
|
||||
lv_group_set_wrap(group_, false);
|
||||
|
||||
lv_obj_set_size(root_, lv_pct(100), LV_SIZE_CONTENT);
|
||||
lv_obj_set_flex_flow(root_, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_flex_align(root_, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START,
|
||||
lv_obj_set_size(content_, lv_pct(100), LV_SIZE_CONTENT);
|
||||
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_scrollbar_mode(root_, LV_SCROLLBAR_MODE_OFF);
|
||||
lv_obj_set_scrollbar_mode(content_, LV_SCROLLBAR_MODE_OFF);
|
||||
|
||||
lv_obj_t* above_fold_container = lv_obj_create(root_);
|
||||
lv_obj_t* above_fold_container = lv_obj_create(content_);
|
||||
lv_obj_set_layout(above_fold_container, LV_LAYOUT_FLEX);
|
||||
lv_obj_set_size(above_fold_container, lv_pct(100), lv_disp_get_ver_res(NULL));
|
||||
lv_obj_set_flex_flow(above_fold_container, LV_FLEX_FLOW_COLUMN);
|
||||
@@ -182,7 +182,7 @@ Playing::Playing(std::weak_ptr<database::Database> db, audio::TrackQueue* queue)
|
||||
lv_obj_set_style_text_font(next_up_hint_, &font_symbols, 0);
|
||||
lv_obj_set_size(next_up_hint_, LV_SIZE_CONTENT, lv_pct(100));
|
||||
|
||||
next_up_container_ = lv_list_create(root_);
|
||||
next_up_container_ = lv_list_create(content_);
|
||||
lv_obj_set_layout(next_up_container_, LV_LAYOUT_FLEX);
|
||||
lv_obj_set_size(next_up_container_, lv_pct(100), lv_disp_get_ver_res(NULL));
|
||||
lv_obj_set_flex_flow(next_up_container_, LV_FLEX_FLOW_COLUMN);
|
||||
@@ -295,12 +295,12 @@ auto Playing::ApplyNextUp(const std::vector<database::Track>& tracks) -> void {
|
||||
}
|
||||
|
||||
auto Playing::OnFocusAboveFold() -> void {
|
||||
lv_obj_scroll_to_y(root_, 0, LV_ANIM_ON);
|
||||
lv_obj_scroll_to_y(content_, 0, LV_ANIM_ON);
|
||||
}
|
||||
|
||||
auto Playing::OnFocusBelowFold() -> void {
|
||||
if (lv_obj_get_scroll_y(root_) < lv_obj_get_y(next_up_header_)) {
|
||||
lv_obj_scroll_to_y(root_, lv_obj_get_y(next_up_header_), LV_ANIM_ON);
|
||||
if (lv_obj_get_scroll_y(content_) < lv_obj_get_y(next_up_header_)) {
|
||||
lv_obj_scroll_to_y(content_, lv_obj_get_y(next_up_header_), LV_ANIM_ON);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,14 +69,13 @@ TrackBrowser::TrackBrowser(
|
||||
loading_page_(move(initial_page)),
|
||||
initial_page_(),
|
||||
current_pages_() {
|
||||
lv_obj_set_layout(root_, LV_LAYOUT_FLEX);
|
||||
lv_obj_set_size(root_, lv_pct(100), lv_pct(100));
|
||||
lv_obj_set_flex_flow(root_, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_flex_align(root_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER,
|
||||
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);
|
||||
|
||||
// The default scrollbar is deceptive because we load in items progressively.
|
||||
lv_obj_set_scrollbar_mode(root_, LV_SCROLLBAR_MODE_OFF);
|
||||
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);
|
||||
|
||||
@@ -84,10 +83,10 @@ TrackBrowser::TrackBrowser(
|
||||
.show_back_button = true,
|
||||
.title = title,
|
||||
};
|
||||
auto top_bar = CreateTopBar(root_, config);
|
||||
auto top_bar = CreateTopBar(content_, config);
|
||||
back_button_ = top_bar->button();
|
||||
|
||||
list_ = lv_list_create(root_);
|
||||
list_ = lv_list_create(content_);
|
||||
lv_obj_set_width(list_, lv_pct(100));
|
||||
lv_obj_set_flex_grow(list_, 1);
|
||||
lv_obj_center(list_);
|
||||
|
||||
+25
-1
@@ -5,13 +5,18 @@
|
||||
*/
|
||||
|
||||
#include "ui_fsm.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include "audio_events.hpp"
|
||||
|
||||
#include "core/lv_obj.h"
|
||||
#include "misc/lv_gc.h"
|
||||
|
||||
#include "audio_events.hpp"
|
||||
#include "display.hpp"
|
||||
#include "event_queue.hpp"
|
||||
#include "gpios.hpp"
|
||||
#include "lvgl_task.hpp"
|
||||
#include "modal_confirm.hpp"
|
||||
#include "relative_wheel.hpp"
|
||||
#include "screen.hpp"
|
||||
#include "screen_menu.hpp"
|
||||
@@ -23,6 +28,7 @@
|
||||
#include "touchwheel.hpp"
|
||||
#include "track_queue.hpp"
|
||||
#include "ui_events.hpp"
|
||||
#include "widget_top_bar.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -40,6 +46,7 @@ std::weak_ptr<database::Database> UiState::sDb;
|
||||
|
||||
std::stack<std::shared_ptr<Screen>> UiState::sScreens;
|
||||
std::shared_ptr<Screen> UiState::sCurrentScreen;
|
||||
std::shared_ptr<Modal> UiState::sCurrentModal;
|
||||
|
||||
auto UiState::Init(drivers::IGpios* gpio_expander, audio::TrackQueue* queue)
|
||||
-> bool {
|
||||
@@ -75,6 +82,7 @@ void UiState::PushScreen(std::shared_ptr<Screen> screen) {
|
||||
sScreens.push(sCurrentScreen);
|
||||
}
|
||||
sCurrentScreen = screen;
|
||||
UpdateTopBar();
|
||||
}
|
||||
|
||||
void UiState::PopScreen() {
|
||||
@@ -83,12 +91,28 @@ void UiState::PopScreen() {
|
||||
}
|
||||
sCurrentScreen = sScreens.top();
|
||||
sScreens.pop();
|
||||
UpdateTopBar();
|
||||
}
|
||||
|
||||
void UiState::react(const system_fsm::KeyLockChanged& ev) {
|
||||
sDisplay->SetDisplayOn(ev.falling);
|
||||
}
|
||||
|
||||
void UiState::react(const system_fsm::BatteryPercentChanged&) {
|
||||
UpdateTopBar();
|
||||
}
|
||||
|
||||
void UiState::UpdateTopBar() {
|
||||
widgets::TopBar::State state{
|
||||
.playback_state = widgets::TopBar::PlaybackState::kIdle,
|
||||
.battery_percent = 50,
|
||||
.is_charging = true,
|
||||
};
|
||||
if (sCurrentScreen) {
|
||||
sCurrentScreen->UpdateTopBar(state);
|
||||
}
|
||||
}
|
||||
|
||||
namespace states {
|
||||
|
||||
void Splash::exit() {
|
||||
|
||||
Reference in New Issue
Block a user