diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0bcb788fae..2450b00ff3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -396,7 +396,7 @@ list(APPEND SOURCE_FILES displayapp/screens/Motion.cpp displayapp/screens/FlashLight.cpp displayapp/screens/List.cpp - displayapp/screens/CheckboxList.cpp + displayapp/screens/Container.cpp displayapp/screens/BatteryInfo.cpp displayapp/screens/Steps.cpp displayapp/screens/Timer.cpp @@ -429,6 +429,7 @@ list(APPEND SOURCE_FILES displayapp/screens/WatchFaceDigital.cpp displayapp/screens/WatchFaceInfineat.cpp displayapp/screens/WatchFaceTerminal.cpp + displayapp/screens/WatchFaceFuzzy.cpp displayapp/screens/WatchFacePineTimeStyle.cpp ## diff --git a/src/displayapp/Apps.h b/src/displayapp/Apps.h index 8aad953517..6e3830c2c9 100644 --- a/src/displayapp/Apps.h +++ b/src/displayapp/Apps.h @@ -6,6 +6,7 @@ namespace Pinetime { None, Launcher, Clock, + WatchFaceDigitalPreview, SysInfo, FirmwareUpdate, FirmwareValidation, diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index aa2c037e3f..aca77c4dab 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -12,6 +12,7 @@ #include "components/motor/MotorController.h" #include "displayapp/screens/ApplicationList.h" #include "displayapp/screens/Clock.h" +#include "displayapp/screens/WatchFaceDigital.h" #include "displayapp/screens/FirmwareUpdate.h" #include "displayapp/screens/FirmwareValidation.h" #include "displayapp/screens/InfiniPaint.h" @@ -327,9 +328,21 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) settingsController, heartRateController, motionController, + motorController, filesystem); break; - + case Apps::WatchFaceDigitalPreview: + currentScreen = std::make_unique(this, + dateTimeController, + batteryController, + bleController, + notificationManager, + settingsController, + heartRateController, + motionController, + Screens::Screen::Modes::Preview); + ReturnApp(Apps::Clock, FullRefreshDirections::Left, TouchEvents::None); + break; case Apps::Error: currentScreen = std::make_unique(this, bootError); ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::None); diff --git a/src/displayapp/fonts/README.md b/src/displayapp/fonts/README.md index b2669a788c..03b872c404 100644 --- a/src/displayapp/fonts/README.md +++ b/src/displayapp/fonts/README.md @@ -15,9 +15,7 @@ using [this site](http://www.ltg.ed.ac.uk/~richard/utf-8.cgi?input=f185&mode=hex) - Define the new symbols in `src/displayapp/screens/Symbols.h`: -``` -static constexpr const char* newSymbol = "\xEF\x86\x85"; -``` +Fill the font converter fields as follows: ### the config file format: diff --git a/src/displayapp/fonts/fonts.json b/src/displayapp/fonts/fonts.json index 006b884947..6d409bb9f3 100644 --- a/src/displayapp/fonts/fonts.json +++ b/src/displayapp/fonts/fonts.json @@ -18,7 +18,7 @@ "sources": [ { "file": "JetBrainsMono-Regular.ttf", - "range": "0x25, 0x2b, 0x2d, 0x30-0x3a" + "range": "0x20, 0x25, 0x27, 0x2b, 0x2d, 0x30-0x3a, 0x61-0x7a" } ], "bpp": 1, diff --git a/src/displayapp/screens/CheckboxList.cpp b/src/displayapp/screens/CheckboxList.cpp deleted file mode 100644 index b89add4333..0000000000 --- a/src/displayapp/screens/CheckboxList.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "displayapp/screens/CheckboxList.h" -#include "displayapp/DisplayApp.h" -#include "displayapp/screens/Styles.h" - -using namespace Pinetime::Applications::Screens; - -namespace { - static void event_handler(lv_obj_t* obj, lv_event_t event) { - CheckboxList* screen = static_cast(obj->user_data); - screen->UpdateSelected(obj, event); - } - -} - -CheckboxList::CheckboxList(const uint8_t screenID, - const uint8_t numScreens, - DisplayApp* app, - Controllers::Settings& settingsController, - const char* optionsTitle, - const char* optionsSymbol, - void (Controllers::Settings::*SetOptionIndex)(uint8_t), - uint8_t (Controllers::Settings::*GetOptionIndex)() const, - std::array options) - : Screen(app), - screenID {screenID}, - settingsController {settingsController}, - SetOptionIndex {SetOptionIndex}, - GetOptionIndex {GetOptionIndex}, - options {options} { - - settingsController.SetWatchfacesMenu(screenID); - - // Set the background to Black - lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); - - if (numScreens > 1) { - pageIndicatorBasePoints[0].x = LV_HOR_RES - 1; - pageIndicatorBasePoints[0].y = 0; - pageIndicatorBasePoints[1].x = LV_HOR_RES - 1; - pageIndicatorBasePoints[1].y = LV_VER_RES; - - pageIndicatorBase = lv_line_create(lv_scr_act(), NULL); - lv_obj_set_style_local_line_width(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3); - lv_obj_set_style_local_line_color(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111)); - lv_line_set_points(pageIndicatorBase, pageIndicatorBasePoints.data(), 2); - - const uint16_t indicatorSize = LV_VER_RES / numScreens; - const uint16_t indicatorPos = indicatorSize * screenID; - - pageIndicatorPoints[0].x = LV_HOR_RES - 1; - pageIndicatorPoints[0].y = indicatorPos; - pageIndicatorPoints[1].x = LV_HOR_RES - 1; - pageIndicatorPoints[1].y = indicatorPos + indicatorSize; - - pageIndicator = lv_line_create(lv_scr_act(), NULL); - lv_obj_set_style_local_line_width(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3); - lv_obj_set_style_local_line_color(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); - lv_line_set_points(pageIndicator, pageIndicatorPoints.data(), 2); - } - - lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr); - - lv_obj_set_style_local_bg_opa(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP); - lv_obj_set_style_local_pad_all(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 10); - lv_obj_set_style_local_pad_inner(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5); - lv_obj_set_style_local_border_width(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0); - - lv_obj_set_pos(container1, 10, 60); - lv_obj_set_width(container1, LV_HOR_RES - 20); - lv_obj_set_height(container1, LV_VER_RES - 50); - lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT); - - lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text_static(title, optionsTitle); - lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); - lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 10, 15); - - lv_obj_t* icon = lv_label_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_text_color(icon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE); - lv_label_set_text_static(icon, optionsSymbol); - lv_label_set_align(icon, LV_LABEL_ALIGN_CENTER); - lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0); - - for (unsigned int i = 0; i < options.size(); i++) { - if (strcmp(options[i], "")) { - cbOption[i] = lv_checkbox_create(container1, nullptr); - lv_checkbox_set_text(cbOption[i], options[i]); - cbOption[i]->user_data = this; - lv_obj_set_event_cb(cbOption[i], event_handler); - SetRadioButtonStyle(cbOption[i]); - - if (static_cast((settingsController.*GetOptionIndex)() - MaxItems * screenID) == i) { - lv_checkbox_set_checked(cbOption[i], true); - } - } - } -} - -CheckboxList::~CheckboxList() { - lv_obj_clean(lv_scr_act()); - settingsController.SaveSettings(); -} - -void CheckboxList::UpdateSelected(lv_obj_t* object, lv_event_t event) { - if (event == LV_EVENT_VALUE_CHANGED) { - for (unsigned int i = 0; i < options.size(); i++) { - if (strcmp(options[i], "")) { - if (object == cbOption[i]) { - lv_checkbox_set_checked(cbOption[i], true); - (settingsController.*SetOptionIndex)(MaxItems * screenID + i); - } else { - lv_checkbox_set_checked(cbOption[i], false); - } - } - } - } -} diff --git a/src/displayapp/screens/CheckboxList.h b/src/displayapp/screens/CheckboxList.h deleted file mode 100644 index 5bdd143e3d..0000000000 --- a/src/displayapp/screens/CheckboxList.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include "displayapp/screens/Screen.h" -#include "displayapp/Apps.h" -#include "components/settings/Settings.h" - -namespace Pinetime { - namespace Applications { - namespace Screens { - class CheckboxList : public Screen { - public: - static constexpr size_t MaxItems = 4; - - CheckboxList(const uint8_t screenID, - const uint8_t numScreens, - DisplayApp* app, - Controllers::Settings& settingsController, - const char* optionsTitle, - const char* optionsSymbol, - void (Controllers::Settings::*SetOptionIndex)(uint8_t), - uint8_t (Controllers::Settings::*GetOptionIndex)() const, - std::array options); - - ~CheckboxList() override; - - void UpdateSelected(lv_obj_t* object, lv_event_t event); - - private: - const uint8_t screenID; - Controllers::Settings& settingsController; - const char* optionsTitle; - const char* optionsSymbol; - void (Controllers::Settings::*SetOptionIndex)(uint8_t); - uint8_t (Controllers::Settings::*GetOptionIndex)() const; - std::array options; - std::array cbOption; - std::array pageIndicatorBasePoints; - std::array pageIndicatorPoints; - lv_obj_t* pageIndicatorBase; - lv_obj_t* pageIndicator; - }; - } - } -} diff --git a/src/displayapp/screens/Clock.cpp b/src/displayapp/screens/Clock.cpp index d1e0f5659f..a92d9344e9 100644 --- a/src/displayapp/screens/Clock.cpp +++ b/src/displayapp/screens/Clock.cpp @@ -4,6 +4,7 @@ #include #include "components/battery/BatteryController.h" #include "components/motion/MotionController.h" +#include "components/motor/MotorController.h" #include "components/ble/BleController.h" #include "components/ble/NotificationManager.h" #include "components/settings/Settings.h" @@ -13,6 +14,7 @@ #include "displayapp/screens/WatchFaceInfineat.h" #include "displayapp/screens/WatchFaceAnalog.h" #include "displayapp/screens/WatchFacePineTimeStyle.h" +#include "displayapp/screens/WatchFaceFuzzy.h" using namespace Pinetime::Applications::Screens; @@ -24,6 +26,7 @@ Clock::Clock(DisplayApp* app, Controllers::Settings& settingsController, Controllers::HeartRateController& heartRateController, Controllers::MotionController& motionController, + Controllers::MotorController& motorController, Controllers::FS& filesystem) : Screen(app), dateTimeController {dateTimeController}, @@ -33,6 +36,7 @@ Clock::Clock(DisplayApp* app, settingsController {settingsController}, heartRateController {heartRateController}, motionController {motionController}, + motorController {motorController}, filesystem {filesystem}, screen {[this, &settingsController]() { switch (settingsController.GetClockFace()) { @@ -51,6 +55,9 @@ Clock::Clock(DisplayApp* app, case 4: return WatchFaceInfineatScreen(); break; + case 5: + return WatchFaceFuzzyScreen(); + break; } return WatchFaceDigitalScreen(); }()} { @@ -115,8 +122,16 @@ std::unique_ptr Clock::WatchFaceInfineatScreen() { dateTimeController, batteryController, bleController, - notificatioManager, + notificationManager, settingsController, motionController, filesystem); } + +std::unique_ptr Clock::WatchFaceFuzzyScreen() { + return std::make_unique(app, + dateTimeController, + settingsController, + motorController, + motionController); +} diff --git a/src/displayapp/screens/Clock.h b/src/displayapp/screens/Clock.h index e5605c5f54..f210d1ebb7 100644 --- a/src/displayapp/screens/Clock.h +++ b/src/displayapp/screens/Clock.h @@ -15,6 +15,7 @@ namespace Pinetime { class Ble; class NotificationManager; class MotionController; + class MotorController; } namespace Applications { @@ -29,6 +30,7 @@ namespace Pinetime { Controllers::Settings& settingsController, Controllers::HeartRateController& heartRateController, Controllers::MotionController& motionController, + Controllers::MotorController& motorController, Controllers::FS& filesystem); ~Clock() override; @@ -43,6 +45,7 @@ namespace Pinetime { Controllers::Settings& settingsController; Controllers::HeartRateController& heartRateController; Controllers::MotionController& motionController; + Controllers::MotorController& motorController; Controllers::FS& filesystem; std::unique_ptr screen; @@ -51,6 +54,7 @@ namespace Pinetime { std::unique_ptr WatchFacePineTimeStyleScreen(); std::unique_ptr WatchFaceTerminalScreen(); std::unique_ptr WatchFaceInfineatScreen(); + std::unique_ptr WatchFaceFuzzyScreen(); }; } } diff --git a/src/displayapp/screens/Container.cpp b/src/displayapp/screens/Container.cpp new file mode 100644 index 0000000000..a04312fb9f --- /dev/null +++ b/src/displayapp/screens/Container.cpp @@ -0,0 +1,17 @@ +#include "displayapp/screens/Container.h" + +using namespace Pinetime::Applications::Screens; + +Container::Container( + DisplayApp* app, + lv_obj_t* container, + uint8_t screenIdx, + uint8_t nScreens, + bool horizontal) + : Screen(app), container {container}, pageIndicator {screenIdx, nScreens, horizontal} { + pageIndicator.Create(); +} + +Container::~Container() { + lv_obj_clean(lv_scr_act()); +} diff --git a/src/displayapp/screens/Container.h b/src/displayapp/screens/Container.h new file mode 100644 index 0000000000..7d3787812c --- /dev/null +++ b/src/displayapp/screens/Container.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include "displayapp/screens/Screen.h" +#include "displayapp/widgets/PageIndicator.h" + +namespace Pinetime { + namespace Applications { + namespace Screens { + + class Container : public Screen { + public: + Container(DisplayApp* app, lv_obj_t* container, uint8_t screenIdx, uint8_t nScreens, bool horizontal = false); + ~Container() override; + + private: + lv_obj_t* container; + Widgets::PageIndicator pageIndicator; + }; + } + } +} diff --git a/src/displayapp/screens/Notifications.h b/src/displayapp/screens/Notifications.h index 9d843a9b13..adcb26617b 100644 --- a/src/displayapp/screens/Notifications.h +++ b/src/displayapp/screens/Notifications.h @@ -18,7 +18,6 @@ namespace Pinetime { class Notifications : public Screen { public: - enum class Modes { Normal, Preview }; explicit Notifications(DisplayApp* app, Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::AlertNotificationService& alertNotificationService, diff --git a/src/displayapp/screens/Screen.h b/src/displayapp/screens/Screen.h index e72a2368fb..167b698ef8 100644 --- a/src/displayapp/screens/Screen.h +++ b/src/displayapp/screens/Screen.h @@ -44,7 +44,9 @@ namespace Pinetime { } public: - explicit Screen(DisplayApp* app) : app {app} { + enum class Modes { Normal, Preview }; + explicit Screen(DisplayApp* app, Modes mode = Modes::Normal) + : app {app}, mode {mode} { } virtual ~Screen() = default; @@ -71,6 +73,7 @@ namespace Pinetime { protected: DisplayApp* app; bool running = true; + Modes mode = Modes::Normal; }; } } diff --git a/src/displayapp/screens/ScreenList.h b/src/displayapp/screens/ScreenList.h index ad882948de..38a7612f2f 100644 --- a/src/displayapp/screens/ScreenList.h +++ b/src/displayapp/screens/ScreenList.h @@ -99,6 +99,10 @@ namespace Pinetime { return false; } + uint8_t getScreenIndex() { + return screenIndex; + } + private: uint8_t initScreen = 0; const std::array()>, N> screens; diff --git a/src/displayapp/screens/WatchFaceDigital.cpp b/src/displayapp/screens/WatchFaceDigital.cpp index 47f40dabbe..44e71dfd12 100644 --- a/src/displayapp/screens/WatchFaceDigital.cpp +++ b/src/displayapp/screens/WatchFaceDigital.cpp @@ -21,8 +21,9 @@ WatchFaceDigital::WatchFaceDigital(DisplayApp* app, Controllers::NotificationManager& notificationManager, Controllers::Settings& settingsController, Controllers::HeartRateController& heartRateController, - Controllers::MotionController& motionController) - : Screen(app), + Controllers::MotionController& motionController, + Screen::Modes mode) + : Screen(app, mode), currentDateTime {{}}, dateTimeController {dateTimeController}, notificationManager {notificationManager}, @@ -71,6 +72,10 @@ WatchFaceDigital::WatchFaceDigital(DisplayApp* app, lv_label_set_text_static(stepIcon, Symbols::shoe); lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0); + if (mode == Screen::Modes::Preview) { + timeoutTickCount = xTaskGetTickCount() + (5 * 1024); + } + taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); Refresh(); } @@ -175,4 +180,8 @@ void WatchFaceDigital::Refresh() { lv_obj_realign(stepValue); lv_obj_realign(stepIcon); } + + if (mode == Screen::Modes::Preview && xTaskGetTickCount() > timeoutTickCount) { + running = false; + } } diff --git a/src/displayapp/screens/WatchFaceDigital.h b/src/displayapp/screens/WatchFaceDigital.h index 60446afaa7..731b014472 100644 --- a/src/displayapp/screens/WatchFaceDigital.h +++ b/src/displayapp/screens/WatchFaceDigital.h @@ -31,7 +31,8 @@ namespace Pinetime { Controllers::NotificationManager& notificationManager, Controllers::Settings& settingsController, Controllers::HeartRateController& heartRateController, - Controllers::MotionController& motionController); + Controllers::MotionController& motionController, + Screen::Modes mode = Screen::Modes::Normal); ~WatchFaceDigital() override; void Refresh() override; @@ -71,6 +72,8 @@ namespace Pinetime { Controllers::HeartRateController& heartRateController; Controllers::MotionController& motionController; + uint32_t timeoutTickCount; + lv_task_t* taskRefresh; Widgets::StatusIcons statusIcons; }; diff --git a/src/displayapp/screens/WatchFaceFuzzy.cpp b/src/displayapp/screens/WatchFaceFuzzy.cpp new file mode 100644 index 0000000000..3b3bec55de --- /dev/null +++ b/src/displayapp/screens/WatchFaceFuzzy.cpp @@ -0,0 +1,230 @@ +#include "WatchFaceFuzzy.h" + +#include +#include + +using namespace Pinetime::Applications::Screens; + +WatchFaceFuzzy::WatchFaceFuzzy(DisplayApp* app, + Controllers::DateTime& dateTimeController, + Controllers::Settings& settingsController, + Controllers::MotorController& motorController, + Controllers::MotionController& motionController) + : Screen(app), + dateTimeController {dateTimeController}, + settingsController {settingsController}, + motorController {motorController}, + motionController {motionController} { + + backgroundLabel = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_click(backgroundLabel, true); + lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP); + lv_obj_set_size(backgroundLabel, LV_HOR_RES, LV_VER_RES); + lv_obj_set_pos(backgroundLabel, 0, 0); + lv_label_set_text_static(backgroundLabel, ""); + + timeLabel = lv_label_create(backgroundLabel, nullptr); + lv_label_set_long_mode(timeLabel, LV_LABEL_LONG_BREAK); + lv_obj_set_width(timeLabel, LV_HOR_RES); + lv_label_set_align(timeLabel, LV_LABEL_ALIGN_CENTER); + lv_label_set_recolor(timeLabel, true); + lv_obj_set_style_local_text_color(timeLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); + lv_obj_set_style_local_text_font(timeLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42); + + taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); + + Refresh(); +} + +WatchFaceFuzzy::~WatchFaceFuzzy() { + lv_task_del(taskRefresh); + lv_obj_clean(lv_scr_act()); +} + +void WatchFaceFuzzy::Refresh() { + // Triggered once, on shaking state change (off -> on) + should_digital = motionController.Should_ShakeWake(settingsController.GetShakeThreshold()); + if (!shaking && should_digital) { + motorController.RunForDuration(35); + app->StartApp(Apps::WatchFaceDigitalPreview, DisplayApp::FullRefreshDirections::Right); + } + shaking = should_digital; + + // TODO: while the refresh rate is high enough to detect interaction, + // we only need to run the rest of the code every 60 seconds. + uint8_t hours, minutes; + std::string hoursStr, timeStr; + + hours = dateTimeController.Hours() % 12; + minutes = dateTimeController.Minutes(); + auto sector = minutes / 5 + (minutes % 5 > 2); + if (sector == 12) { + hours = (hours + 1) % 12; + sector = 0; + } + + timeStr = timeSectors[sector]; + if (timeStr.find("%1") != std::string::npos) { + hours = (hours + 1) % 12; + } + hoursStr = std::string("#") + timeAccent + " " + hourNames[hours] + "#"; + timeStr.replace(timeStr.find("%"), 2, hoursStr); + + lv_label_set_text(timeLabel, timeStr.c_str()); + lv_obj_align(timeLabel, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); +} + +/* Inspired by XFCE4-panel's fuzzy clock. + * + * https://salsa.debian.org/xfce-team/desktop/xfce4-panel/-/blob/debian/master/plugins/clock/clock-fuzzy.c + * + * Strings contain either a `%0` or a `%1`, indicating the position of + * the `hour` or `hour+1`, respectively. + */ +const char* WatchFaceFuzzy::timeSectors[] = { + "%0\no'clock", + "five past\n%0", + "ten past\n%0", + "quarter\npast\n%0", + "twenty\npast\n%0", + "twenty\nfive past\n%0", + "half past\n%0", + "twenty\nfive to\n%1", + "twenty\nto %1", + "quarter\nto %1", + "ten to\n%1", + "five to\n%1", +}; +const char* WatchFaceFuzzy::hourNames[] = { + "twelve", + "one", + "two", + "three", + "four", + "five", + "six", + "seven", + "eight", + "nine", + "ten", + "eleven", +}; + +/* Once i18n is implemented, new languages can be introduced like this: + * + * const char* ca-ES_sectors[] = { + * "%0\nen punt", + * "%0\ni cinc", + * "%0\ni deu", + * "%0\ni quart", + * "%0\ni vint", + * "%0\ni vint-\ni-cinc", + * "%0\ni mitja", + * "%1\nmenys\nvint-\ni-cinc", + * "%1\nmenys\nvint", + * "%1\nmenys\nquart", + * "%1\nmenys deu", + * "%1\nmenys\ncinc", + * }; + * const char* ca-ES_hourNames[] = { + * "les dotze", + * "la una", + * "les dues", + * "les tres", + * "les\nquatre", + * "les cinc", + * "les sis", + * "les set", + * "les vuit", + * "les nou", + * "les deu", + * "les onze", + * }; + * + * const char* es-ES_sectors[] = { + * "%0\nen punto", + * "%0\ny cinco", + * "%0\ny diez", + * "%0\ny cuarto", + * "%0\ny veinte", + * "%0\ny veinti\ncinco", + * "%0\ny media", + * "%1\nmenos\nveinti\ncinco", + * "%1\nmenos\nveinte", + * "%1\nmenos\ncuarto", + * "%1\nmenos\ndiez", + * "%1\nmenos\ncinco", + * }; + * const char* es-ES_hourNames[] = { + * "las doce", + * "la una", + * "las dos", + * "las tres", + * "las\ncuatro", + * "las cinco", + * "las seis", + * "las siete", + * "las ocho", + * "las nueve", + * "las diez", + * "las once", + * }; + * + * char* it-IT_sectors[] = { + * "%0\nin punto", + * "%0 e cinque", + * "%0 e dieci", + * "%0 e un quarto", + * "%0 e venti", + * "%0 e venti cinque", + * "%0 e mezza", + * "%0 e trenta cinque", + * "%1 meno venti", + * "%1 meno un quarto", + * "%1 meno dieci", + * "%1 meno cinque", + * }; + * const char* it-IT_hourNames[] = { + * "dodici", + * "una", + * "due", + * "tre", + * "quattro", + * "cinque", + * "sei", + * "sette", + * "otto", + * "nove", + * "dieci", + * "undici", + * }; + * + * const char* de_DE_sectors[] = { + * "%0 Uhr", + * "Fünf nach %0", + * "Zehn nach %0", + * "Viertel nach %0", + * "Zwanzig nach %0", + * "Fünf vor halb %1", + * "Halb %1", + * "Fünf nach halb %1", + * "Zwanzig vor %1", + * "Viertel vor %1", + * "Zehn vor %1", + * "Fünf vor %1", + * }; + * const char* de_DE_hourNames[] = { + * "Zwölf", + * "Eins", // TODO: "Ein" in "Ein Uhr" + * "Zwei", + * "Drei", + * "Vier", + * "Fünf", + * "Sechs", + * "Sieben", + * "Acht", + * "Neun", + * "Zehn", + * "Elf", + * }; + */ diff --git a/src/displayapp/screens/WatchFaceFuzzy.h b/src/displayapp/screens/WatchFaceFuzzy.h new file mode 100644 index 0000000000..0474de020f --- /dev/null +++ b/src/displayapp/screens/WatchFaceFuzzy.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include "displayapp/screens/Screen.h" +#include "displayapp/DisplayApp.h" +#include "components/datetime/DateTimeController.h" +#include "components/settings/Settings.h" +#include "components/motor/MotorController.h" +#include "components/motion/MotionController.h" + +namespace Pinetime { + namespace Applications { + namespace Screens { + class WatchFaceFuzzy : public Screen { + public: + WatchFaceFuzzy(DisplayApp* app, + Controllers::DateTime& dateTimeController, + Controllers::Settings& settingsController, + Controllers::MotorController& motorController, + Controllers::MotionController& motionController); + ~WatchFaceFuzzy() override; + + void Refresh() override; + + private: + const char* timeAccent = "ffffff"; + static const char* timeSectors[12]; + static const char* hourNames[12]; + bool shaking = true; + bool should_digital; + + lv_obj_t* backgroundLabel; + lv_obj_t* timeLabel; + + Controllers::DateTime& dateTimeController; + Controllers::Settings& settingsController; + Controllers::MotorController& motorController; + Controllers::MotionController& motionController; + + lv_task_t* taskRefresh; + }; + } + } +} diff --git a/src/displayapp/screens/settings/SettingWatchFace.cpp b/src/displayapp/screens/settings/SettingWatchFace.cpp index bd2f349c3e..e87491c78c 100644 --- a/src/displayapp/screens/settings/SettingWatchFace.cpp +++ b/src/displayapp/screens/settings/SettingWatchFace.cpp @@ -1,62 +1,106 @@ #include "displayapp/screens/settings/SettingWatchFace.h" #include #include "displayapp/DisplayApp.h" -#include "displayapp/screens/CheckboxList.h" #include "displayapp/screens/Screen.h" #include "displayapp/screens/Styles.h" -#include "displayapp/screens/Symbols.h" #include "components/settings/Settings.h" using namespace Pinetime::Applications::Screens; -constexpr const char* SettingWatchFace::title; -constexpr const char* SettingWatchFace::symbol; +namespace { + void event_handler(lv_obj_t* obj, lv_event_t event) { + auto* settings = static_cast(obj->user_data); + settings->UpdateSelected(obj, event); + } +} + +constexpr std::array SettingWatchFace::options; + +auto SettingWatchFace::CreateScreenList() { + std::array()>, nScreens> screens; + for (uint8_t i = 0; i < screens.size(); i++) { + screens[i] = [this, i]() -> std::unique_ptr { return CreateScreen(i); }; + } + return screens; +} + +std::unique_ptr SettingWatchFace::CreateScreen(uint8_t screenIdx) { + /* Container */ + lv_obj_t* container = lv_obj_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_bg_opa(container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP); + lv_obj_set_size(container, LV_HOR_RES, LV_VER_RES); + lv_obj_set_pos(container, 0, 0); + + /* Title... */ + lv_obj_t* title = lv_label_create(container, nullptr); + lv_label_set_text_static(title, this->title); + lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); + lv_obj_align(title, container, LV_ALIGN_IN_TOP_MID, 10, 15); + + /* ...with icon */ + lv_obj_t* icon = lv_label_create(container, nullptr); + lv_obj_set_style_local_text_color(icon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE); + lv_label_set_text_static(icon, this->icon); + lv_label_set_align(icon, LV_LABEL_ALIGN_CENTER); + lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0); + + /* Watchface option list */ + lv_obj_t* list = lv_cont_create(container, nullptr); -SettingWatchFace::SettingWatchFace(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Settings& settingsController) + lv_obj_set_style_local_bg_opa(list, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP); + lv_obj_set_style_local_pad_all(list, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 10); + lv_obj_set_style_local_pad_inner(list, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5); + lv_obj_set_style_local_border_width(list, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0); + + lv_obj_set_pos(list, 10, 60); + lv_obj_set_width(list, LV_HOR_RES - 20); + lv_obj_set_height(list, LV_VER_RES - 50); + lv_cont_set_layout(list, LV_LAYOUT_COLUMN_LEFT); + + for (uint8_t i = 0; i < optionsPerScreen ; i++) { + uint8_t optionIdx = screenIdx * optionsPerScreen + i; + if (optionIdx < nOptions) { + lv_obj_t* checkbox = lv_checkbox_create(list, nullptr); + lv_checkbox_set_text(checkbox, options[optionIdx]); + checkbox->user_data = this; + lv_obj_set_event_cb(checkbox, event_handler); + SetRadioButtonStyle(checkbox); + if (optionIdx == settingsController.GetClockFace()) { + lv_checkbox_set_checked(checkbox, true); + } + checkboxes[i] = checkbox; + } else { + checkboxes[i] = nullptr; + } + } + + return std::make_unique(app, container, screenIdx, nScreens, true); +} + +SettingWatchFace::SettingWatchFace(DisplayApp* app, Controllers::Settings& settingsController) : Screen(app), settingsController {settingsController}, - screens {app, - settingsController.GetWatchfacesMenu(), - {[this]() -> std::unique_ptr { - return CreateScreen1(); - }, - [this]() -> std::unique_ptr { - return CreateScreen2(); - }}, - Screens::ScreenListModes::UpDown} { -} + screens {app, 0, CreateScreenList(), Screens::ScreenListModes::RightLeft} { } SettingWatchFace::~SettingWatchFace() { lv_obj_clean(lv_scr_act()); settingsController.SaveSettings(); } -bool SettingWatchFace::OnTouchEvent(Pinetime::Applications::TouchEvents event) { +bool SettingWatchFace::OnTouchEvent(Applications::TouchEvents event) { return screens.OnTouchEvent(event); } -std::unique_ptr SettingWatchFace::CreateScreen1() { - std::array watchfaces {"Digital face", "Analog face", "PineTimeStyle", "Terminal"}; - return std::make_unique(0, - 2, - app, - settingsController, - title, - symbol, - &Controllers::Settings::SetClockFace, - &Controllers::Settings::GetClockFace, - watchfaces); -} - -std::unique_ptr SettingWatchFace::CreateScreen2() { - std::array watchfaces {"Infineat face", "", "", ""}; - return std::make_unique(1, - 2, - app, - settingsController, - title, - symbol, - &Controllers::Settings::SetClockFace, - &Controllers::Settings::GetClockFace, - watchfaces); +void SettingWatchFace::UpdateSelected(lv_obj_t* obj, lv_event_t event) { + if (event == LV_EVENT_VALUE_CHANGED) { + uint8_t screenIdx = screens.getScreenIndex(); + for (uint8_t i = 0; i < optionsPerScreen && checkboxes[i]; i++) { + if (obj == checkboxes[i]) { + lv_checkbox_set_checked(checkboxes[i], true); + settingsController.SetClockFace(screenIdx * optionsPerScreen + i); + } else { + lv_checkbox_set_checked(checkboxes[i], false); + } + } + } } diff --git a/src/displayapp/screens/settings/SettingWatchFace.h b/src/displayapp/screens/settings/SettingWatchFace.h index 7d14554eb2..32412885d0 100644 --- a/src/displayapp/screens/settings/SettingWatchFace.h +++ b/src/displayapp/screens/settings/SettingWatchFace.h @@ -3,32 +3,45 @@ #include #include #include - -#include "displayapp/screens/ScreenList.h" #include "components/settings/Settings.h" +#include "displayapp/screens/Container.h" #include "displayapp/screens/Screen.h" +#include "displayapp/screens/ScreenList.h" #include "displayapp/screens/Symbols.h" namespace Pinetime { - namespace Applications { namespace Screens { class SettingWatchFace : public Screen { public: - SettingWatchFace(DisplayApp* app, Pinetime::Controllers::Settings& settingsController); + SettingWatchFace(DisplayApp* app, Controllers::Settings& settingsController); ~SettingWatchFace() override; - bool OnTouchEvent(TouchEvents event) override; + bool OnTouchEvent(Applications::TouchEvents event) override; + + void UpdateSelected(lv_obj_t* object, lv_event_t event); private: + static constexpr const char* icon = Symbols::home; + static constexpr const char* title = "Watch face"; + + static constexpr uint8_t optionsPerScreen = 4; + static constexpr uint8_t nOptions = 6; + static constexpr uint8_t nScreens = (nOptions + optionsPerScreen - 1) / optionsPerScreen; + static constexpr std::array options = { + "Digital face", "Analog face", "PineTimeStyle", "Terminal", + "Infineat", "Fuzzy face", + }; + + auto CreateScreenList(); + std::unique_ptr CreateScreen(uint8_t screenIdx); + Controllers::Settings& settingsController; - ScreenList<2> screens; - static constexpr const char* title = "Watch face"; - static constexpr const char* symbol = Symbols::home; - std::unique_ptr CreateScreen1(); - std::unique_ptr CreateScreen2(); + ScreenList screens; + + lv_obj_t* checkboxes[optionsPerScreen]; }; } } diff --git a/src/displayapp/widgets/PageIndicator.cpp b/src/displayapp/widgets/PageIndicator.cpp index 84d03e7ed0..2f4996876a 100644 --- a/src/displayapp/widgets/PageIndicator.cpp +++ b/src/displayapp/widgets/PageIndicator.cpp @@ -3,27 +3,28 @@ using namespace Pinetime::Applications::Widgets; -PageIndicator::PageIndicator(uint8_t nCurrentScreen, uint8_t nScreens) : nCurrentScreen {nCurrentScreen}, nScreens {nScreens} { +PageIndicator::PageIndicator(uint8_t nCurrentScreen, uint8_t nScreens, bool horizontal) + : nCurrentScreen {nCurrentScreen}, nScreens {nScreens}, horizontal {horizontal} { } void PageIndicator::Create() { - pageIndicatorBasePoints[0].x = LV_HOR_RES - 1; - pageIndicatorBasePoints[0].y = 0; - pageIndicatorBasePoints[1].x = LV_HOR_RES - 1; - pageIndicatorBasePoints[1].y = LV_VER_RES; + pageIndicatorBasePoints[0].x = horizontal ? 0 : LV_HOR_RES - 1; + pageIndicatorBasePoints[0].y = horizontal ? LV_VER_RES - 1 : 0; + pageIndicatorBasePoints[1].x = horizontal ? LV_HOR_RES : LV_HOR_RES - 1; + pageIndicatorBasePoints[1].y = horizontal ? LV_VER_RES - 1 : LV_VER_RES; pageIndicatorBase = lv_line_create(lv_scr_act(), nullptr); lv_obj_set_style_local_line_width(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3); lv_obj_set_style_local_line_color(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, Colors::bgDark); lv_line_set_points(pageIndicatorBase, pageIndicatorBasePoints, 2); - const int16_t indicatorSize = LV_VER_RES / nScreens; + const int16_t indicatorSize = (horizontal ? LV_HOR_RES : LV_VER_RES) / nScreens; const int16_t indicatorPos = indicatorSize * nCurrentScreen; - pageIndicatorPoints[0].x = LV_HOR_RES - 1; - pageIndicatorPoints[0].y = indicatorPos; - pageIndicatorPoints[1].x = LV_HOR_RES - 1; - pageIndicatorPoints[1].y = indicatorPos + indicatorSize; + pageIndicatorPoints[0].x = horizontal ? indicatorPos : LV_HOR_RES - 1; + pageIndicatorPoints[0].y = horizontal ? LV_VER_RES - 1 : indicatorPos; + pageIndicatorPoints[1].x = horizontal ? indicatorPos + indicatorSize : LV_HOR_RES - 1; + pageIndicatorPoints[1].y = horizontal ? LV_VER_RES - 1 : indicatorPos + indicatorSize; pageIndicator = lv_line_create(lv_scr_act(), nullptr); lv_obj_set_style_local_line_width(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3); diff --git a/src/displayapp/widgets/PageIndicator.h b/src/displayapp/widgets/PageIndicator.h index 8484735e58..ecafda0b57 100644 --- a/src/displayapp/widgets/PageIndicator.h +++ b/src/displayapp/widgets/PageIndicator.h @@ -6,12 +6,13 @@ namespace Pinetime { namespace Widgets { class PageIndicator { public: - PageIndicator(uint8_t nCurrentScreen, uint8_t nScreens); + PageIndicator(uint8_t nCurrentScreen, uint8_t nScreens, bool horizontal = false); void Create(); private: uint8_t nCurrentScreen; uint8_t nScreens; + bool horizontal; lv_point_t pageIndicatorBasePoints[2]; lv_point_t pageIndicatorPoints[2];