Add placeholder settings UI

custom
jacqueline 2 years ago
parent 3b3bc64d19
commit 795f268737
  1. 2
      src/ui/CMakeLists.txt
  2. 9
      src/ui/include/screen.hpp
  3. 3
      src/ui/include/screen_menu.hpp
  4. 70
      src/ui/include/screen_settings.hpp
  5. 5
      src/ui/include/ui_events.hpp
  6. 5
      src/ui/include/ui_fsm.hpp
  7. 1
      src/ui/modal.cpp
  8. 24
      src/ui/screen.cpp
  9. 40
      src/ui/screen_menu.cpp
  10. 2
      src/ui/screen_playing.cpp
  11. 214
      src/ui/screen_settings.cpp
  12. 9
      src/ui/ui_fsm.cpp
  13. 2
      src/ui/widget_top_bar.cpp

@ -6,7 +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"
"modal_progress.cpp" "modal.cpp" "modal_confirm.cpp" "screen_settings.cpp"
"splash.c" "font_fusion.c" "font_symbols.c"
INCLUDE_DIRS "include"
REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer")

@ -52,8 +52,8 @@ class Screen {
-> widgets::TopBar*;
lv_obj_t* const root_;
lv_obj_t* const content_;
lv_obj_t* const modal_content_;
lv_obj_t* content_;
lv_obj_t* modal_content_;
lv_group_t* const group_;
lv_group_t* modal_group_;
@ -62,4 +62,9 @@ class Screen {
std::unique_ptr<widgets::TopBar> top_bar_;
};
class MenuScreen : public Screen {
public:
MenuScreen(const std::string& title, bool show_back_button = true);
};
} // namespace ui

@ -13,11 +13,12 @@
#include "lvgl.h"
#include "screen.hpp"
#include "screen_settings.hpp"
namespace ui {
namespace screens {
class Menu : public Screen {
class Menu : public MenuScreen {
public:
explicit Menu(std::vector<database::IndexInfo> indexes);
~Menu();

@ -0,0 +1,70 @@
/*
* 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 "screen.hpp"
namespace ui {
namespace screens {
class Settings : public MenuScreen {
public:
Settings();
~Settings();
private:
std::shared_ptr<Screen> bluetooth_;
std::shared_ptr<Screen> headphones_;
std::shared_ptr<Screen> appearance_;
std::shared_ptr<Screen> input_method_;
std::shared_ptr<Screen> storage_;
std::shared_ptr<Screen> firmware_update_;
std::shared_ptr<Screen> about_;
};
class Bluetooth : public MenuScreen {
public:
Bluetooth();
};
class Headphones : public MenuScreen {
public:
Headphones();
};
class Appearance : public MenuScreen {
public:
Appearance();
};
class InputMethod : public MenuScreen {
public:
InputMethod();
};
class Storage : public MenuScreen {
public:
Storage();
};
class FirmwareUpdate : public MenuScreen {
public:
FirmwareUpdate();
};
class About : public MenuScreen {
public:
About();
};
} // namespace screens
} // namespace ui

@ -9,6 +9,7 @@
#include <memory>
#include "database.hpp"
#include "index.hpp"
#include "screen.hpp"
#include "tinyfsm.hpp"
namespace ui {
@ -36,6 +37,10 @@ struct IndexSelected : tinyfsm::Event {
};
struct BackPressed : tinyfsm::Event {};
struct ShowNowPlaying : tinyfsm::Event {};
struct ShowSettingsPage : tinyfsm::Event {
std::shared_ptr<Screen> screen;
};
struct ModalConfirmPressed : tinyfsm::Event {};
struct ModalCancelPressed : tinyfsm::Event {};

@ -52,6 +52,8 @@ 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::ShowNowPlaying&){};
virtual void react(const internal::ShowSettingsPage&){};
virtual void react(const internal::ModalCancelPressed&) {
sCurrentModal.reset();
}
@ -104,6 +106,9 @@ class Browse : public UiState {
void react(const internal::IndexSelected&) override;
void react(const internal::BackPressed&) override;
void react(const internal::ShowNowPlaying&) override;
void react(const internal::ShowSettingsPage&) override;
void react(const system_fsm::StorageMounted&) override;
using UiState::react;
};

@ -33,7 +33,6 @@ Modal::Modal(Screen* host)
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_);

@ -57,4 +57,28 @@ auto Screen::CreateTopBar(lv_obj_t* parent,
return top_bar_.get();
}
MenuScreen::MenuScreen(const std::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);
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);
}
} // namespace ui

@ -26,7 +26,16 @@
namespace ui {
namespace screens {
static void item_click_cb(lv_event_t* ev) {
static void now_playing_click_cb(lv_event_t* ev) {
events::Ui().Dispatch(internal::ShowNowPlaying{});
}
static void settings_click_callback(lv_event_t* ev) {
std::shared_ptr<Screen> settings{new Settings()};
events::Ui().Dispatch(internal::ShowSettingsPage{.screen = settings});
}
static void index_click_cb(lv_event_t* ev) {
if (ev->user_data == NULL) {
return;
}
@ -36,27 +45,26 @@ static void item_click_cb(lv_event_t* ev) {
events::Ui().Dispatch(internal::IndexSelected{.index = *index});
}
Menu::Menu(std::vector<database::IndexInfo> indexes) : indexes_(indexes) {
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(content_, config);
Menu::Menu(std::vector<database::IndexInfo> indexes)
: MenuScreen(" ", false), indexes_(indexes) {
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);
lv_obj_set_size(list, lv_pct(100), lv_pct(100));
lv_obj_t* now_playing = lv_list_add_btn(list, NULL, "Now Playing");
lv_obj_add_event_cb(now_playing, now_playing_click_cb, LV_EVENT_CLICKED,
NULL);
lv_group_add_obj(group_, now_playing);
for (database::IndexInfo& index : indexes_) {
lv_obj_t* item = lv_list_add_btn(list, NULL, index.name.c_str());
lv_obj_add_event_cb(item, item_click_cb, LV_EVENT_CLICKED, &index);
lv_obj_add_event_cb(item, index_click_cb, LV_EVENT_CLICKED, &index);
lv_group_add_obj(group_, item);
}
lv_obj_t* settings = lv_list_add_btn(list, NULL, "Settings");
lv_obj_add_event_cb(settings, settings_click_callback, LV_EVENT_CLICKED,
NULL);
lv_group_add_obj(group_, settings);
}
Menu::~Menu() {}

@ -109,7 +109,7 @@ Playing::Playing(std::weak_ptr<database::Database> db, audio::TrackQueue* queue)
lv_obj_set_layout(content_, LV_LAYOUT_FLEX);
lv_group_set_wrap(group_, false);
lv_obj_set_size(content_, lv_pct(100), LV_SIZE_CONTENT);
lv_obj_set_size(content_, lv_pct(100), lv_disp_get_ver_res(NULL));
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);

@ -0,0 +1,214 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#include "screen_settings.hpp"
#include "core/lv_event.h"
#include "core/lv_obj.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/spinner/lv_spinner.h"
#include "hal/lv_hal_disp.h"
#include "index.hpp"
#include "misc/lv_anim.h"
#include "misc/lv_area.h"
#include "screen.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"
namespace ui {
namespace screens {
static void open_sub_menu_cb(lv_event_t* e) {
std::shared_ptr<Screen>* next_screen =
reinterpret_cast<std::shared_ptr<Screen>*>(e->user_data);
events::Ui().Dispatch(internal::ShowSettingsPage{
.screen = *next_screen,
});
}
static void sub_menu(lv_obj_t* list,
lv_group_t* group,
const std::string& text,
std::shared_ptr<Screen>* screen) {
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, screen);
}
Settings::Settings()
: MenuScreen("Settings"),
bluetooth_(new Bluetooth()),
headphones_(new Headphones()),
appearance_(new Appearance()),
input_method_(new InputMethod()),
storage_(new Storage()),
firmware_update_(new FirmwareUpdate()),
about_(new About()) {
lv_obj_t* list = lv_list_create(content_);
lv_obj_set_size(list, lv_pct(100), lv_pct(100));
lv_list_add_text(list, "Audio");
sub_menu(list, group_, "Bluetooth", &bluetooth_);
sub_menu(list, group_, "Headphones", &headphones_);
lv_list_add_text(list, "Interface");
sub_menu(list, group_, "Appearance", &appearance_);
sub_menu(list, group_, "Input Method", &input_method_);
lv_list_add_text(list, "System");
sub_menu(list, group_, "Storage", &storage_);
sub_menu(list, group_, "Firmware Update", &firmware_update_);
sub_menu(list, group_, "About", &about_);
}
Settings::~Settings() {}
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::string& left,
const std::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;
}
Bluetooth::Bluetooth() : MenuScreen("Bluetooth") {
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);
lv_obj_t* devices_label = lv_label_create(content_);
lv_label_set_text(devices_label, "Devices");
lv_obj_t* devices_list = lv_list_create(content_);
lv_list_add_text(devices_list, "My Headphones");
lv_group_add_obj(group_,
lv_list_add_btn(devices_list, NULL, "Something else"));
lv_group_add_obj(group_, lv_list_add_btn(devices_list, NULL, "A car??"));
lv_group_add_obj(group_,
lv_list_add_btn(devices_list, NULL, "OLED TV ANDROID"));
lv_group_add_obj(
group_, lv_list_add_btn(devices_list, NULL, "there could be another"));
lv_group_add_obj(group_, lv_list_add_btn(devices_list, NULL,
"this one has a really long name"));
}
Headphones::Headphones() : MenuScreen("Headphones") {
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_dropdown_set_options(vol_dropdown,
"Line Level (-10 dBV)\nPro Level (+4 dBu)\nMax "
"before clipping\nUnlimited\nCustom");
lv_group_add_obj(group_, vol_dropdown);
lv_obj_t* warning_label = label_pair(
content_, "!!", "Changing volume limit is for advanced users.");
lv_label_set_long_mode(warning_label, LV_LABEL_LONG_WRAP);
lv_obj_set_flex_grow(warning_label, 1);
lv_obj_t* balance_label = lv_label_create(content_);
lv_label_set_text(balance_label, "Left/Right Balance");
lv_obj_t* balance = lv_slider_create(content_);
lv_obj_set_width(balance, lv_pct(100));
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);
}
Appearance::Appearance() : MenuScreen("Appearance") {
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);
}
InputMethod::InputMethod() : MenuScreen("Input Method") {
lv_obj_t* wheel_label = lv_label_create(content_);
lv_label_set_text(wheel_label, "What does the wheel do?");
lv_obj_t* wheel_dropdown = lv_dropdown_create(content_);
lv_dropdown_set_options(wheel_dropdown, "Scroll\nDirectional\nBig Button");
lv_group_add_obj(group_, wheel_dropdown);
lv_obj_t* buttons_label = lv_label_create(content_);
lv_label_set_text(buttons_label, "What do the buttons do?");
lv_obj_t* buttons_dropdown = lv_dropdown_create(content_);
lv_dropdown_set_options(buttons_dropdown, "Volume\nScroll");
lv_group_add_obj(group_, buttons_dropdown);
}
Storage::Storage() : MenuScreen("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* reset_btn = lv_btn_create(content_);
lv_obj_t* reset_label = lv_label_create(reset_btn);
lv_label_set_text(reset_label, "Reset Database");
lv_group_add_obj(group_, reset_btn);
}
FirmwareUpdate::FirmwareUpdate() : MenuScreen("Firmware Update") {
label_pair(content_, "ESP32 FW:", "vIDKLOL");
label_pair(content_, "SAMD21 FW:", "vIDKLOL");
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 ESP32");
lv_group_add_obj(group_, flash_esp_btn);
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 SAMD21");
lv_group_add_obj(group_, flash_samd_btn);
}
About::About() : MenuScreen("About") {
lv_obj_t* label = lv_label_create(content_);
lv_label_set_text(label, "Some licenses or whatever");
}
} // namespace screens
} // namespace ui

@ -21,6 +21,7 @@
#include "screen.hpp"
#include "screen_menu.hpp"
#include "screen_playing.hpp"
#include "screen_settings.hpp"
#include "screen_splash.hpp"
#include "screen_track_browser.hpp"
#include "source.hpp"
@ -137,6 +138,14 @@ void Browse::react(const system_fsm::StorageMounted& ev) {
PushScreen(std::make_shared<screens::Menu>(db->GetIndexes()));
}
void Browse::react(const internal::ShowNowPlaying& ev) {
transit<Playing>();
}
void Browse::react(const internal::ShowSettingsPage& ev) {
PushScreen(ev.screen);
}
void Browse::react(const internal::RecordSelected& ev) {
auto db = sDb.lock();
if (!db) {

@ -55,7 +55,7 @@ TopBar::TopBar(lv_obj_t* parent, const Configuration& config) {
auto TopBar::Update(const State& state) -> void {
switch (state.playback_state) {
case PlaybackState::kIdle:
lv_label_set_text(playback_, "");
lv_label_set_text(playback_, "-");
break;
case PlaybackState::kPaused:
lv_label_set_text(playback_, "");

Loading…
Cancel
Save