Implement UI scaling and more (#501)
**New Features** * Runtime font accessors and new symbol fonts for text, launcher, statusbar, and shared icons. * Added font height base setting to device.properties * Text fonts now have 3 sizes: small, default, large **Improvements** * Renamed `UiScale` to `UiDensity` * Statusbar, toolbar and many UI components now compute heights and spacing from fonts/density. * SSD1306 initialization sequence refined for more stable startup. * Multiple image assets replaced by symbol-font rendering. * Many layout improvements related to density, font scaling and icon scaling * Updated folder name capitalization for newer style
This commit is contained in:
committed by
GitHub
parent
72c9b2b113
commit
9a11e6f47b
@@ -18,7 +18,7 @@ list(APPEND REQUIRES_LIST
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
|
||||
list(APPEND REQUIRES_LIST
|
||||
PlatformEsp32
|
||||
platform-esp32
|
||||
driver
|
||||
elf_loader
|
||||
QRCode
|
||||
@@ -42,7 +42,7 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
else ()
|
||||
|
||||
list(APPEND REQUIRES_LIST
|
||||
PlatformPosix
|
||||
platform-posix
|
||||
freertos_kernel
|
||||
cJSON
|
||||
lvgl
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/hal/sdcard/SdCardDevice.h>
|
||||
#include <memory>
|
||||
#include <tactility/hal/Device.h>
|
||||
#include <vector>
|
||||
|
||||
namespace tt::hal {
|
||||
|
||||
@@ -11,9 +13,9 @@ typedef std::vector<std::shared_ptr<Device>> DeviceVector;
|
||||
typedef std::shared_ptr<Device> (*CreateDevice)();
|
||||
|
||||
/** Affects LVGL widget style */
|
||||
enum class UiScale {
|
||||
enum class UiDensity {
|
||||
/** Ideal for very small non-touch screen devices (e.g. Waveshare S3 LCD 1.3") */
|
||||
Smallest,
|
||||
Compact,
|
||||
/** Nothing was changed in the LVGL UI/UX */
|
||||
Default
|
||||
};
|
||||
@@ -25,7 +27,7 @@ struct Configuration {
|
||||
const InitBoot initBoot = nullptr;
|
||||
|
||||
/** Modify LVGL widget size */
|
||||
const UiScale uiScale = UiScale::Default;
|
||||
const UiDensity uiDensity = UiDensity::Default;
|
||||
|
||||
const std::function<DeviceVector()> createDevices = [] { return DeviceVector(); };
|
||||
};
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
namespace tt::lvgl {
|
||||
|
||||
constexpr auto STATUSBAR_ICON_LIMIT = 8;
|
||||
constexpr auto STATUSBAR_ICON_SIZE = 20;
|
||||
constexpr auto STATUSBAR_HEIGHT = STATUSBAR_ICON_SIZE + 2;
|
||||
|
||||
/** Create a statusbar widget. Needs to be called with LVGL lock. */
|
||||
lv_obj_t* statusbar_create(lv_obj_t* parent);
|
||||
@@ -28,4 +26,6 @@ void statusbar_icon_set_image(int8_t id, const std::string& image);
|
||||
/** Update the visibility for an icon on the statusbar. Does not need to be called with LVGL lock. */
|
||||
void statusbar_icon_set_visibility(int8_t id, bool visible);
|
||||
|
||||
int statusbar_get_height();
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "../app/AppContext.h"
|
||||
#include "Tactility/Tactility.h"
|
||||
|
||||
#include <lvgl.h>
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
#define TOOLBAR_ACTION_LIMIT 4
|
||||
|
||||
/** Create a toolbar widget that shows the app name as title */
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <Tactility/file/FileLock.h>
|
||||
#include <Tactility/file/PropertiesFile.h>
|
||||
#include <Tactility/hal/HalPrivate.h>
|
||||
#include <Tactility/hal/sdcard/SdCardDevice.h>
|
||||
#include <Tactility/Logger.h>
|
||||
#include <Tactility/LogMessages.h>
|
||||
#include <Tactility/lvgl/LvglPrivate.h>
|
||||
|
||||
@@ -21,7 +21,7 @@ class AppListApp final : public App {
|
||||
const void* icon = !manifest->appIcon.empty() ? manifest->appIcon.c_str() : LVGL_SYMBOL_TOOLBAR;
|
||||
lv_obj_t* btn = lv_list_add_button(list, icon, manifest->appName.c_str());
|
||||
lv_obj_t* image = lv_obj_get_child(btn, 0);
|
||||
lv_obj_set_style_text_font(image, LVGL_SYMBOL_FONT_DEFAULT, LV_PART_MAIN);
|
||||
lv_obj_set_style_text_font(image, lvgl_get_shared_icon_font(), LV_PART_MAIN);
|
||||
lv_obj_add_event_cb(btn, &onAppPressed, LV_EVENT_SHORT_CLICKED, manifest.get());
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ class AppSettingsApp final : public App {
|
||||
const void* icon = !manifest->appIcon.empty() ? manifest->appIcon.c_str() : LVGL_SYMBOL_TOOLBAR;
|
||||
lv_obj_t* btn = lv_list_add_button(list, icon, manifest->appName.c_str());
|
||||
lv_obj_t* image = lv_obj_get_child(btn, 0);
|
||||
lv_obj_set_style_text_font(image, LVGL_SYMBOL_FONT_DEFAULT, LV_PART_MAIN);
|
||||
lv_obj_set_style_text_font(image, lvgl_get_shared_icon_font(), LV_PART_MAIN);
|
||||
lv_obj_add_event_cb(btn, &onAppPressed, LV_EVENT_SHORT_CLICKED, manifest.get());
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
|
||||
void onShow(AppContext& app, lv_obj_t* parent) override {
|
||||
auto* display = lv_obj_get_display(parent);
|
||||
int32_t parent_height = lv_display_get_vertical_resolution(display) - lvgl::STATUSBAR_HEIGHT;
|
||||
int32_t parent_height = lv_display_get_vertical_resolution(display) - lvgl::statusbar_get_height();
|
||||
|
||||
lv_obj_add_event_cb(parent, onContinuePressed, LV_EVENT_SHORT_CLICKED, nullptr);
|
||||
auto* top_label = lv_label_create(parent);
|
||||
|
||||
@@ -122,7 +122,7 @@ public:
|
||||
|
||||
void onShow(AppContext& app, lv_obj_t* parent) override {
|
||||
displaySettings = settings::display::loadOrGetDefault();
|
||||
auto ui_scale = hal::getConfiguration()->uiScale;
|
||||
auto ui_density = hal::getConfiguration()->uiDensity;
|
||||
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT);
|
||||
@@ -144,7 +144,7 @@ public:
|
||||
lv_obj_set_size(brightness_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||
lv_obj_set_style_pad_hor(brightness_wrapper, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_border_width(brightness_wrapper, 0, LV_STATE_DEFAULT);
|
||||
if (ui_scale != hal::UiScale::Smallest) {
|
||||
if (ui_density != hal::UiDensity::Compact) {
|
||||
lv_obj_set_style_pad_ver(brightness_wrapper, 4, LV_STATE_DEFAULT);
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ public:
|
||||
lv_obj_set_size(gamma_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||
lv_obj_set_style_pad_hor(gamma_wrapper, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_border_width(gamma_wrapper, 0, LV_STATE_DEFAULT);
|
||||
if (ui_scale != hal::UiScale::Smallest) {
|
||||
if (ui_density != hal::UiDensity::Compact) {
|
||||
lv_obj_set_style_pad_ver(gamma_wrapper, 4, LV_STATE_DEFAULT);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,21 +17,22 @@ namespace tt::app::launcher {
|
||||
|
||||
static const auto LOGGER = Logger("Launcher");
|
||||
|
||||
static int getButtonSize(hal::UiScale scale) {
|
||||
if (scale == hal::UiScale::Smallest) {
|
||||
return 36; // icon size
|
||||
static uint32_t getButtonPadding(hal::UiDensity density, uint32_t buttonSize) {
|
||||
if (density == hal::UiDensity::Compact) {
|
||||
return 0;
|
||||
} else {
|
||||
return 56;
|
||||
return buttonSize / 8;
|
||||
}
|
||||
}
|
||||
|
||||
class LauncherApp final : public App {
|
||||
|
||||
static lv_obj_t* createAppButton(lv_obj_t* parent, hal::UiScale uiScale, const char* imageFile, const char* appId, int32_t itemMargin, bool isLandscape) {
|
||||
auto button_size = getButtonSize(uiScale);
|
||||
|
||||
static lv_obj_t* createAppButton(lv_obj_t* parent, hal::UiDensity uiDensity, const char* imageFile, const char* appId, int32_t itemMargin, bool isLandscape) {
|
||||
const auto button_size = lvgl_get_launcher_icon_font_height();
|
||||
const auto button_padding = getButtonPadding(uiDensity, button_size);
|
||||
auto* apps_button = lv_button_create(parent);
|
||||
lv_obj_set_style_pad_all(apps_button, 0, LV_STATE_DEFAULT);
|
||||
|
||||
lv_obj_set_style_pad_all(apps_button, static_cast<int32_t>(button_padding), LV_STATE_DEFAULT);
|
||||
if (isLandscape) {
|
||||
lv_obj_set_style_margin_hor(apps_button, itemMargin, LV_STATE_DEFAULT);
|
||||
} else {
|
||||
@@ -43,7 +44,7 @@ class LauncherApp final : public App {
|
||||
|
||||
// create the image first
|
||||
auto* button_image = lv_image_create(apps_button);
|
||||
lv_obj_set_style_text_font(button_image, LVGL_SYMBOL_FONT_LAUNCHER, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_text_font(button_image, lvgl_get_launcher_icon_font(), LV_STATE_DEFAULT);
|
||||
lv_image_set_src(button_image, imageFile);
|
||||
lv_obj_set_style_text_color(button_image, lv_theme_get_color_primary(button_image), LV_STATE_DEFAULT);
|
||||
|
||||
@@ -59,8 +60,7 @@ class LauncherApp final : public App {
|
||||
lv_obj_set_style_image_recolor_opa(button_image, LV_OPA_COVER, LV_STATE_DEFAULT);
|
||||
#endif
|
||||
|
||||
// Ensure buttons are still tappable when the asset fails to load
|
||||
// Icon images are 40x40, so we get some extra padding too
|
||||
// Ensure it's square (Material Symbols are slightly wider than tall)
|
||||
lv_obj_set_size(button_image, button_size, button_size);
|
||||
|
||||
lv_obj_add_event_cb(apps_button, onAppPressed, LV_EVENT_SHORT_CLICKED, (void*)appId);
|
||||
@@ -119,20 +119,18 @@ public:
|
||||
void onShow(AppContext& app, lv_obj_t* parent) override {
|
||||
auto* buttons_wrapper = lv_obj_create(parent);
|
||||
|
||||
auto ui_scale = hal::getConfiguration()->uiScale;
|
||||
auto button_size = getButtonSize(ui_scale);
|
||||
auto ui_density = hal::getConfiguration()->uiDensity;
|
||||
const auto button_size = lvgl_get_launcher_icon_font_height();
|
||||
const auto button_padding = getButtonPadding(ui_density, button_size);
|
||||
const auto total_button_size = button_size + (button_padding * 2);
|
||||
|
||||
lv_obj_align(buttons_wrapper, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_obj_set_size(buttons_wrapper, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
|
||||
lv_obj_set_style_border_width(buttons_wrapper, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_flex_grow(buttons_wrapper, 1);
|
||||
|
||||
// Fix for button selection (problem with UiScale::Small on Cardputer)
|
||||
if (!hal::hasDevice(hal::Device::Type::Touch)) {
|
||||
lv_obj_set_style_pad_all(buttons_wrapper, 6, LV_STATE_DEFAULT);
|
||||
} else {
|
||||
lv_obj_set_style_pad_all(buttons_wrapper, 0, LV_STATE_DEFAULT);
|
||||
}
|
||||
// Fix for button selection
|
||||
lv_obj_set_style_pad_all(buttons_wrapper, 6, LV_STATE_DEFAULT);
|
||||
|
||||
const auto* display = lv_obj_get_display(parent);
|
||||
const auto horizontal_px = lv_display_get_horizontal_resolution(display);
|
||||
@@ -146,16 +144,16 @@ public:
|
||||
|
||||
int32_t margin;
|
||||
if (is_landscape_display) {
|
||||
const int32_t available_width = std::max<int32_t>(0, lv_display_get_horizontal_resolution(display) - (3 * button_size));
|
||||
margin = std::min<int32_t>(available_width / 16, button_size);
|
||||
const int32_t available_width = std::max<int32_t>(0, lv_display_get_horizontal_resolution(display) - (3 * total_button_size));
|
||||
margin = std::min<int32_t>(available_width / 16, total_button_size / 2);
|
||||
} else {
|
||||
const int32_t available_height = std::max<int32_t>(0, lv_display_get_vertical_resolution(display) - (3 * button_size));
|
||||
margin = std::min<int32_t>(available_height / 16, button_size);
|
||||
const int32_t available_height = std::max<int32_t>(0, lv_display_get_vertical_resolution(display) - (3 * total_button_size));
|
||||
margin = std::min<int32_t>(available_height / 16, total_button_size / 2);
|
||||
}
|
||||
|
||||
createAppButton(buttons_wrapper, ui_scale, LVGL_SYMBOL_APPS, "AppList", margin, is_landscape_display);
|
||||
createAppButton(buttons_wrapper, ui_scale, LVGL_SYMBOL_FOLDER, "Files", margin, is_landscape_display);
|
||||
createAppButton(buttons_wrapper, ui_scale, LVGL_SYMBOL_SETTINGS, "Settings", margin, is_landscape_display);
|
||||
createAppButton(buttons_wrapper, ui_density, LVGL_SYMBOL_APPS, "AppList", margin, is_landscape_display);
|
||||
createAppButton(buttons_wrapper, ui_density, LVGL_SYMBOL_FOLDER, "Files", margin, is_landscape_display);
|
||||
createAppButton(buttons_wrapper, ui_density, LVGL_SYMBOL_SETTINGS, "Settings", margin, is_landscape_display);
|
||||
|
||||
if (shouldShowPowerButton()) {
|
||||
auto* power_button = lv_btn_create(parent);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#include <Tactility/Tactility.h>
|
||||
|
||||
#include <Tactility/RecursiveMutex.h>
|
||||
#include <Tactility/StringUtils.h>
|
||||
#include <Tactility/app/localesettings/TextResources.h>
|
||||
#include <Tactility/lvgl/Toolbar.h>
|
||||
#include <Tactility/service/loader/Loader.h>
|
||||
#include <Tactility/settings/Time.h>
|
||||
#include <Tactility/StringUtils.h>
|
||||
#include <Tactility/settings/Language.h>
|
||||
#include <Tactility/settings/SystemSettings.h>
|
||||
|
||||
@@ -85,8 +86,6 @@ class LocaleSettingsApp final : public App {
|
||||
public:
|
||||
|
||||
void onShow(AppContext& app, lv_obj_t* parent) override {
|
||||
auto ui_scale = hal::getConfiguration()->uiScale;
|
||||
|
||||
textResources.load();
|
||||
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
#include <Tactility/Tactility.h>
|
||||
#include <Tactility/TactilityConfig.h>
|
||||
|
||||
#include <Tactility/Timer.h>
|
||||
#include <Tactility/kernel/Kernel.h>
|
||||
|
||||
#if TT_FEATURE_SCREENSHOT_ENABLED
|
||||
|
||||
#include <Tactility/app/App.h>
|
||||
#include <Tactility/app/AppManifest.h>
|
||||
#include <Tactility/hal/sdcard/SdCardDevice.h>
|
||||
#include <Tactility/kernel/Platform.h>
|
||||
#include <Tactility/Logger.h>
|
||||
#include <Tactility/lvgl/Lvgl.h>
|
||||
#include <Tactility/lvgl/LvglSync.h>
|
||||
#include <Tactility/lvgl/Toolbar.h>
|
||||
#include <Tactility/service/screenshot/Screenshot.h>
|
||||
#include <Tactility/Timer.h>
|
||||
|
||||
#include <tactility/lvgl_symbols_shared.h>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ static void createWidget(const std::shared_ptr<AppManifest>& manifest, void* par
|
||||
const void* icon = !manifest->appIcon.empty() ? manifest->appIcon.c_str() : LVGL_SYMBOL_TOOLBAR;
|
||||
auto* btn = lv_list_add_button(list, icon, manifest->appName.c_str());
|
||||
lv_obj_t* image = lv_obj_get_child(btn, 0);
|
||||
lv_obj_set_style_text_font(image, LVGL_SYMBOL_FONT_DEFAULT, LV_PART_MAIN);
|
||||
lv_obj_set_style_text_font(image, lvgl_get_shared_icon_font(), LV_PART_MAIN);
|
||||
lv_obj_add_event_cb(btn, &onAppPressed, LV_EVENT_SHORT_CLICKED, (void*)manifest.get());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
#include <Tactility/TactilityConfig.h>
|
||||
#include <Tactility/lvgl/Toolbar.h>
|
||||
#include <Tactility/lvgl/LvglSync.h>
|
||||
#include <Tactility/lvgl/Toolbar.h>
|
||||
|
||||
#include <Tactility/Assets.h>
|
||||
#include <tactility/hal/Device.h>
|
||||
#include <Tactility/hal/sdcard/SdCardDevice.h>
|
||||
#include <Tactility/Tactility.h>
|
||||
#include <Tactility/Timer.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <format>
|
||||
#include <lvgl.h>
|
||||
#include <tactility/lvgl_fonts.h>
|
||||
#include <tactility/hal/Device.h>
|
||||
#include <tactility/lvgl_symbols_shared.h>
|
||||
#include <utility>
|
||||
#include <cstring>
|
||||
@@ -139,7 +140,8 @@ static MemoryBarWidgets createMemoryBar(lv_obj_t* parent, const char* label) {
|
||||
|
||||
auto* left_label = lv_label_create(container);
|
||||
lv_label_set_text(left_label, label);
|
||||
lv_obj_set_width(left_label, 60);
|
||||
auto label_width = 6 * lvgl_get_text_font_height(FONT_SIZE_DEFAULT);
|
||||
lv_obj_set_width(left_label, label_width);
|
||||
|
||||
auto* bar = lv_bar_create(container);
|
||||
lv_obj_set_flex_grow(bar, 1);
|
||||
@@ -148,7 +150,7 @@ static MemoryBarWidgets createMemoryBar(lv_obj_t* parent, const char* label) {
|
||||
lv_obj_set_width(bottom_label, LV_PCT(100));
|
||||
lv_obj_set_style_text_align(bottom_label, LV_TEXT_ALIGN_RIGHT, 0);
|
||||
|
||||
if (hal::getConfiguration()->uiScale == hal::UiScale::Smallest) {
|
||||
if (hal::getConfiguration()->uiDensity == hal::UiDensity::Compact) {
|
||||
lv_obj_set_style_pad_bottom(bottom_label, 2, LV_STATE_DEFAULT);
|
||||
} else {
|
||||
lv_obj_set_style_pad_bottom(bottom_label, 12, LV_STATE_DEFAULT);
|
||||
@@ -436,7 +438,6 @@ class SystemInfoApp final : public App {
|
||||
// Summary
|
||||
auto* summary_label = lv_label_create(psramContainer);
|
||||
lv_label_set_text(summary_label, "PSRAM Usage Summary");
|
||||
lv_obj_set_style_text_font(summary_label, &lv_font_montserrat_14, 0);
|
||||
lv_obj_set_style_pad_bottom(summary_label, 8, 0);
|
||||
|
||||
// Current usage
|
||||
@@ -477,7 +478,6 @@ class SystemInfoApp final : public App {
|
||||
// PSRAM Configuration section
|
||||
auto* config_header = lv_label_create(psramContainer);
|
||||
lv_label_set_text(config_header, "PSRAM Configuration");
|
||||
lv_obj_set_style_text_font(config_header, &lv_font_montserrat_14, 0);
|
||||
lv_obj_set_style_pad_bottom(config_header, 8, 0);
|
||||
|
||||
// Get threshold from sdkconfig
|
||||
@@ -514,7 +514,6 @@ class SystemInfoApp final : public App {
|
||||
// Known PSRAM consumers header
|
||||
auto* consumers_label = lv_label_create(psramContainer);
|
||||
lv_label_set_text(consumers_label, "PSRAM Allocation Strategy");
|
||||
lv_obj_set_style_text_font(consumers_label, &lv_font_montserrat_14, 0);
|
||||
lv_obj_set_style_pad_bottom(consumers_label, 8, 0);
|
||||
|
||||
// Explain what's in PSRAM
|
||||
@@ -554,7 +553,6 @@ class SystemInfoApp final : public App {
|
||||
// App behavior explanation
|
||||
auto* app_behavior_label = lv_label_create(psramContainer);
|
||||
lv_label_set_text(app_behavior_label, "App Memory Behavior");
|
||||
lv_obj_set_style_text_font(app_behavior_label, &lv_font_montserrat_14, 0);
|
||||
lv_obj_set_style_pad_bottom(app_behavior_label, 8, 0);
|
||||
|
||||
auto* app_note1 = lv_label_create(psramContainer);
|
||||
@@ -585,7 +583,8 @@ class SystemInfoApp final : public App {
|
||||
|
||||
auto* tabview = lv_tabview_create(wrapper);
|
||||
lv_tabview_set_tab_bar_position(tabview, LV_DIR_LEFT);
|
||||
lv_tabview_set_tab_bar_size(tabview, 80);
|
||||
auto tab_bar_width = 6 * lvgl_get_text_font_height(FONT_SIZE_DEFAULT);
|
||||
lv_tabview_set_tab_bar_size(tabview, tab_bar_width);
|
||||
|
||||
// Create tabs
|
||||
auto* memory_tab = createTab(tabview, "Memory");
|
||||
@@ -645,7 +644,6 @@ class SystemInfoApp final : public App {
|
||||
// CPU tab - summary at top
|
||||
cpuSummaryLabel = lv_label_create(cpu_tab);
|
||||
lv_label_set_text(cpuSummaryLabel, "Overall CPU Usage: --.-%");
|
||||
lv_obj_set_style_text_font(cpuSummaryLabel, &lv_font_montserrat_14, 0);
|
||||
lv_obj_set_style_pad_bottom(cpuSummaryLabel, 4, 0);
|
||||
|
||||
taskCountLabel = lv_label_create(cpu_tab);
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
#include <tactility/lvgl_fonts.h>
|
||||
#include <tactility/lvgl_symbols_shared.h>
|
||||
|
||||
#include <Tactility/app/AppContext.h>
|
||||
#include <Tactility/app/AppManifest.h>
|
||||
#include <Tactility/app/AppPaths.h>
|
||||
#include <Tactility/app/timezone/TimeZone.h>
|
||||
#include <Tactility/Logger.h>
|
||||
|
||||
#include <Tactility/LogMessages.h>
|
||||
#include <Tactility/lvgl/Lvgl.h>
|
||||
#include <Tactility/lvgl/Toolbar.h>
|
||||
#include <Tactility/lvgl/LvglSync.h>
|
||||
#include <Tactility/Logger.h>
|
||||
#include <Tactility/MountPoints.h>
|
||||
#include <Tactility/service/loader/Loader.h>
|
||||
#include <Tactility/StringUtils.h>
|
||||
#include <Tactility/Timer.h>
|
||||
#include <Tactility/lvgl/LvglSync.h>
|
||||
#include <Tactility/lvgl/Toolbar.h>
|
||||
#include <Tactility/service/loader/Loader.h>
|
||||
|
||||
#include <lvgl.h>
|
||||
#include <memory>
|
||||
@@ -202,10 +204,8 @@ public:
|
||||
lv_obj_set_style_margin_left(icon, 8, 0);
|
||||
lv_obj_set_style_image_recolor_opa(icon, 255, 0);
|
||||
lv_obj_set_style_image_recolor(icon, lv_theme_get_color_primary(parent), 0);
|
||||
|
||||
std::string icon_path = lvgl::PATH_PREFIX + app.getPaths()->getAssetsPath("search.png");
|
||||
lv_image_set_src(icon, icon_path.c_str());
|
||||
lv_obj_set_style_image_recolor(icon, lv_theme_get_color_primary(parent), 0);
|
||||
lv_obj_set_style_text_font(icon, lvgl_get_shared_icon_font(), LV_STATE_DEFAULT);
|
||||
lv_image_set_src(icon, LVGL_SYMBOL_SEARCH);
|
||||
|
||||
auto* textarea = lv_textarea_create(search_wrapper);
|
||||
lv_textarea_set_placeholder_text(textarea, "e.g. Europe/Amsterdam");
|
||||
|
||||
@@ -109,7 +109,7 @@ class TrackballSettingsApp final : public App {
|
||||
public:
|
||||
void onShow(AppContext& app, lv_obj_t* parent) override {
|
||||
tbSettings = settings::trackball::loadOrGetDefault();
|
||||
auto ui_scale = hal::getConfiguration()->uiScale;
|
||||
auto ui_density = hal::getConfiguration()->uiDensity;
|
||||
updated = false;
|
||||
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
@@ -154,7 +154,7 @@ public:
|
||||
lv_obj_set_size(enc_sens_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||
lv_obj_set_style_pad_hor(enc_sens_wrapper, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_border_width(enc_sens_wrapper, 0, LV_STATE_DEFAULT);
|
||||
if (ui_scale != hal::UiScale::Smallest) {
|
||||
if (ui_density != hal::UiDensity::Compact) {
|
||||
lv_obj_set_style_pad_ver(enc_sens_wrapper, 4, LV_STATE_DEFAULT);
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ public:
|
||||
lv_obj_set_size(ptr_sens_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||
lv_obj_set_style_pad_hor(ptr_sens_wrapper, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_border_width(ptr_sens_wrapper, 0, LV_STATE_DEFAULT);
|
||||
if (ui_scale != hal::UiScale::Smallest) {
|
||||
if (ui_density != hal::UiDensity::Compact) {
|
||||
lv_obj_set_style_pad_ver(ptr_sens_wrapper, 4, LV_STATE_DEFAULT);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <Tactility/lvgl/Toolbar.h>
|
||||
#include <Tactility/service/wifi/Wifi.h>
|
||||
#include <Tactility/service/wifi/WifiSettings.h>
|
||||
#include <Tactility/Tactility.h>
|
||||
|
||||
#include <format>
|
||||
#include <string>
|
||||
@@ -161,7 +162,7 @@ void View::updateNetworkList() {
|
||||
lv_obj_add_event_cb(enable_on_boot_switch, onEnableOnBootSwitchChanged, LV_EVENT_VALUE_CHANGED, bindings);
|
||||
lv_obj_add_event_cb(enable_on_boot_wrapper, onEnableOnBootParentClicked, LV_EVENT_SHORT_CLICKED, enable_on_boot_switch);
|
||||
|
||||
if (hal::getConfiguration()->uiScale == hal::UiScale::Smallest) {
|
||||
if (hal::getConfiguration()->uiDensity == hal::UiDensity::Compact) {
|
||||
lv_obj_set_style_pad_ver(enable_on_boot_wrapper, 2, LV_STATE_DEFAULT);
|
||||
} else {
|
||||
lv_obj_set_style_pad_ver(enable_on_boot_wrapper, 8, LV_STATE_DEFAULT);
|
||||
|
||||
@@ -6,13 +6,15 @@
|
||||
#include <Tactility/RecursiveMutex.h>
|
||||
#include <Tactility/Tactility.h>
|
||||
#include <Tactility/Timer.h>
|
||||
#include <tactility/check.h>
|
||||
#include <Tactility/kernel/SystemEvents.h>
|
||||
#include <Tactility/lvgl/LvglSync.h>
|
||||
#include <Tactility/lvgl/Statusbar.h>
|
||||
#include <Tactility/lvgl/Style.h>
|
||||
#include <Tactility/settings/Time.h>
|
||||
|
||||
#include <tactility/check.h>
|
||||
#include <tactility/lvgl_fonts.h>
|
||||
|
||||
#include <lvgl.h>
|
||||
|
||||
namespace tt::lvgl {
|
||||
@@ -88,7 +90,7 @@ static void onUpdateTime() {
|
||||
}
|
||||
}
|
||||
|
||||
static const lv_obj_class_t statusbar_class = {
|
||||
static lv_obj_class_t statusbar_class = {
|
||||
.base_class = &lv_obj_class,
|
||||
.constructor_cb = &statusbar_constructor,
|
||||
.destructor_cb = &statusbar_destructor,
|
||||
@@ -96,7 +98,7 @@ static const lv_obj_class_t statusbar_class = {
|
||||
.user_data = nullptr,
|
||||
.name = nullptr,
|
||||
.width_def = LV_PCT(100),
|
||||
.height_def = STATUSBAR_HEIGHT,
|
||||
.height_def = 20,
|
||||
.editable = false,
|
||||
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
|
||||
.instance_size = sizeof(Statusbar),
|
||||
@@ -157,10 +159,11 @@ static void update_icon(lv_obj_t* image, const StatusbarIcon* icon) {
|
||||
}
|
||||
|
||||
lv_obj_t* statusbar_create(lv_obj_t* parent) {
|
||||
statusbar_class.height_def = statusbar_get_height();
|
||||
lv_obj_t* obj = lv_obj_class_create_obj(&statusbar_class, parent);
|
||||
lv_obj_class_init_obj(obj);
|
||||
|
||||
auto* statusbar = (Statusbar*)obj;
|
||||
auto* statusbar = reinterpret_cast<Statusbar*>(obj);
|
||||
|
||||
lv_obj_set_width(obj, LV_PCT(100));
|
||||
lv_obj_set_style_pad_ver(obj, 0, LV_STATE_DEFAULT);
|
||||
@@ -168,6 +171,10 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) {
|
||||
lv_obj_center(obj);
|
||||
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
|
||||
lv_obj_set_flex_align(obj, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
||||
auto icon_size = lvgl_get_statusbar_icon_font_height();
|
||||
auto ui_density = hal::getConfiguration()->uiDensity;
|
||||
auto icon_padding = (ui_density != hal::UiDensity::Compact) ? static_cast<uint32_t>(icon_size * 0.2f) : 2;
|
||||
lv_obj_set_style_pad_column(obj, icon_padding, LV_STATE_DEFAULT);
|
||||
|
||||
statusbar->time = lv_label_create(obj);
|
||||
lv_obj_set_style_text_color(statusbar->time, lv_color_white(), LV_STATE_DEFAULT);
|
||||
@@ -182,7 +189,8 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) {
|
||||
statusbar_data.mutex.lock(kernel::MAX_TICKS);
|
||||
for (int i = 0; i < STATUSBAR_ICON_LIMIT; ++i) {
|
||||
auto* image = lv_image_create(obj);
|
||||
lv_obj_set_size(image, STATUSBAR_ICON_SIZE, STATUSBAR_ICON_SIZE);
|
||||
lv_obj_set_size(image, icon_size, icon_size); // regular padding doesn't work
|
||||
lv_obj_set_style_text_font(image, lvgl_get_statusbar_icon_font(), LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_pad_all(image, 0, LV_STATE_DEFAULT);
|
||||
obj_set_style_bg_blacken(image);
|
||||
statusbar->icons[i] = image;
|
||||
@@ -190,7 +198,6 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) {
|
||||
update_icon(image, &(statusbar_data.icons[i]));
|
||||
}
|
||||
statusbar_data.mutex.unlock();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -298,4 +305,10 @@ void statusbar_icon_set_visibility(int8_t id, bool visible) {
|
||||
statusbar_data.pubsub->publish(nullptr);
|
||||
}
|
||||
|
||||
int statusbar_get_height() {
|
||||
const auto icon_size = lvgl_get_statusbar_icon_font_height();
|
||||
const auto vertical_padding = static_cast<uint32_t>((static_cast<float>(icon_size) * 0.1f));
|
||||
return icon_size + (2 * vertical_padding);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -1,40 +1,54 @@
|
||||
#define LV_USE_PRIVATE_API 1 // For actual lv_obj_t declaration
|
||||
|
||||
#include <Tactility/Tactility.h>
|
||||
#include <Tactility/lvgl/Toolbar.h>
|
||||
|
||||
#include <tactility/check.h>
|
||||
#include <Tactility/lvgl/Spinner.h>
|
||||
#include <Tactility/service/loader/Loader.h>
|
||||
|
||||
#include <tactility/check.h>
|
||||
#include <tactility/lvgl_fonts.h>
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
static int getToolbarHeight(hal::UiScale uiScale) {
|
||||
if (uiScale == hal::UiScale::Smallest) {
|
||||
return 22;
|
||||
static uint32_t getToolbarHeight(hal::UiDensity uiDensity) {
|
||||
if (uiDensity == hal::UiDensity::Compact) {
|
||||
return lvgl_get_text_font_height(FONT_SIZE_DEFAULT) * 1.4f;
|
||||
} else {
|
||||
return 40;
|
||||
return lvgl_get_text_font_height(FONT_SIZE_LARGE) * 2.2f;
|
||||
}
|
||||
}
|
||||
|
||||
static const _lv_font_t* getToolbarFont(hal::UiScale uiScale) {
|
||||
if (uiScale == hal::UiScale::Smallest) {
|
||||
return &lv_font_montserrat_14;
|
||||
static const _lv_font_t* getToolbarFont(hal::UiDensity uiDensity) {
|
||||
if (uiDensity == hal::UiDensity::Compact) {
|
||||
return lvgl_get_text_font(FONT_SIZE_DEFAULT);
|
||||
} else {
|
||||
return &lv_font_montserrat_18;
|
||||
return lvgl_get_text_font(FONT_SIZE_LARGE);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t getActionIconPadding(hal::UiDensity ui_density) {
|
||||
auto toolbar_height = getToolbarHeight(ui_density);
|
||||
// Minimal 8 pixels total padding for selection/animation (4+4 pixels)
|
||||
return (ui_density != hal::UiDensity::Compact) ? (uint32_t)(toolbar_height * 0.2f) : 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helps with button expansion and also with vertical alignment of content,
|
||||
* as the parent flex doesn't allow for vertical alignment
|
||||
*/
|
||||
static lv_obj_t* create_action_wrapper(lv_obj_t* parent) {
|
||||
static lv_obj_t* create_action_wrapper(lv_obj_t* parent, hal::UiDensity ui_density) {
|
||||
auto* wrapper = lv_obj_create(parent);
|
||||
lv_obj_set_size(wrapper, LV_SIZE_CONTENT, getToolbarHeight(hal::getConfiguration()->uiScale));
|
||||
lv_obj_set_style_pad_all(wrapper, 2, LV_STATE_DEFAULT); // For selection / click expansion
|
||||
auto toolbar_height = getToolbarHeight(ui_density);
|
||||
lv_obj_set_size(wrapper, LV_SIZE_CONTENT, toolbar_height);
|
||||
|
||||
auto icon_padding = getActionIconPadding(ui_density);
|
||||
auto icon_padding_half = icon_padding / 2;
|
||||
|
||||
lv_obj_set_style_pad_all(wrapper, icon_padding_half, LV_STATE_DEFAULT); // For selection and touch animation
|
||||
lv_obj_set_style_bg_opa(wrapper, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_border_width(wrapper, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_border_opa(wrapper, 0, LV_STATE_DEFAULT);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
@@ -49,7 +63,7 @@ typedef struct {
|
||||
|
||||
static void toolbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj);
|
||||
|
||||
static const lv_obj_class_t toolbar_class = {
|
||||
static lv_obj_class_t toolbar_class = {
|
||||
.base_class = &lv_obj_class,
|
||||
.constructor_cb = &toolbar_constructor,
|
||||
.destructor_cb = nullptr,
|
||||
@@ -57,7 +71,7 @@ static const lv_obj_class_t toolbar_class = {
|
||||
.user_data = nullptr,
|
||||
.name = nullptr,
|
||||
.width_def = LV_PCT(100),
|
||||
.height_def = LV_SIZE_CONTENT,
|
||||
.height_def = 0,
|
||||
.editable = false,
|
||||
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
|
||||
.instance_size = sizeof(Toolbar),
|
||||
@@ -70,16 +84,14 @@ static void stop_app(lv_event_t* event) {
|
||||
|
||||
static void toolbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) {
|
||||
LV_UNUSED(class_p);
|
||||
LV_TRACE_OBJ_CREATE("begin");
|
||||
lv_obj_remove_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
|
||||
LV_TRACE_OBJ_CREATE("finished");
|
||||
}
|
||||
|
||||
lv_obj_t* toolbar_create(lv_obj_t* parent, const std::string& title) {
|
||||
LV_LOG_INFO("begin");
|
||||
auto ui_scale = hal::getConfiguration()->uiScale;
|
||||
auto toolbar_height = getToolbarHeight(ui_scale);
|
||||
auto ui_density = hal::getConfiguration()->uiDensity;
|
||||
auto toolbar_height = getToolbarHeight(ui_density);
|
||||
toolbar_class.height_def = toolbar_height;
|
||||
lv_obj_t* obj = lv_obj_class_create_obj(&toolbar_class, parent);
|
||||
lv_obj_class_init_obj(obj);
|
||||
lv_obj_set_height(obj, toolbar_height);
|
||||
@@ -87,44 +99,40 @@ lv_obj_t* toolbar_create(lv_obj_t* parent, const std::string& title) {
|
||||
auto* toolbar = reinterpret_cast<Toolbar*>(obj);
|
||||
lv_obj_set_width(obj, LV_PCT(100));
|
||||
lv_obj_set_style_pad_all(obj, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_pad_column(obj, 0, LV_STATE_DEFAULT);
|
||||
|
||||
lv_obj_center(obj);
|
||||
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
|
||||
|
||||
auto* close_button_wrapper = lv_obj_create(obj);
|
||||
lv_obj_set_size(close_button_wrapper, LV_SIZE_CONTENT, toolbar_height);
|
||||
lv_obj_set_style_pad_all(close_button_wrapper, 2, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_border_width(close_button_wrapper, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_bg_opa(close_button_wrapper, 0, LV_STATE_DEFAULT);
|
||||
auto icon_padding = getActionIconPadding(ui_density);
|
||||
|
||||
auto* close_button_wrapper = create_action_wrapper(obj, ui_density);
|
||||
|
||||
toolbar->close_button = lv_button_create(close_button_wrapper);
|
||||
|
||||
if (ui_scale == hal::UiScale::Smallest) {
|
||||
lv_obj_set_size(toolbar->close_button, toolbar_height - 8, toolbar_height - 8);
|
||||
} else {
|
||||
lv_obj_set_size(toolbar->close_button, toolbar_height - 6, toolbar_height - 6);
|
||||
if (ui_density == hal::UiDensity::Compact) {
|
||||
lv_obj_set_style_bg_opa(toolbar->close_button, LV_OPA_TRANSP, LV_STATE_DEFAULT);
|
||||
}
|
||||
|
||||
lv_obj_set_size(toolbar->close_button, toolbar_height - icon_padding, toolbar_height - icon_padding);
|
||||
|
||||
lv_obj_set_style_pad_all(toolbar->close_button, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_align(toolbar->close_button, LV_ALIGN_CENTER, 0, 0);
|
||||
toolbar->close_button_image = lv_image_create(toolbar->close_button);
|
||||
lv_obj_align(toolbar->close_button_image, LV_ALIGN_CENTER, 0, 0);
|
||||
|
||||
auto* title_wrapper = lv_obj_create(obj);
|
||||
uint32_t title_left_padding = (ui_density != hal::UiDensity::Compact) ? icon_padding : 2;
|
||||
uint32_t title_right_padding = (ui_density != hal::UiDensity::Compact) ? (icon_padding / 2) : 2;
|
||||
lv_obj_set_size(title_wrapper, LV_SIZE_CONTENT, LV_PCT(100));
|
||||
lv_obj_set_style_bg_opa(title_wrapper, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_pad_all(title_wrapper, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_pad_left(title_wrapper, title_left_padding, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_pad_right(title_wrapper, title_right_padding, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_pad_ver(title_wrapper, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_border_width(title_wrapper, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_flex_grow(title_wrapper, 1);
|
||||
|
||||
if (ui_scale == hal::UiScale::Smallest) {
|
||||
lv_obj_set_style_pad_left(title_wrapper, 4, LV_STATE_DEFAULT);
|
||||
} else {
|
||||
lv_obj_set_style_pad_left(title_wrapper, 8, LV_STATE_DEFAULT);
|
||||
}
|
||||
|
||||
toolbar->title_label = lv_label_create(title_wrapper);
|
||||
lv_obj_set_style_text_font(toolbar->title_label, getToolbarFont(ui_scale), LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_text_font(toolbar->title_label, getToolbarFont(ui_density), LV_STATE_DEFAULT);
|
||||
lv_label_set_text(toolbar->title_label, title.c_str());
|
||||
lv_label_set_long_mode(toolbar->title_label, LV_LABEL_LONG_MODE_SCROLL);
|
||||
lv_obj_set_style_text_align(toolbar->title_label, LV_TEXT_ALIGN_LEFT, LV_STATE_DEFAULT);
|
||||
@@ -135,6 +143,7 @@ lv_obj_t* toolbar_create(lv_obj_t* parent, const std::string& title) {
|
||||
lv_obj_set_width(toolbar->action_container, LV_SIZE_CONTENT);
|
||||
lv_obj_set_flex_flow(toolbar->action_container, LV_FLEX_FLOW_ROW);
|
||||
lv_obj_set_style_pad_all(toolbar->action_container, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_pad_column(toolbar->action_container, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_border_width(toolbar->action_container, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_bg_opa(toolbar->action_container, 0, LV_STATE_DEFAULT);
|
||||
|
||||
@@ -170,19 +179,20 @@ lv_obj_t* toolbar_add_button_action(lv_obj_t* obj, const char* imageOrButton, bo
|
||||
check(toolbar->action_count < TOOLBAR_ACTION_LIMIT, "max actions reached");
|
||||
toolbar->action_count++;
|
||||
|
||||
auto ui_scale = hal::getConfiguration()->uiScale;
|
||||
auto toolbar_height = getToolbarHeight(ui_scale);
|
||||
auto ui_density = hal::getConfiguration()->uiDensity;
|
||||
auto toolbar_height = getToolbarHeight(ui_density);
|
||||
|
||||
auto* wrapper = create_action_wrapper(toolbar->action_container);
|
||||
auto* wrapper = create_action_wrapper(toolbar->action_container, ui_density);
|
||||
|
||||
auto* action_button = lv_button_create(wrapper);
|
||||
if (ui_scale == hal::UiScale::Smallest) {
|
||||
lv_obj_set_size(action_button, toolbar_height - 8, toolbar_height - 8);
|
||||
} else {
|
||||
lv_obj_set_size(action_button, toolbar_height - 6, toolbar_height - 6);
|
||||
}
|
||||
auto padding = getActionIconPadding(ui_density);
|
||||
|
||||
lv_obj_t* action_button = lv_button_create(wrapper);
|
||||
lv_obj_set_size(action_button, toolbar_height - padding, toolbar_height - padding);
|
||||
lv_obj_set_style_pad_all(action_button, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_align(action_button, LV_ALIGN_CENTER, 0, 0);
|
||||
if (ui_density == hal::UiDensity::Compact) {
|
||||
lv_obj_set_style_bg_opa(action_button, LV_OPA_TRANSP, LV_STATE_DEFAULT);
|
||||
}
|
||||
|
||||
lv_obj_add_event_cb(action_button, callback, LV_EVENT_SHORT_CLICKED, user_data);
|
||||
lv_obj_t* button_content;
|
||||
@@ -209,7 +219,8 @@ lv_obj_t* toolbar_add_text_button_action(lv_obj_t* obj, const char* text, lv_eve
|
||||
lv_obj_t* toolbar_add_switch_action(lv_obj_t* obj) {
|
||||
auto* toolbar = reinterpret_cast<Toolbar*>(obj);
|
||||
|
||||
auto* wrapper = create_action_wrapper(toolbar->action_container);
|
||||
auto ui_density = hal::getConfiguration()->uiDensity;
|
||||
auto* wrapper = create_action_wrapper(toolbar->action_container, ui_density);
|
||||
lv_obj_set_style_pad_hor(wrapper, 4, LV_STATE_DEFAULT);
|
||||
|
||||
lv_obj_t* widget = lv_switch_create(wrapper);
|
||||
@@ -220,7 +231,8 @@ lv_obj_t* toolbar_add_switch_action(lv_obj_t* obj) {
|
||||
lv_obj_t* toolbar_add_spinner_action(lv_obj_t* obj) {
|
||||
auto* toolbar = reinterpret_cast<Toolbar*>(obj);
|
||||
|
||||
auto* wrapper = create_action_wrapper(toolbar->action_container);
|
||||
auto ui_density = hal::getConfiguration()->uiDensity;
|
||||
auto* wrapper = create_action_wrapper(toolbar->action_container, ui_density);
|
||||
|
||||
auto* spinner = spinner_create(wrapper);
|
||||
lv_obj_set_align(spinner, LV_ALIGN_CENTER);
|
||||
|
||||
@@ -11,7 +11,7 @@ extern lv_obj_t* __real_lv_button_create(lv_obj_t* parent);
|
||||
lv_obj_t* __wrap_lv_button_create(lv_obj_t* parent) {
|
||||
auto button = __real_lv_button_create(parent);
|
||||
|
||||
if (tt::hal::getConfiguration()->uiScale == tt::hal::UiScale::Smallest) {
|
||||
if (tt::hal::getConfiguration()->uiDensity == tt::hal::UiDensity::Compact) {
|
||||
lv_obj_set_style_pad_all(button, 2, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_radius(button, 3, LV_STATE_DEFAULT);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ extern lv_obj_t* __real_lv_dropdown_create(lv_obj_t* parent);
|
||||
lv_obj_t* __wrap_lv_dropdown_create(lv_obj_t* parent) {
|
||||
auto dropdown = __real_lv_dropdown_create(parent);
|
||||
|
||||
if (tt::hal::getConfiguration()->uiScale == tt::hal::UiScale::Smallest) {
|
||||
if (tt::hal::getConfiguration()->uiDensity == tt::hal::UiDensity::Compact) {
|
||||
lv_obj_set_style_pad_all(dropdown, 2, LV_STATE_DEFAULT);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ extern lv_obj_t* __real_lv_list_add_button(lv_obj_t* list, const void* icon, con
|
||||
lv_obj_t* __wrap_lv_list_create(lv_obj_t* parent) {
|
||||
auto* list = __real_lv_list_create(parent);
|
||||
|
||||
if (tt::hal::getConfiguration()->uiScale == tt::hal::UiScale::Smallest) {
|
||||
if (tt::hal::getConfiguration()->uiDensity == tt::hal::UiDensity::Compact) {
|
||||
lv_obj_set_style_pad_row(list, 2, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_pad_column(list, 2, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_pad_all(list, 2, LV_STATE_DEFAULT);
|
||||
@@ -24,7 +24,7 @@ lv_obj_t* __wrap_lv_list_create(lv_obj_t* parent) {
|
||||
lv_obj_t* __wrap_lv_list_add_button(lv_obj_t* list, const void* icon, const char* txt) {
|
||||
auto* button = __real_lv_list_add_button(list, icon, txt);
|
||||
|
||||
if (tt::hal::getConfiguration()->uiScale == tt::hal::UiScale::Smallest) {
|
||||
if (tt::hal::getConfiguration()->uiDensity == tt::hal::UiDensity::Compact) {
|
||||
lv_obj_set_style_pad_ver(button, 2, LV_STATE_DEFAULT);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,14 +12,14 @@ extern lv_obj_t* __real_lv_obj_create(lv_obj_t* parent);
|
||||
void __wrap_lv_obj_set_flex_flow(lv_obj_t* obj, lv_flex_flow_t flow) {
|
||||
__real_lv_obj_set_flex_flow(obj, flow);
|
||||
|
||||
if (tt::hal::getConfiguration()->uiScale == tt::hal::UiScale::Smallest) {
|
||||
if (tt::hal::getConfiguration()->uiDensity == tt::hal::UiDensity::Compact) {
|
||||
lv_obj_set_style_pad_gap(obj, 4, LV_STATE_DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
lv_obj_t* __wrap_lv_obj_create(lv_obj_t* parent) {
|
||||
auto obj = __real_lv_obj_create(parent);
|
||||
if (tt::hal::getConfiguration()->uiScale == tt::hal::UiScale::Smallest) {
|
||||
if (tt::hal::getConfiguration()->uiDensity == tt::hal::UiDensity::Compact) {
|
||||
lv_obj_set_style_pad_all(obj, 2, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_pad_gap(obj, 2, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_radius(obj, 3, LV_STATE_DEFAULT);
|
||||
|
||||
@@ -11,7 +11,7 @@ extern lv_obj_t* __real_lv_switch_create(lv_obj_t* parent);
|
||||
lv_obj_t* __wrap_lv_switch_create(lv_obj_t* parent) {
|
||||
auto widget = __real_lv_switch_create(parent);
|
||||
|
||||
if (tt::hal::getConfiguration()->uiScale == tt::hal::UiScale::Smallest) {
|
||||
if (tt::hal::getConfiguration()->uiDensity == tt::hal::UiDensity::Compact) {
|
||||
lv_obj_set_style_size(widget, 25, 15, LV_STATE_DEFAULT);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ extern lv_obj_t* __real_lv_textarea_create(lv_obj_t* parent);
|
||||
lv_obj_t* __wrap_lv_textarea_create(lv_obj_t* parent) {
|
||||
auto textarea = __real_lv_textarea_create(parent);
|
||||
|
||||
if (tt::hal::getConfiguration()->uiScale == tt::hal::UiScale::Smallest) {
|
||||
if (tt::hal::getConfiguration()->uiDensity == tt::hal::UiDensity::Compact) {
|
||||
lv_obj_set_style_pad_all(textarea, 2, LV_STATE_DEFAULT);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,16 +35,16 @@ void download(
|
||||
auto certificate_length = strlen(reinterpret_cast<const char*>(certificate.get())) + 1;
|
||||
|
||||
// TODO: Fix for missing initializer warnings
|
||||
auto config = std::make_unique<esp_http_client_config_t>(esp_http_client_config_t {
|
||||
.url = url.c_str(),
|
||||
.auth_type = HTTP_AUTH_TYPE_NONE,
|
||||
.cert_pem = reinterpret_cast<const char*>(certificate.get()),
|
||||
.cert_len = certificate_length,
|
||||
.tls_version = ESP_HTTP_CLIENT_TLS_VER_TLS_1_3,
|
||||
.method = HTTP_METHOD_GET,
|
||||
.timeout_ms = 5000,
|
||||
.transport_type = HTTP_TRANSPORT_OVER_SSL
|
||||
});
|
||||
auto config = std::make_unique<esp_http_client_config_t>();
|
||||
memset(config.get(), 0, sizeof(esp_http_client_config_t));
|
||||
config->url = url.c_str();
|
||||
config->auth_type = HTTP_AUTH_TYPE_NONE;
|
||||
config->cert_pem = reinterpret_cast<const char*>(certificate.get());
|
||||
config->cert_len = certificate_length;
|
||||
config->tls_version = ESP_HTTP_CLIENT_TLS_VER_TLS_1_3;
|
||||
config->method = HTTP_METHOD_GET;
|
||||
config->timeout_ms = 5000;
|
||||
config->transport_type = HTTP_TRANSPORT_OVER_SSL;
|
||||
|
||||
auto client = std::make_unique<EspHttpClient>();
|
||||
if (!client->init(std::move(config))) {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
#include <Tactility/Tactility.h>
|
||||
|
||||
#include <Tactility/Logger.h>
|
||||
#include <Tactility/lvgl/Statusbar.h>
|
||||
#include <Tactility/service/ServiceContext.h>
|
||||
#include <Tactility/service/ServiceManifest.h>
|
||||
#include <Tactility/service/ServicePaths.h>
|
||||
#include <Tactility/service/memorychecker/MemoryCheckerService.h>
|
||||
|
||||
#include <tactility/lvgl_symbols_statusbar.h>
|
||||
|
||||
namespace tt::service::memorychecker {
|
||||
|
||||
static const auto LOGGER = Logger("MemoryChecker");
|
||||
@@ -55,8 +54,7 @@ bool MemoryCheckerService::onStart(ServiceContext& service) {
|
||||
auto lock = mutex.asScopedLock();
|
||||
lock.lock();
|
||||
|
||||
auto icon_path = std::string("A:") + service.getPaths()->getAssetsPath("memory_alert.png");
|
||||
statusbarIconId = lvgl::statusbar_icon_add(icon_path, false);
|
||||
statusbarIconId = lvgl::statusbar_icon_add(LVGL_SYMBOL_MEMORY, false);
|
||||
lvgl::statusbar_icon_set_visibility(statusbarIconId, false);
|
||||
|
||||
timer.setCallbackPriority(Thread::Priority::Lower);
|
||||
|
||||
@@ -14,60 +14,40 @@
|
||||
#include <Tactility/service/gps/GpsService.h>
|
||||
#include <Tactility/service/wifi/Wifi.h>
|
||||
|
||||
#include <tactility/lvgl_symbols_statusbar.h>
|
||||
|
||||
namespace tt::service::statusbar {
|
||||
|
||||
static const auto LOGGER = Logger("StatusbarService");
|
||||
|
||||
// SD card status
|
||||
constexpr auto* STATUSBAR_ICON_SDCARD = "sdcard.png";
|
||||
constexpr auto* STATUSBAR_ICON_SDCARD_ALERT = "sdcard_alert.png";
|
||||
|
||||
// Wifi status
|
||||
constexpr auto* STATUSBAR_ICON_WIFI_OFF_WHITE = "wifi_off_white.png";
|
||||
constexpr auto* STATUSBAR_ICON_WIFI_SCAN_WHITE = "wifi_scan_white.png";
|
||||
constexpr auto* STATUSBAR_ICON_WIFI_SIGNAL_WEAK_WHITE = "wifi_signal_weak_white.png";
|
||||
constexpr auto* STATUSBAR_ICON_WIFI_SIGNAL_MEDIUM_WHITE = "wifi_signal_medium_white.png";
|
||||
constexpr auto* STATUSBAR_ICON_WIFI_SIGNAL_STRONG_WHITE = "wifi_signal_strong_white.png";
|
||||
|
||||
// Power status
|
||||
constexpr auto* STATUSBAR_ICON_POWER_0 = "power_0.png";
|
||||
constexpr auto* STATUSBAR_ICON_POWER_10 = "power_10.png";
|
||||
constexpr auto* STATUSBAR_ICON_POWER_20 = "power_20.png";
|
||||
constexpr auto* STATUSBAR_ICON_POWER_30 = "power_30.png";
|
||||
constexpr auto* STATUSBAR_ICON_POWER_40 = "power_40.png";
|
||||
constexpr auto* STATUSBAR_ICON_POWER_50 = "power_50.png";
|
||||
constexpr auto* STATUSBAR_ICON_POWER_60 = "power_60.png";
|
||||
constexpr auto* STATUSBAR_ICON_POWER_70 = "power_70.png";
|
||||
constexpr auto* STATUSBAR_ICON_POWER_80 = "power_80.png";
|
||||
constexpr auto* STATUSBAR_ICON_POWER_90 = "power_90.png";
|
||||
constexpr auto* STATUSBAR_ICON_POWER_100 = "power_100.png";
|
||||
|
||||
// GPS
|
||||
constexpr auto* STATUSBAR_ICON_GPS = "location.png";
|
||||
|
||||
extern const ServiceManifest manifest;
|
||||
|
||||
const char* getWifiStatusIconForRssi(int rssi) {
|
||||
if (rssi >= -60) {
|
||||
return STATUSBAR_ICON_WIFI_SIGNAL_STRONG_WHITE;
|
||||
return LVGL_SYMBOL_SIGNAL_WIFI_4_BAR;
|
||||
} else if (rssi >= -70) {
|
||||
return STATUSBAR_ICON_WIFI_SIGNAL_MEDIUM_WHITE;
|
||||
return LVGL_SYMBOL_NETWORK_WIFI_3_BAR;
|
||||
} else if (rssi >= -80) {
|
||||
return LVGL_SYMBOL_NETWORK_WIFI_2_BAR;
|
||||
} else if (rssi >= -90) {
|
||||
return LVGL_SYMBOL_NETWORK_WIFI_1_BAR;
|
||||
} else {
|
||||
return STATUSBAR_ICON_WIFI_SIGNAL_WEAK_WHITE;
|
||||
return LVGL_SYMBOL_SIGNAL_WIFI_BAD;
|
||||
}
|
||||
}
|
||||
|
||||
static const char* getWifiStatusIcon(wifi::RadioState state, bool secure) {
|
||||
static const char* getWifiStatusIcon(wifi::RadioState state) {
|
||||
int rssi;
|
||||
switch (state) {
|
||||
using enum wifi::RadioState;
|
||||
case On:
|
||||
case OnPending:
|
||||
case ConnectionPending:
|
||||
return STATUSBAR_ICON_WIFI_SCAN_WHITE;
|
||||
return LVGL_SYMBOL_SIGNAL_WIFI_0_BAR;
|
||||
case OffPending:
|
||||
case Off:
|
||||
return STATUSBAR_ICON_WIFI_OFF_WHITE;
|
||||
return LVGL_SYMBOL_SIGNAL_WIFI_OFF;
|
||||
case ConnectionActive:
|
||||
rssi = wifi::getRssi();
|
||||
return getWifiStatusIconForRssi(rssi);
|
||||
@@ -80,11 +60,11 @@ static const char* getSdCardStatusIcon(hal::sdcard::SdCardDevice::State state) {
|
||||
switch (state) {
|
||||
using enum hal::sdcard::SdCardDevice::State;
|
||||
case Mounted:
|
||||
return STATUSBAR_ICON_SDCARD;
|
||||
return LVGL_SYMBOL_SD_CARD;
|
||||
case Error:
|
||||
case Unmounted:
|
||||
case Timeout:
|
||||
return STATUSBAR_ICON_SDCARD_ALERT;
|
||||
return LVGL_SYMBOL_SD_CARD_ALERT;
|
||||
default:
|
||||
check(false, "Unhandled SdCard state");
|
||||
}
|
||||
@@ -113,27 +93,19 @@ static const char* getPowerStatusIcon() {
|
||||
uint8_t charge = charge_level.valueAsUint8;
|
||||
|
||||
if (charge >= 95) {
|
||||
return STATUSBAR_ICON_POWER_100;
|
||||
} else if (charge >= 85) {
|
||||
return STATUSBAR_ICON_POWER_90;
|
||||
} else if (charge >= 75) {
|
||||
return STATUSBAR_ICON_POWER_80;
|
||||
} else if (charge >= 65) {
|
||||
return STATUSBAR_ICON_POWER_70;
|
||||
} else if (charge >= 55) {
|
||||
return STATUSBAR_ICON_POWER_60;
|
||||
} else if (charge >= 45) {
|
||||
return STATUSBAR_ICON_POWER_50;
|
||||
} else if (charge >= 35) {
|
||||
return STATUSBAR_ICON_POWER_40;
|
||||
} else if (charge >= 25) {
|
||||
return STATUSBAR_ICON_POWER_30;
|
||||
} else if (charge >= 15) {
|
||||
return STATUSBAR_ICON_POWER_20;
|
||||
} else if (charge >= 5) {
|
||||
return STATUSBAR_ICON_POWER_10;
|
||||
return LVGL_SYMBOL_BATTERY_ANDROID_FRAME_FULL;
|
||||
} else if (charge >= 80) {
|
||||
return LVGL_SYMBOL_BATTERY_ANDROID_FRAME_6;
|
||||
} else if (charge >= 64) {
|
||||
return LVGL_SYMBOL_BATTERY_ANDROID_FRAME_5;
|
||||
} else if (charge >= 48) {
|
||||
return LVGL_SYMBOL_BATTERY_ANDROID_FRAME_4;
|
||||
} else if (charge >= 32) {
|
||||
return LVGL_SYMBOL_BATTERY_ANDROID_FRAME_3;
|
||||
} else if (charge >= 16) {
|
||||
return LVGL_SYMBOL_BATTERY_ANDROID_FRAME_2;
|
||||
} else {
|
||||
return STATUSBAR_ICON_POWER_0;
|
||||
return LVGL_SYMBOL_BATTERY_ANDROID_FRAME_1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,8 +122,6 @@ class StatusbarService final : public Service {
|
||||
int8_t power_icon_id;
|
||||
const char* power_last_icon = nullptr;
|
||||
|
||||
std::unique_ptr<ServicePaths> paths;
|
||||
|
||||
void lock() const {
|
||||
mutex.lock();
|
||||
}
|
||||
@@ -165,8 +135,7 @@ class StatusbarService final : public Service {
|
||||
bool show_icon = (gps_state == gps::State::OnPending) || (gps_state == gps::State::On);
|
||||
if (gps_last_state != show_icon) {
|
||||
if (show_icon) {
|
||||
auto icon_path = "A:" + paths->getAssetsPath(STATUSBAR_ICON_GPS);
|
||||
lvgl::statusbar_icon_set_image(gps_icon_id, icon_path);
|
||||
lvgl::statusbar_icon_set_image(gps_icon_id, LVGL_SYMBOL_LOCATION_ON);
|
||||
lvgl::statusbar_icon_set_visibility(gps_icon_id, true);
|
||||
} else {
|
||||
lvgl::statusbar_icon_set_visibility(gps_icon_id, false);
|
||||
@@ -177,12 +146,10 @@ class StatusbarService final : public Service {
|
||||
|
||||
void updateWifiIcon() {
|
||||
wifi::RadioState radio_state = wifi::getRadioState();
|
||||
bool is_secure = wifi::isConnectionSecure();
|
||||
const char* desired_icon = getWifiStatusIcon(radio_state, is_secure);
|
||||
const char* desired_icon = getWifiStatusIcon(radio_state);
|
||||
if (wifi_last_icon != desired_icon) {
|
||||
if (desired_icon != nullptr) {
|
||||
auto icon_path = "A:" + paths->getAssetsPath(desired_icon);
|
||||
lvgl::statusbar_icon_set_image(wifi_icon_id, icon_path);
|
||||
lvgl::statusbar_icon_set_image(wifi_icon_id, desired_icon);
|
||||
lvgl::statusbar_icon_set_visibility(wifi_icon_id, true);
|
||||
} else {
|
||||
lvgl::statusbar_icon_set_visibility(wifi_icon_id, false);
|
||||
@@ -195,8 +162,7 @@ class StatusbarService final : public Service {
|
||||
const char* desired_icon = getPowerStatusIcon();
|
||||
if (power_last_icon != desired_icon) {
|
||||
if (desired_icon != nullptr) {
|
||||
auto icon_path = "A:" + paths->getAssetsPath(desired_icon);
|
||||
lvgl::statusbar_icon_set_image(power_icon_id, icon_path);
|
||||
lvgl::statusbar_icon_set_image(power_icon_id, desired_icon);
|
||||
lvgl::statusbar_icon_set_visibility(power_icon_id, true);
|
||||
} else {
|
||||
lvgl::statusbar_icon_set_visibility(power_icon_id, false);
|
||||
@@ -214,8 +180,7 @@ class StatusbarService final : public Service {
|
||||
if (state != hal::sdcard::SdCardDevice::State::Timeout) {
|
||||
auto* desired_icon = getSdCardStatusIcon(state);
|
||||
if (sdcard_last_icon != desired_icon) {
|
||||
auto icon_path = "A:" + paths->getAssetsPath(desired_icon);
|
||||
lvgl::statusbar_icon_set_image(sdcard_icon_id, icon_path);
|
||||
lvgl::statusbar_icon_set_image(sdcard_icon_id, desired_icon);
|
||||
lvgl::statusbar_icon_set_visibility(sdcard_icon_id, true);
|
||||
sdcard_last_icon = desired_icon;
|
||||
}
|
||||
@@ -258,8 +223,6 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
paths = serviceContext.getPaths();
|
||||
|
||||
// TODO: Make thread-safe for LVGL
|
||||
lvgl::statusbar_icon_set_visibility(wifi_icon_id, true);
|
||||
|
||||
|
||||
@@ -8,16 +8,15 @@
|
||||
#include <Tactility/MountPoints.h>
|
||||
#include <Tactility/file/File.h>
|
||||
#include <Tactility/Logger.h>
|
||||
#include <Tactility/kernel/SystemEvents.h>
|
||||
#include <Tactility/lvgl/Statusbar.h>
|
||||
#include <Tactility/Mutex.h>
|
||||
|
||||
#include <Tactility/Tactility.h>
|
||||
#include <Tactility/TactilityConfig.h>
|
||||
#include <tactility/hal/Device.h>
|
||||
#include <Tactility/app/AppRegistration.h>
|
||||
#include <Tactility/app/AppManifest.h>
|
||||
#include <Tactility/app/App.h>
|
||||
#include <Tactility/hal/sdcard/SdCardDevice.h>
|
||||
#include <Tactility/service/wifi/Wifi.h>
|
||||
#include <Tactility/network/HttpdReq.h>
|
||||
#include <Tactility/network/Url.h>
|
||||
@@ -32,23 +31,25 @@
|
||||
#include <lv_screenshot.h>
|
||||
#endif
|
||||
|
||||
#include <esp_system.h>
|
||||
#include <esp_heap_caps.h>
|
||||
#include <esp_chip_info.h>
|
||||
#include <esp_vfs_fat.h>
|
||||
#include <esp_flash.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <esp_netif.h>
|
||||
#include <lwip/ip4_addr.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <sstream>
|
||||
#include <tactility/lvgl_symbols_statusbar.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <esp_chip_info.h>
|
||||
#include <esp_flash.h>
|
||||
#include <esp_heap_caps.h>
|
||||
#include <esp_netif.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_vfs_fat.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <iomanip>
|
||||
#include <cctype>
|
||||
#include <lwip/ip4_addr.h>
|
||||
#include <mbedtls/base64.h>
|
||||
#include <sstream>
|
||||
|
||||
namespace tt::service::webserver {
|
||||
|
||||
@@ -519,13 +520,9 @@ bool WebServerService::startServer() {
|
||||
LOGGER.info("HTTP server started successfully on port {}", settings.webServerPort);
|
||||
publish_event(this, WebServerEvent::WebServerStarted);
|
||||
|
||||
// Show statusbar icon (different icon for AP vs Station mode)
|
||||
// Show statusbar icon
|
||||
if (statusbarIconId >= 0) {
|
||||
const char* icon_name = (settings.wifiMode == settings::webserver::WiFiMode::AccessPoint)
|
||||
? "webserver_ap_white.png"
|
||||
: "webserver_station_white.png";
|
||||
auto icon_path = std::string("A:/system/service/Statusbar/assets/") + icon_name;
|
||||
lvgl::statusbar_icon_set_image(statusbarIconId, icon_path);
|
||||
lvgl::statusbar_icon_set_image(statusbarIconId, LVGL_SYMBOL_CLOUD);
|
||||
lvgl::statusbar_icon_set_visibility(statusbarIconId, true);
|
||||
LOGGER.info("WebServer statusbar icon shown ({} mode)",
|
||||
settings.wifiMode == settings::webserver::WiFiMode::AccessPoint ? "AP" : "Station");
|
||||
|
||||
Reference in New Issue
Block a user