From d8533c42df9d3ac250d896c9fd1077b8c9a9552b Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 17 Aug 2023 15:58:32 +1000 Subject: [PATCH] Start on basic onboarding screens --- src/database/CMakeLists.txt | 6 +- src/database/database.cpp | 10 +++ src/database/include/db_events.hpp | 27 ++++++++ src/ui/CMakeLists.txt | 5 +- src/ui/include/screen_onboarding.hpp | 50 +++++++++++++++ src/ui/include/ui_fsm.hpp | 7 ++ src/ui/screen_onboarding.cpp | 95 ++++++++++++++++++++++++++++ 7 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 src/database/include/db_events.hpp create mode 100644 src/ui/include/screen_onboarding.hpp create mode 100644 src/ui/screen_onboarding.cpp diff --git a/src/database/CMakeLists.txt b/src/database/CMakeLists.txt index c5cc59cb..129920cd 100644 --- a/src/database/CMakeLists.txt +++ b/src/database/CMakeLists.txt @@ -3,9 +3,11 @@ # SPDX-License-Identifier: GPL-3.0-only idf_component_register( - SRCS "env_esp.cpp" "database.cpp" "track.cpp" "records.cpp" "file_gatherer.cpp" "tag_parser.cpp" "index.cpp" + SRCS "env_esp.cpp" "database.cpp" "track.cpp" "records.cpp" + "file_gatherer.cpp" "tag_parser.cpp" "index.cpp" INCLUDE_DIRS "include" - REQUIRES "result" "span" "esp_psram" "fatfs" "libtags" "komihash" "cbor" "tasks" "shared_string" "util") + REQUIRES "result" "span" "esp_psram" "fatfs" "libtags" "komihash" "cbor" + "tasks" "shared_string" "util" "tinyfsm" "events") target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) diff --git a/src/database/database.cpp b/src/database/database.cpp index e6341e43..2a5b3236 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -27,7 +27,9 @@ #include "leveldb/slice.h" #include "leveldb/write_batch.h" +#include "db_events.hpp" #include "env_esp.hpp" +#include "event_queue.hpp" #include "file_gatherer.hpp" #include "records.hpp" #include "result.hpp" @@ -131,6 +133,7 @@ Database::~Database() { } auto Database::Update() -> std::future { + events::Ui().Dispatch(event::UpdateStarted{}); return worker_task_->Dispatch([&]() -> void { leveldb::ReadOptions read_options; read_options.fill_cache = false; @@ -151,6 +154,9 @@ auto Database::Update() -> std::future { // Stage 1: verify all existing tracks are still valid. ESP_LOGI(kTag, "verifying existing tracks"); + events::Ui().Dispatch(event::UpdateProgress{ + .stage = event::UpdateProgress::Stage::kVerifyingExistingTracks, + }); { leveldb::Iterator* it = db_->NewIterator(read_options); OwningSlice prefix = EncodeDataPrefix(); @@ -206,6 +212,9 @@ auto Database::Update() -> std::future { // Stage 2: search for newly added files. ESP_LOGI(kTag, "scanning for new tracks"); + events::Ui().Dispatch(event::UpdateProgress{ + .stage = event::UpdateProgress::Stage::kScanningForNewTracks, + }); file_gatherer_->FindFiles("", [&](const std::string& path) { TrackTags tags; if (!tag_parser_->ReadAndParseTags(path, &tags) || @@ -254,6 +263,7 @@ auto Database::Update() -> std::future { existing_data->filepath().c_str(), path.c_str()); } }); + events::Ui().Dispatch(event::UpdateFinished{}); }); } diff --git a/src/database/include/db_events.hpp b/src/database/include/db_events.hpp new file mode 100644 index 00000000..c071c6d4 --- /dev/null +++ b/src/database/include/db_events.hpp @@ -0,0 +1,27 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include "tinyfsm.hpp" + +namespace database { +namespace event { + +struct UpdateStarted : tinyfsm::Event {}; + +struct UpdateFinished : tinyfsm::Event {}; + +struct UpdateProgress : tinyfsm::Event { + enum class Stage { + kVerifyingExistingTracks, + kScanningForNewTracks, + }; + Stage stage; +}; + +} // namespace event +} // namespace database diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 71330cb5..f17020ac 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -3,8 +3,9 @@ # SPDX-License-Identifier: GPL-3.0-only 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" + 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" "splash.c" "font_fusion.c" "font_symbols.c" INCLUDE_DIRS "include" REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer") diff --git a/src/ui/include/screen_onboarding.hpp b/src/ui/include/screen_onboarding.hpp new file mode 100644 index 00000000..d7751926 --- /dev/null +++ b/src/ui/include/screen_onboarding.hpp @@ -0,0 +1,50 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include +#include + +#include "lvgl.h" + +#include "screen.hpp" + +namespace ui { +namespace screens { + +class Onboarding : public Screen { + public: + Onboarding(const std::string& title, bool show_prev, bool show_next); + + private: + lv_obj_t* window_; + lv_obj_t* title_; + lv_obj_t* next_button_; + lv_obj_t* prev_button_; + + protected: + lv_obj_t* content_; +}; + +namespace onboarding { + +class LinkToManual : public Onboarding { + LinkToManual(); +}; + +class Controls : public Onboarding { + Controls(); +}; + +class FormatSdCard : public Onboarding { + FormatSdCard(); +}; + +} // namespace onboarding + +} // namespace screens +} // namespace ui diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index bb8ecd0a..1551932a 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -79,6 +79,13 @@ class Splash : public UiState { using UiState::react; }; +class Onboarding : public UiState { + public: + void entry() override; + + using UiState::react; +}; + class Browse : public UiState { void entry() override; diff --git a/src/ui/screen_onboarding.cpp b/src/ui/screen_onboarding.cpp new file mode 100644 index 00000000..ea8223ba --- /dev/null +++ b/src/ui/screen_onboarding.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "screen_onboarding.hpp" + +#include "draw/lv_draw_rect.h" +#include "extra/libs/qrcode/lv_qrcode.h" +#include "extra/widgets/win/lv_win.h" +#include "font/lv_symbol_def.h" +#include "misc/lv_color.h" +#include "widgets/lv_btn.h" +#include "widgets/lv_label.h" +#include "widgets/lv_switch.h" + +static const char kManualUrl[] = "https://tangara.gay/onboarding"; + +namespace ui { +namespace screens { + +Onboarding::Onboarding(const std::string& title, + bool show_prev, + bool show_next) { + window_ = lv_win_create(root_, 18); + if (show_prev) { + prev_button_ = lv_win_add_btn(window_, LV_SYMBOL_LEFT, 20); + } + title_ = lv_win_add_title(window_, title.c_str()); + if (show_next) { + next_button_ = lv_win_add_btn(window_, LV_SYMBOL_RIGHT, 20); + } + + content_ = lv_win_get_content(window_); +} + +namespace onboarding { + +LinkToManual::LinkToManual() : Onboarding("Welcome!", false, true) { + lv_obj_t* intro = lv_label_create(content_); + lv_label_set_text(intro, "this screen links you to better instructions"); + + lv_obj_t* qr = + lv_qrcode_create(content_, 100, lv_color_black(), lv_color_white()); + lv_qrcode_update(qr, kManualUrl, sizeof(kManualUrl)); +} + +static void create_radio_button(lv_obj_t* parent, const std::string& text) { + lv_obj_t* obj = lv_checkbox_create(parent); + lv_checkbox_set_text(obj, text.c_str()); + // TODO: radio styling +} + +Controls::Controls() : Onboarding("Controls", true, true) { + lv_obj_t* label = lv_label_create(content_); + lv_label_set_text(label, "this screen changes your control scheme."); + + label = lv_label_create(content_); + lv_label_set_text(label, "how does the touch wheel behave?"); + + create_radio_button(content_, "iPod-style"); + create_radio_button(content_, "Directional"); + create_radio_button(content_, "One Big Button"); + + label = lv_label_create(content_); + lv_label_set_text(label, "how do the side buttons behave?"); + + create_radio_button(content_, "Adjust volume"); + create_radio_button(content_, "Scroll"); +} + +FormatSdCard::FormatSdCard() : Onboarding("SD Card", true, false) { + lv_obj_t* label = lv_label_create(content_); + lv_label_set_text( + label, "this screen is optional. it offers to format your sd card."); + + lv_obj_t* button = lv_btn_create(content_); + label = lv_label_create(button); + lv_label_set_text(label, "Format"); + + label = lv_label_create(content_); + lv_label_set_text(label, "Advanced"); + + lv_obj_t* advanced_container = lv_obj_create(content_); + + label = lv_label_create(advanced_container); + lv_label_set_text(label, "Use exFAT"); + lv_switch_create(advanced_container); +} + +} // namespace onboarding + +} // namespace screens +} // namespace ui