Merge develop into main (#338)

### Cardputer:
- Fix keyboard issue with up/down button conflict when selecting switch
- Fix backlight flickering

### UI improvements
- Removed a 3 pixel border that went around the entire desktop environment
- Improved system layout (GuiService)
- Statusbar: improved layout (mainly margin/padding)
- Toolbar: fixed margin/padding of all buttons, fixed alignment of all content
- Improved layout/UI of many apps

### Other
- Update LVGL to 9.3.0 official release (was dev version)
This commit is contained in:
Ken Van Hoeylandt 2025-09-16 23:12:07 +02:00 committed by GitHub
parent 53b711584f
commit a2af95b92d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 362 additions and 495 deletions

View File

@ -6,5 +6,5 @@ constexpr auto* TAG = "Cardputer";
bool initBoot() { bool initBoot() {
TT_LOG_I(TAG, "initBoot"); TT_LOG_I(TAG, "initBoot");
return driver::pwmbacklight::init(GPIO_NUM_38, 256); return driver::pwmbacklight::init(GPIO_NUM_38, 512);
} }

View File

@ -62,11 +62,23 @@ void CardputerKeyboard::readCallback(lv_indev_t* indev, lv_indev_data_t* data) {
} else { } else {
for (auto& i : self->keyboard.keysState().values) { for (auto& i : self->keyboard.keysState().values) {
if (i == ';') { // Up if (i == ';') { // Up
data->key = LV_KEY_UP; /*
data->state = LV_INDEV_STATE_PRESSED; * WARNING:
* lv_switch picks up on this and toggles it, while the CardputerEncoder uses it for scrolling.
* We disable the keypress so the encoder can work properly.
*/
// TODO: Can we detect the active widget and ignore it in case it's a switch?
// data->key = LV_KEY_UP;
// data->state = LV_INDEV_STATE_PRESSED;
} else if (i == '.') { // Down } else if (i == '.') { // Down
data->key = LV_KEY_DOWN; /*
data->state = LV_INDEV_STATE_PRESSED; * WARNING:
* lv_switch picks up on this and toggles it, while the CardputerEncoder uses it for scrolling.
* We disable the keypress so the encoder can work properly.
*/
// TODO: Can we detect the active widget and ignore it in case it's a switch?
// data->key = LV_KEY_DOWN;
// data->state = LV_INDEV_STATE_PRESSED;
} else if (i == ',') { // Left } else if (i == ',') { // Left
data->key = LV_KEY_LEFT; data->key = LV_KEY_LEFT;
data->state = LV_INDEV_STATE_PRESSED; data->state = LV_INDEV_STATE_PRESSED;

View File

@ -36,7 +36,7 @@ cd -
cp Libraries/lvgl/lvgl.h $find_target_dir cp Libraries/lvgl/lvgl.h $find_target_dir
cp Libraries/lvgl/lv_version.h $find_target_dir cp Libraries/lvgl/lv_version.h $find_target_dir
cp Libraries/lvgl/LICENCE.txt $lvgl_library_path/LICENSE.txt cp Libraries/lvgl/LICENCE.txt $lvgl_library_path/LICENSE.txt
cp Libraries/lvgl_conf/lv_conf_kconfig.h $lvgl_library_path/Include/lv_conf.h cp Libraries/lvgl/src/lv_conf_kconfig.h $lvgl_library_path/Include/lv_conf.h
# elf_loader # elf_loader
elf_loader_library_path=$library_path/elf_loader elf_loader_library_path=$library_path/elf_loader

View File

@ -49,11 +49,12 @@ if (DEFINED ENV{ESP_IDF_VERSION})
set(EXCLUDE_COMPONENTS "Simulator") set(EXCLUDE_COMPONENTS "Simulator")
# LVGL # LVGL
get_filename_component( # set(LV_CONF_PATH Libraries/lvgl/src/lv_conf_kconfig.h)
LVGL_CONFIG_FULL_PATH Libraries/lvgl_conf ABSOLUTE # get_filename_component(
) # LV_CONF_PATH Libraries/lvgl/src/lv_conf_kconfig.h ABSOLUTE
# )
# add_compile_definitions(LV_CONF_PATH="${LVGL_CONFIG_FULL_PATH}")
add_compile_definitions(LV_CONF_PATH="${LVGL_CONFIG_FULL_PATH}/lv_conf_kconfig.h")
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND) idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_log_write" APPEND) idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_log_write" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_button_create" APPEND) idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_button_create" APPEND)
@ -111,8 +112,6 @@ if (NOT DEFINED ENV{ESP_IDF_VERSION})
# LVGL # LVGL
add_subdirectory(Libraries/lvgl) # Added as idf component for ESP and as library for other targets add_subdirectory(Libraries/lvgl) # Added as idf component for ESP and as library for other targets
include_directories(lvgl PUBLIC ${PROJECT_SOURCE_DIR}/Libraries/lvgl_conf)
target_compile_definitions(lvgl PUBLIC "-DLV_CONF_PATH=\"${PROJECT_SOURCE_DIR}/Libraries/lvgl_conf/lv_conf_simulator.h\"")
target_link_libraries(lvgl PRIVATE SDL2-static) target_link_libraries(lvgl PRIVATE SDL2-static)
endif () endif ()

View File

@ -2,6 +2,7 @@
## Higher Priority ## Higher Priority
- Show a warning in the web installer when flashing CYD 28R board regarding v1/v2/v3
- Fix Development service: when no SD card is present, the app fails to install. Consider installing to `/data` - Fix Development service: when no SD card is present, the app fails to install. Consider installing to `/data`
Note: Change app install to "transfer file" functionality. We can have a proper install when we have app packaging. Note: Change app install to "transfer file" functionality. We can have a proper install when we have app packaging.
Note: Consider installation path option in interface Note: Consider installation path option in interface
@ -15,12 +16,10 @@
- CrowPanel Basic 3.5": check why GraphicsDemo fails - CrowPanel Basic 3.5": check why GraphicsDemo fails
- CrowPanel Basic 3.5": check why System Info doesn't show storage info - CrowPanel Basic 3.5": check why System Info doesn't show storage info
- Update to LVGL v9.3 stable - Update to LVGL v9.3 stable
- Files app: delete folder recursively
- Create `app::getSettingsPath()` to get paths to properties files by first trying sd card and then trying `/data` - Create `app::getSettingsPath()` to get paths to properties files by first trying sd card and then trying `/data`
## Medium Priority ## Medium Priority
- Implement `uninstall` action in `tactility.py`
- Improve EspLcdDisplay to contain all the standard configuration options, and implement a default init function. Add a configuration class. - Improve EspLcdDisplay to contain all the standard configuration options, and implement a default init function. Add a configuration class.
- Statusbar icon that shows low/critical memory warnings - Statusbar icon that shows low/critical memory warnings
- Make WiFi setup app that starts an access point and hosts a webpage to set up the device. - Make WiFi setup app that starts an access point and hosts a webpage to set up the device.
@ -29,13 +28,12 @@
- Try out ILI9342 https://github.com/jbrilha/esp_lcd_ili9342 - Try out ILI9342 https://github.com/jbrilha/esp_lcd_ili9342
- All drivers (e.g. display, touch, etc.) should call stop() in their destructor, or at least assert that they should not be running. - All drivers (e.g. display, touch, etc.) should call stop() in their destructor, or at least assert that they should not be running.
- Bug: Turn on WiFi (when testing it wasn't connected/connecting - just active). Open chat. Observe crash. - Bug: Turn on WiFi (when testing it wasn't connected/connecting - just active). Open chat. Observe crash.
- Toolbar: when the title doesn't fit, scroll the text instead of splitting it onto a new line (try on Waveshare 1.47")
- UI: create UI size classification (e.g. "compact" for tiny screens without touch)
- Bug: Crash handling app cannot be exited with an EncoderDevice. (current work-around is to manually reset the device) - Bug: Crash handling app cannot be exited with an EncoderDevice. (current work-around is to manually reset the device)
- I2C app should show error when I2C port is disabled when the scan button was manually pressed - I2C app should show error when I2C port is disabled when the scan button was manually pressed
## Lower Priority ## Lower Priority
- Rename `filebrowser` to `files` and `FileBrowser.cpp` to `Files.cpp`
- Implement system suspend that turns off the screen - Implement system suspend that turns off the screen
- The boot button on some devices can be used as GPIO_NUM_0 at runtime - The boot button on some devices can be used as GPIO_NUM_0 at runtime
- Localize all apps - Localize all apps

View File

@ -77,6 +77,14 @@ bool St7789Display::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lc
return false; return false;
} }
// Warning: it looks like LVGL rotation is broken when "gap" is set and the screen is moved to a non-default orientation
int gap_x = configuration->swapXY ? configuration->gapY : configuration->gapX;
int gap_y = configuration->swapXY ? configuration->gapX : configuration->gapY;
if (esp_lcd_panel_set_gap(panelHandle, gap_x, gap_y) != ESP_OK) {
TT_LOG_E(TAG, "Failed to set panel gap");
return false;
}
if (esp_lcd_panel_swap_xy(panelHandle, configuration->swapXY) != ESP_OK) { if (esp_lcd_panel_swap_xy(panelHandle, configuration->swapXY) != ESP_OK) {
TT_LOG_E(TAG, "Failed to swap XY "); TT_LOG_E(TAG, "Failed to swap XY ");
return false; return false;
@ -92,13 +100,6 @@ bool St7789Display::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lc
return false; return false;
} }
int gap_x = configuration->swapXY ? configuration->gapY : configuration->gapX;
int gap_y = configuration->swapXY ? configuration->gapX : configuration->gapY;
if (esp_lcd_panel_set_gap(panelHandle, gap_x, gap_y) != ESP_OK) {
TT_LOG_E(TAG, "Failed to set panel gap");
return false;
}
return true; return true;
} }

@ -1 +1 @@
Subproject commit 6decbb7f7783f6e48d4591fcb9f7810c2fb08e61 Subproject commit c033a98afddd65aaafeebea625382a94020fe4a7

View File

@ -1,244 +0,0 @@
/** * @file lv_conf_kconfig.h * Configs that need special handling when LVGL is used with Kconfig */
#ifndef LV_CONF_KCONFIG_CUSTOM_H
#define LV_CONF_KCONFIG_CUSTOM_H
#define LV_CONF_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef LV_CONF_KCONFIG_EXTERNAL_INCLUDE
# include LV_CONF_KCONFIG_EXTERNAL_INCLUDE
#else
# ifdef ESP_PLATFORM
# include "sdkconfig.h"
# include "esp_attr.h"
# endif
# ifdef __NuttX__
# include <nuttx/config.h>
# elif defined(__RTTHREAD__)
# define LV_CONF_INCLUDE_SIMPLE
# include <lv_rt_thread_conf.h>
# endif
#endif /*LV_CONF_KCONFIG_EXTERNAL_INCLUDE*/
/*******************
* LV_USE_STDLIB_MALLOC
*******************/
#ifdef CONFIG_LV_USE_BUILTIN_MALLOC
# define CONFIG_LV_USE_STDLIB_MALLOC LV_STDLIB_BUILTIN
#elif defined(CONFIG_LV_USE_CLIB_MALLOC)
# define CONFIG_LV_USE_STDLIB_MALLOC LV_STDLIB_CLIB
#elif defined(CONFIG_LV_USE_MICROPYTHON_MALLOC)
# define CONFIG_LV_USE_STDLIB_MALLOC LV_STDLIB_MICROPYTHON
#elif defined(CONFIG_LV_USE_RTTHREAD_MALLOC)
# define CONFIG_LV_USE_STDLIB_MALLOC LV_STDLIB_RTTHREAD
#elif defined (CONFIG_LV_USE_CUSTOM_MALLOC)
# define CONFIG_LV_USE_STDLIB_MALLOC LV_STDLIB_CUSTOM
#endif
/*******************
* LV_USE_STDLIB_STRING
*******************/
#ifdef CONFIG_LV_USE_BUILTIN_STRING
# define CONFIG_LV_USE_STDLIB_STRING LV_STDLIB_BUILTIN
#elif defined(CONFIG_LV_USE_CLIB_STRING)
# define CONFIG_LV_USE_STDLIB_STRING LV_STDLIB_CLIB
#elif defined(CONFIG_LV_USE_MICROPYTHON_STRING)
# define CONFIG_LV_USE_STDLIB_STRING LV_STDLIB_MICROPYTHON
#elif defined(CONFIG_LV_USE_RTTHREAD_STRING)
# define CONFIG_LV_USE_STDLIB_STRING LV_STDLIB_RTTHREAD
#elif defined (CONFIG_LV_USE_CUSTOM_STRING)
# define CONFIG_LV_USE_STDLIB_STRING LV_STDLIB_CUSTOM
#endif
/*******************
* LV_USE_STDLIB_SPRINTF
*******************/
#ifdef CONFIG_LV_USE_BUILTIN_SPRINTF
# define CONFIG_LV_USE_STDLIB_SPRINTF LV_STDLIB_BUILTIN
#elif defined(CONFIG_LV_USE_CLIB_SPRINTF)
# define CONFIG_LV_USE_STDLIB_SPRINTF LV_STDLIB_CLIB
#elif defined(CONFIG_LV_USE_MICROPYTHON_SPRINTF)
# define CONFIG_LV_USE_STDLIB_SPRINTF LV_STDLIB_MICROPYTHON
#elif defined(CONFIG_LV_USE_RTTHREAD_SPRINTF)
# define CONFIG_LV_USE_STDLIB_SPRINTF LV_STDLIB_RTTHREAD
#elif defined (CONFIG_LV_USE_CUSTOM_SPRINTF)
# define CONFIG_LV_USE_STDLIB_SPRINTF LV_STDLIB_CUSTOM
#endif
/*******************
* LV_MEM_SIZE
*******************/
#ifdef CONFIG_LV_MEM_SIZE_KILOBYTES
# if(CONFIG_LV_MEM_SIZE_KILOBYTES < 2)
# error "LV_MEM_SIZE >= 2kB is required"
# endif
# define CONFIG_LV_MEM_SIZE (CONFIG_LV_MEM_SIZE_KILOBYTES * 1024U)
#endif
#ifdef CONFIG_LV_MEM_POOL_EXPAND_SIZE_KILOBYTES
# define CONFIG_LV_MEM_POOL_EXPAND_SIZE (CONFIG_LV_MEM_POOL_EXPAND_SIZE_KILOBYTES * 1024U)
#endif
/*------------------
* MONITOR POSITION
*-----------------*/
#ifdef CONFIG_LV_PERF_MONITOR_ALIGN_TOP_LEFT
# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_TOP_LEFT
#elif defined(CONFIG_LV_USE_PERF_MONITOR_ALIGN_TOP_MID)
# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_TOP_MID
#elif defined(CONFIG_LV_PERF_MONITOR_ALIGN_TOP_RIGHT)
# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_TOP_RIGHT
#elif defined(CONFIG_LV_PERF_MONITOR_ALIGN_BOTTOM_LEFT)
# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_LEFT
#elif defined(CONFIG_LV_PERF_MONITOR_ALIGN_BOTTOM_MID)
# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_MID
#elif defined(CONFIG_LV_PERF_MONITOR_ALIGN_BOTTOM_RIGHT)
# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
#elif defined(CONFIG_LV_PERF_MONITOR_ALIGN_LEFT_MID)
# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_LEFT_MID
#elif defined(CONFIG_LV_PERF_MONITOR_ALIGN_RIGHT_MID)
# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_RIGHT_MID
#elif defined(CONFIG_LV_PERF_MONITOR_ALIGN_CENTER)
# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_CENTER
#endif
#ifdef CONFIG_LV_MEM_MONITOR_ALIGN_TOP_LEFT
# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_TOP_LEFT
#elif defined(CONFIG_LV_USE_MEM_MONITOR_ALIGN_TOP_MID)
# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_TOP_MID
#elif defined(CONFIG_LV_MEM_MONITOR_ALIGN_TOP_RIGHT)
# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_TOP_RIGHT
#elif defined(CONFIG_LV_MEM_MONITOR_ALIGN_BOTTOM_LEFT)
# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT
#elif defined(CONFIG_LV_MEM_MONITOR_ALIGN_BOTTOM_MID)
# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_MID
#elif defined(CONFIG_LV_MEM_MONITOR_ALIGN_BOTTOM_RIGHT)
# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
#elif defined(CONFIG_LV_MEM_MONITOR_ALIGN_LEFT_MID)
# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_LEFT_MID
#elif defined(CONFIG_LV_MEM_MONITOR_ALIGN_RIGHT_MID)
# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_RIGHT_MID
#elif defined(CONFIG_LV_MEM_MONITOR_ALIGN_CENTER)
# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_CENTER
#endif
/********************
* FONT SELECTION
*******************/
/**
* NOTE: In Kconfig instead of `LV_DEFAULT_FONT`
* `CONFIG_LV_FONT_DEFAULT_<font_name>` is defined
* hence the large selection with if-s
*/
/*------------------
* DEFAULT FONT
*-----------------*/
#ifdef CONFIG_LV_FONT_DEFAULT_MONTSERRAT_8
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_8
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_10)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_10
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_12)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_12
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_14)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_14
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_16)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_16
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_18)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_18
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_20)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_20
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_22)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_22
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_24)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_24
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_26)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_26
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_28)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_28
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_30)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_30
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_32)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_32
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_34)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_34
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_36)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_36
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_38)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_38
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_40)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_40
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_42)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_42
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_44)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_44
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_46)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_46
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_48)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_48
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_12_SUBPX)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_12_subpx
#elif defined(CONFIG_LV_FONT_DEFAULT_MONTSERRAT_28_COMPRESSED)
# define CONFIG_LV_FONT_DEFAULT &lv_font_montserrat_28_compressed
#elif defined(CONFIG_LV_FONT_DEFAULT_DEJAVU_16_PERSIAN_HEBREW)
# define CONFIG_LV_FONT_DEFAULT &lv_font_dejavu_16_persian_hebrew
#elif defined(CONFIG_LV_FONT_DEFAULT_SIMSUN_16_CJK)
# define CONFIG_LV_FONT_DEFAULT &lv_font_simsun_16_cjk
#elif defined(CONFIG_LV_FONT_DEFAULT_UNSCII_8)
# define CONFIG_LV_FONT_DEFAULT &lv_font_unscii_8
#elif defined(CONFIG_LV_FONT_DEFAULT_UNSCII_16)
# define CONFIG_LV_FONT_DEFAULT &lv_font_unscii_16
#endif
/*------------------
* TEXT ENCODING
*-----------------*/
#ifdef CONFIG_LV_TXT_ENC_UTF8
# define CONFIG_LV_TXT_ENC LV_TXT_ENC_UTF8
#elif defined(CONFIG_LV_TXT_ENC_ASCII)
# define CONFIG_LV_TXT_ENC LV_TXT_ENC_ASCII
#endif
/*------------------
* BIDI DIRECTION
*-----------------*/
#ifdef CONFIG_LV_BASE_DIR_LTR
# define CONFIG_LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_LTR
#elif defined(CONFIG_LV_BASE_DIR_RTL)
# define CONFIG_LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_RTL
#elif defined(CONFIG_LV_BASE_DIR_AUTO)
# define CONFIG_LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO
#endif
/*------------------
* LINUX FBDEV
*-----------------*/
#ifdef CONFIG_LV_LINUX_FBDEV_RENDER_MODE_PARTIAL
# define CONFIG_LV_LINUX_FBDEV_RENDER_MODE LV_DISPLAY_RENDER_MODE_PARTIAL
#elif defined(CONFIG_LV_LINUX_FBDEV_RENDER_MODE_DIRECT)
# define CONFIG_LV_LINUX_FBDEV_RENDER_MODE LV_DISPLAY_RENDER_MODE_DIRECT
#elif defined(CONFIG_LV_LINUX_FBDEV_RENDER_MODE_FULL)
# define CONFIG_LV_LINUX_FBDEV_RENDER_MODE LV_DISPLAY_RENDER_MODE_FULL
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CONF_KCONFIG_H*/

View File

@ -1,14 +1,14 @@
#pragma once #pragma once
#include "Tactility/app/AppContext.h" #include <Tactility/app/AppContext.h>
#include <lvgl.h> #include <lvgl.h>
namespace tt::lvgl { namespace tt::lvgl {
#define STATUSBAR_ICON_LIMIT 8 constexpr auto STATUSBAR_ICON_LIMIT = 8;
#define STATUSBAR_ICON_SIZE 20 constexpr auto STATUSBAR_ICON_SIZE = 20;
#define STATUSBAR_HEIGHT (STATUSBAR_ICON_SIZE + 4) // 4 extra pixels for border and outline constexpr auto STATUSBAR_HEIGHT = STATUSBAR_ICON_SIZE + 2;
lv_obj_t* statusbar_create(lv_obj_t* parent); lv_obj_t* statusbar_create(lv_obj_t* parent);
int8_t statusbar_icon_add(const std::string& image); int8_t statusbar_icon_add(const std::string& image);

View File

@ -30,7 +30,7 @@ public:
explicit View(const std::shared_ptr<State>& state) : state(state) {} explicit View(const std::shared_ptr<State>& state) : state(state) {}
void init(lv_obj_t* parent); void init(const AppContext& appContext, lv_obj_t* parent);
void update(); void update();
void onNavigateUpPressed(); void onNavigateUpPressed();

View File

@ -145,16 +145,19 @@ public:
} }
void onShow(TT_UNUSED AppContext& app, lv_obj_t* parent) override { void onShow(TT_UNUSED AppContext& app, lv_obj_t* parent) override {
lvgl::obj_set_style_bg_blacken(parent);
lv_obj_set_style_border_width(parent, 0, LV_STATE_DEFAULT);
lv_obj_set_style_radius(parent, 0, LV_STATE_DEFAULT);
auto* image = lv_image_create(parent); auto* image = lv_image_create(parent);
lv_obj_set_size(image, LV_PCT(100), LV_PCT(100)); lv_obj_set_size(image, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_align(image, LV_ALIGN_CENTER, 0, 0);
const auto paths = app.getPaths(); const auto paths = app.getPaths();
const char* logo = hal::usb::isUsbBootMode() ? "logo_usb.png" : "logo.png"; const char* logo = hal::usb::isUsbBootMode() ? "logo_usb.png" : "logo.png";
const auto logo_path = paths->getSystemPathLvgl(logo); const auto logo_path = paths->getSystemPathLvgl(logo);
TT_LOG_I(TAG, "%s", logo_path.c_str()); TT_LOG_I(TAG, "%s", logo_path.c_str());
lv_image_set_src(image, logo_path.c_str()); lv_image_set_src(image, logo_path.c_str());
lvgl::obj_set_style_bg_blacken(parent);
} }
}; };

View File

@ -1,21 +1,21 @@
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include "Tactility/app/crashdiagnostics/QrHelpers.h" #include <Tactility/app/crashdiagnostics/QrHelpers.h>
#include "Tactility/app/crashdiagnostics/QrUrl.h" #include <Tactility/app/crashdiagnostics/QrUrl.h>
#include "Tactility/app/launcher/Launcher.h" #include <Tactility/app/launcher/Launcher.h>
#include "Tactility/lvgl/Statusbar.h" #include <Tactility/lvgl/Statusbar.h>
#include "Tactility/service/loader/Loader.h" #include <Tactility/service/loader/Loader.h>
#include <lvgl.h> #include <lvgl.h>
#include <qrcode.h> #include <qrcode.h>
#define TAG "crash_diagnostics" #define TAG "CrashDiagnostics"
namespace tt::app::crashdiagnostics { namespace tt::app::crashdiagnostics {
void onContinuePressed(TT_UNUSED lv_event_t* event) { void onContinuePressed(TT_UNUSED lv_event_t* event) {
tt::service::loader::stopApp(); service::loader::stopApp();
tt::app::launcher::start(); launcher::start();
} }
class CrashDiagnosticsApp : public App { class CrashDiagnosticsApp : public App {
@ -24,7 +24,7 @@ public:
void onShow(AppContext& app, lv_obj_t* parent) override { void onShow(AppContext& app, lv_obj_t* parent) override {
auto* display = lv_obj_get_display(parent); auto* display = lv_obj_get_display(parent);
int32_t parent_height = lv_display_get_vertical_resolution(display) - STATUSBAR_HEIGHT; int32_t parent_height = lv_display_get_vertical_resolution(display) - lvgl::STATUSBAR_HEIGHT;
lv_obj_add_event_cb(parent, onContinuePressed, LV_EVENT_SHORT_CLICKED, nullptr); lv_obj_add_event_cb(parent, onContinuePressed, LV_EVENT_SHORT_CLICKED, nullptr);
auto* top_label = lv_label_create(parent); auto* top_label = lv_label_create(parent);

View File

@ -108,45 +108,45 @@ public:
// Wrappers // Wrappers
lv_obj_t* secondary_flex = lv_obj_create(parent); lv_obj_t* content_wrapper = lv_obj_create(parent);
lv_obj_set_width(secondary_flex, LV_PCT(100)); lv_obj_set_width(content_wrapper, LV_PCT(100));
lv_obj_set_flex_grow(secondary_flex, 1); lv_obj_set_flex_grow(content_wrapper, 1);
lv_obj_set_flex_flow(secondary_flex, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(content_wrapper, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_border_width(secondary_flex, 0, 0); lv_obj_set_style_border_width(content_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_all(secondary_flex, 0, 0); lvgl::obj_set_style_bg_invisible(content_wrapper);
lv_obj_set_style_pad_gap(secondary_flex, 0, 0);
lvgl::obj_set_style_bg_invisible(secondary_flex);
// align() methods don't work on flex, so we need this extra wrapper
lv_obj_t* wrapper = lv_obj_create(secondary_flex);
lv_obj_set_size(wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lvgl::obj_set_style_bg_invisible(wrapper);
lv_obj_set_style_border_width(wrapper, 0, 0);
// Enable on boot // Enable on boot
lv_obj_t* enable_label = lv_label_create(wrapper); lv_obj_t* enable_wrapper = lv_obj_create(content_wrapper);
lv_label_set_text(enable_label, "Enable on boot"); lv_obj_set_size(enable_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_align(enable_label, LV_ALIGN_TOP_LEFT, 0, 6); lvgl::obj_set_style_bg_invisible(enable_wrapper);
lv_obj_set_style_border_width(enable_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_all(enable_wrapper, 0, LV_STATE_DEFAULT);
enableOnBootSwitch = lv_switch_create(wrapper); lv_obj_t* enable_label = lv_label_create(enable_wrapper);
lv_label_set_text(enable_label, "Enable on boot");
lv_obj_align(enable_label, LV_ALIGN_LEFT_MID, 0, 0);
enableOnBootSwitch = lv_switch_create(enable_wrapper);
lv_obj_add_event_cb(enableOnBootSwitch, onEnableOnBootSwitchChanged, LV_EVENT_VALUE_CHANGED, this); lv_obj_add_event_cb(enableOnBootSwitch, onEnableOnBootSwitchChanged, LV_EVENT_VALUE_CHANGED, this);
lv_obj_align(enableOnBootSwitch, LV_ALIGN_TOP_RIGHT, 0, 0); lv_obj_align(enableOnBootSwitch, LV_ALIGN_RIGHT_MID, 0, 0);
if (service::development::shouldEnableOnBoot()) { if (service::development::shouldEnableOnBoot()) {
lv_obj_add_state(enableOnBootSwitch, LV_STATE_CHECKED); lv_obj_add_state(enableOnBootSwitch, LV_STATE_CHECKED);
} else { } else {
lv_obj_remove_state(enableOnBootSwitch, LV_STATE_CHECKED); lv_obj_remove_state(enableOnBootSwitch, LV_STATE_CHECKED);
} }
statusLabel = lv_label_create(wrapper); // Status
lv_obj_align(statusLabel, LV_ALIGN_TOP_LEFT, 0, 50);
auto warning_label = lv_label_create(wrapper); statusLabel = lv_label_create(content_wrapper);
// Warning
auto warning_label = lv_label_create(content_wrapper);
lv_label_set_text(warning_label, "This feature is experimental and uses an unsecured http connection."); lv_label_set_text(warning_label, "This feature is experimental and uses an unsecured http connection.");
lv_obj_set_width(warning_label, LV_PCT(100)); lv_obj_set_width(warning_label, LV_PCT(100));
lv_label_set_long_mode(warning_label, LV_LABEL_LONG_WRAP); lv_label_set_long_mode(warning_label, LV_LABEL_LONG_WRAP);
lv_obj_set_style_text_color(warning_label, lv_color_make(0xff, 0xff, 00), LV_STATE_DEFAULT); lv_obj_set_style_text_color(warning_label, lv_color_make(0xff, 0xff, 00), LV_STATE_DEFAULT);
lv_obj_align(warning_label, LV_ALIGN_TOP_LEFT, 0, 80);
updateViewState(); updateViewState();

View File

@ -65,6 +65,7 @@ public:
void onShow(AppContext& app, lv_obj_t* parent) override { void onShow(AppContext& app, lv_obj_t* parent) override {
displaySettings = settings::display::loadOrGetDefault(); displaySettings = settings::display::loadOrGetDefault();
auto ui_scale = hal::getConfiguration()->uiScale;
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT); lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT);
@ -79,39 +80,49 @@ public:
lv_obj_set_width(main_wrapper, LV_PCT(100)); lv_obj_set_width(main_wrapper, LV_PCT(100));
lv_obj_set_flex_grow(main_wrapper, 1); lv_obj_set_flex_grow(main_wrapper, 1);
// Backlight slider
if (hal_display->supportsBacklightDuty()) { if (hal_display->supportsBacklightDuty()) {
auto* brightness_wrapper = lv_obj_create(main_wrapper); auto* brightness_wrapper = lv_obj_create(main_wrapper);
lv_obj_set_size(brightness_wrapper, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(brightness_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_hor(brightness_wrapper, 0, 0); lv_obj_set_style_pad_hor(brightness_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_ver(brightness_wrapper, 6, 0); lv_obj_set_style_border_width(brightness_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(brightness_wrapper, 0, 0); if (ui_scale != hal::UiScale::Smallest) {
lv_obj_set_style_pad_ver(brightness_wrapper, 4, LV_STATE_DEFAULT);
}
auto* brightness_label = lv_label_create(brightness_wrapper); auto* brightness_label = lv_label_create(brightness_wrapper);
lv_label_set_text(brightness_label, "Brightness"); lv_label_set_text(brightness_label, "Brightness");
lv_obj_align(brightness_label, LV_ALIGN_LEFT_MID, 0, 0);
auto* brightness_slider = lv_slider_create(brightness_wrapper); auto* brightness_slider = lv_slider_create(brightness_wrapper);
lv_obj_set_width(brightness_slider, LV_PCT(50)); lv_obj_set_width(brightness_slider, LV_PCT(50));
lv_obj_align(brightness_slider, LV_ALIGN_TOP_RIGHT, -8, 0); lv_obj_align(brightness_slider, LV_ALIGN_RIGHT_MID, 0, 0);
lv_slider_set_range(brightness_slider, 0, 255); lv_slider_set_range(brightness_slider, 0, 255);
lv_obj_add_event_cb(brightness_slider, onBacklightSliderEvent, LV_EVENT_VALUE_CHANGED, this); lv_obj_add_event_cb(brightness_slider, onBacklightSliderEvent, LV_EVENT_VALUE_CHANGED, this);
lv_slider_set_value(brightness_slider, displaySettings.backlightDuty, LV_ANIM_OFF); lv_slider_set_value(brightness_slider, displaySettings.backlightDuty, LV_ANIM_OFF);
} }
// Gamma slider
if (hal_display->getGammaCurveCount() > 0) { if (hal_display->getGammaCurveCount() > 0) {
auto* gamma_wrapper = lv_obj_create(main_wrapper); auto* gamma_wrapper = lv_obj_create(main_wrapper);
lv_obj_set_size(gamma_wrapper, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(gamma_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_hor(gamma_wrapper, 0, 0); lv_obj_set_style_pad_hor(gamma_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_ver(gamma_wrapper, 6, 0); lv_obj_set_style_border_width(gamma_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(gamma_wrapper, 0, 0); if (ui_scale != hal::UiScale::Smallest) {
lv_obj_set_style_pad_ver(gamma_wrapper, 4, LV_STATE_DEFAULT);
}
auto* gamma_label = lv_label_create(gamma_wrapper); auto* gamma_label = lv_label_create(gamma_wrapper);
lv_label_set_text(gamma_label, "Gamma"); lv_label_set_text(gamma_label, "Gamma");
lv_obj_align(gamma_label, LV_ALIGN_LEFT_MID, 0, 0);
lv_obj_set_y(gamma_label, 0); lv_obj_set_y(gamma_label, 0);
auto* gamma_slider = lv_slider_create(gamma_wrapper); auto* gamma_slider = lv_slider_create(gamma_wrapper);
lv_obj_set_width(gamma_slider, LV_PCT(50)); lv_obj_set_width(gamma_slider, LV_PCT(50));
lv_obj_align(gamma_slider, LV_ALIGN_TOP_RIGHT, -8, 0); lv_obj_align(gamma_slider, LV_ALIGN_RIGHT_MID, 0, 0);
lv_slider_set_range(gamma_slider, 0, hal_display->getGammaCurveCount()); lv_slider_set_range(gamma_slider, 0, hal_display->getGammaCurveCount());
lv_obj_add_event_cb(gamma_slider, onGammaSliderEvent, LV_EVENT_VALUE_CHANGED, this); lv_obj_add_event_cb(gamma_slider, onGammaSliderEvent, LV_EVENT_VALUE_CHANGED, this);
@ -119,19 +130,21 @@ public:
lv_slider_set_value(gamma_slider, curve_index, LV_ANIM_OFF); lv_slider_set_value(gamma_slider, curve_index, LV_ANIM_OFF);
} }
// Orientation
auto* orientation_wrapper = lv_obj_create(main_wrapper); auto* orientation_wrapper = lv_obj_create(main_wrapper);
lv_obj_set_size(orientation_wrapper, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(orientation_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(orientation_wrapper, 0, 0); lv_obj_set_style_pad_all(orientation_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(orientation_wrapper, 0, 0); lv_obj_set_style_border_width(orientation_wrapper, 0, LV_STATE_DEFAULT);
auto* orientation_label = lv_label_create(orientation_wrapper); auto* orientation_label = lv_label_create(orientation_wrapper);
lv_label_set_text(orientation_label, "Orientation"); lv_label_set_text(orientation_label, "Orientation");
lv_obj_align(orientation_label, LV_ALIGN_TOP_LEFT, 0, 8); lv_obj_align(orientation_label, LV_ALIGN_LEFT_MID, 0, 0);
auto* orientation_dropdown = lv_dropdown_create(orientation_wrapper); auto* orientation_dropdown = lv_dropdown_create(orientation_wrapper);
// Note: order correlates with settings::display::Orientation item order // Note: order correlates with settings::display::Orientation item order
lv_dropdown_set_options(orientation_dropdown, "Landscape\nPortrait Right\nLandscape Flipped\nPortrait Left"); lv_dropdown_set_options(orientation_dropdown, "Landscape\nPortrait Right\nLandscape Flipped\nPortrait Left");
lv_obj_align(orientation_dropdown, LV_ALIGN_TOP_RIGHT, 0, 0); lv_obj_align(orientation_dropdown, LV_ALIGN_RIGHT_MID, 0, 0);
lv_obj_set_style_border_color(orientation_dropdown, lv_color_hex(0xFAFAFA), LV_PART_MAIN); lv_obj_set_style_border_color(orientation_dropdown, lv_color_hex(0xFAFAFA), LV_PART_MAIN);
lv_obj_set_style_border_width(orientation_dropdown, 1, LV_PART_MAIN); lv_obj_set_style_border_width(orientation_dropdown, 1, LV_PART_MAIN);
lv_obj_add_event_cb(orientation_dropdown, onOrientationSet, LV_EVENT_VALUE_CHANGED, this); lv_obj_add_event_cb(orientation_dropdown, onOrientationSet, LV_EVENT_VALUE_CHANGED, this);

View File

@ -24,7 +24,7 @@ public:
} }
void onShow(AppContext& appContext, lv_obj_t* parent) override { void onShow(AppContext& appContext, lv_obj_t* parent) override {
view->init(parent); view->init(appContext, parent);
} }
void onResult(AppContext& appContext, TT_UNUSED LaunchId launchId, Result result, std::unique_ptr<Bundle> bundle) override { void onResult(AppContext& appContext, TT_UNUSED LaunchId launchId, Result result, std::unique_ptr<Bundle> bundle) override {

View File

@ -255,11 +255,11 @@ void View::update() {
} }
} }
void View::init(lv_obj_t* parent) { void View::init(const AppContext& appContext, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT); lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT);
auto* toolbar = lvgl::toolbar_create(parent, "Files"); auto* toolbar = lvgl::toolbar_create(parent, appContext);
navigate_up_button = lvgl::toolbar_add_button_action(toolbar, LV_SYMBOL_UP, &onNavigateUpPressedCallback, this); navigate_up_button = lvgl::toolbar_add_button_action(toolbar, LV_SYMBOL_UP, &onNavigateUpPressedCallback, this);
auto* wrapper = lv_obj_create(parent); auto* wrapper = lv_obj_create(parent);

View File

@ -1,4 +1,4 @@
#include "Tactility/service/loader/Loader.h" #include <Tactility/service/loader/Loader.h>
#include <Tactility/Assets.h> #include <Tactility/Assets.h>
#include <Tactility/app/gpio/GpioHal.h> #include <Tactility/app/gpio/GpioHal.h>
#include "Tactility/lvgl/Toolbar.h" #include "Tactility/lvgl/Toolbar.h"
@ -119,11 +119,17 @@ void GpioApp::onShow(AppContext& app, lv_obj_t* parent) {
lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0);
// Main content wrapper, enables scrolling content without scrolling the toolbar // Main content wrapper, enables scrolling content without scrolling the toolbar
auto* wrapper = lv_obj_create(parent); auto* expansion_wrapper = lv_obj_create(parent);
lv_obj_set_width(wrapper, LV_PCT(100)); lv_obj_set_width(expansion_wrapper, LV_PCT(100));
lv_obj_set_flex_grow(wrapper, 1); lv_obj_set_flex_grow(expansion_wrapper, 1);
lv_obj_set_style_border_width(wrapper, 0, LV_STATE_DEFAULT); lv_obj_set_style_border_width(expansion_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_all(wrapper, 0, LV_STATE_DEFAULT); lv_obj_set_style_pad_all(expansion_wrapper, 0, LV_STATE_DEFAULT);
auto* centering_wrapper = lv_obj_create(expansion_wrapper);
lv_obj_set_size(centering_wrapper, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_align(centering_wrapper, LV_ALIGN_CENTER);
lv_obj_set_style_border_width(centering_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_all(centering_wrapper, 0, LV_STATE_DEFAULT);
auto* display = lv_obj_get_display(parent); auto* display = lv_obj_get_display(parent);
auto horizontal_px = lv_display_get_horizontal_resolution(display); auto horizontal_px = lv_display_get_horizontal_resolution(display);
@ -134,14 +140,14 @@ void GpioApp::onShow(AppContext& app, lv_obj_t* parent) {
const auto square_spacing = getSquareSpacing(ui_scale); const auto square_spacing = getSquareSpacing(ui_scale);
int32_t x_spacing = block_width + square_spacing; int32_t x_spacing = block_width + square_spacing;
uint8_t column = 0; uint8_t column = 0;
const uint8_t offset_from_left_label = 4;
const uint8_t column_limit = is_landscape_display ? 10 : 5; const uint8_t column_limit = is_landscape_display ? 10 : 5;
auto* row_wrapper = createGpioRowWrapper(wrapper); auto* row_wrapper = createGpioRowWrapper(centering_wrapper);
lv_obj_align(row_wrapper, LV_ALIGN_TOP_MID, 0, 0); lv_obj_align(row_wrapper, LV_ALIGN_TOP_MID, 0, 0);
mutex.lock(); mutex.lock();
for (int i = GPIO_NUM_MIN; i < GPIO_NUM_MAX; ++i) { for (int i = GPIO_NUM_MIN; i < GPIO_NUM_MAX; ++i) {
constexpr uint8_t offset_from_left_label = 4;
// Add the GPIO number before the first item on a row // Add the GPIO number before the first item on a row
if (column == 0) { if (column == 0) {
@ -165,7 +171,7 @@ void GpioApp::onShow(AppContext& app, lv_obj_t* parent) {
lv_obj_set_pos(postfix, (column + 1) * x_spacing + offset_from_left_label, 0); lv_obj_set_pos(postfix, (column + 1) * x_spacing + offset_from_left_label, 0);
// Add a new row wrapper underneath the last one // Add a new row wrapper underneath the last one
auto* new_row_wrapper = createGpioRowWrapper(wrapper); auto* new_row_wrapper = createGpioRowWrapper(centering_wrapper);
lv_obj_align_to(new_row_wrapper, row_wrapper, LV_ALIGN_BOTTOM_LEFT, 0, square_spacing); lv_obj_align_to(new_row_wrapper, row_wrapper, LV_ALIGN_BOTTOM_LEFT, 0, square_spacing);
row_wrapper = new_row_wrapper; row_wrapper = new_row_wrapper;

View File

@ -91,6 +91,13 @@ public:
lv_obj_set_style_border_width(buttons_wrapper, 0, LV_STATE_DEFAULT); lv_obj_set_style_border_width(buttons_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_flex_grow(buttons_wrapper, 1); 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);
}
const auto* display = lv_obj_get_display(parent); const auto* display = lv_obj_get_display(parent);
const auto horizontal_px = lv_display_get_horizontal_resolution(display); const auto horizontal_px = lv_display_get_horizontal_resolution(display);
const auto vertical_px = lv_display_get_vertical_resolution(display); const auto vertical_px = lv_display_get_vertical_resolution(display);

View File

@ -82,6 +82,8 @@ class LocaleSettingsApp : public App {
public: public:
void onShow(AppContext& app, lv_obj_t* parent) override { void onShow(AppContext& app, lv_obj_t* parent) override {
auto ui_scale = hal::getConfiguration()->uiScale;
textResources.load(); textResources.load();
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
@ -94,6 +96,8 @@ public:
lv_obj_set_width(main_wrapper, LV_PCT(100)); lv_obj_set_width(main_wrapper, LV_PCT(100));
lv_obj_set_flex_grow(main_wrapper, 1); lv_obj_set_flex_grow(main_wrapper, 1);
// Region
auto* region_wrapper = lv_obj_create(main_wrapper); auto* region_wrapper = lv_obj_create(main_wrapper);
lv_obj_set_width(region_wrapper, LV_PCT(100)); lv_obj_set_width(region_wrapper, LV_PCT(100));
lv_obj_set_height(region_wrapper, LV_SIZE_CONTENT); lv_obj_set_height(region_wrapper, LV_SIZE_CONTENT);
@ -104,20 +108,23 @@ public:
lv_label_set_text(regionLabel , textResources[i18n::Text::REGION].c_str()); lv_label_set_text(regionLabel , textResources[i18n::Text::REGION].c_str());
lv_obj_align(regionLabel , LV_ALIGN_LEFT_MID, 0, 0); lv_obj_align(regionLabel , LV_ALIGN_LEFT_MID, 0, 0);
auto* region_button = lv_button_create(region_wrapper);
lv_obj_align(region_button, LV_ALIGN_RIGHT_MID, 0, 0);
auto* region_button_image = lv_image_create(region_button);
lv_obj_add_event_cb(region_button, onConfigureTimeZonePressed, LV_EVENT_SHORT_CLICKED, nullptr);
lv_image_set_src(region_button_image, LV_SYMBOL_SETTINGS);
timeZoneLabel = lv_label_create(region_wrapper); timeZoneLabel = lv_label_create(region_wrapper);
std::string timeZoneName = settings::getTimeZoneName(); std::string timeZoneName = settings::getTimeZoneName();
if (timeZoneName.empty()) { if (timeZoneName.empty()) {
timeZoneName = "not set"; timeZoneName = "not set";
} }
lv_label_set_text(timeZoneLabel, timeZoneName.c_str());
// TODO: Find out why Y offset is needed
lv_obj_align_to(timeZoneLabel, regionLabel, LV_ALIGN_OUT_RIGHT_MID, 10, 8);
auto* region_button = lv_button_create(region_wrapper); lv_label_set_text(timeZoneLabel, timeZoneName.c_str());
lv_obj_align(region_button, LV_ALIGN_TOP_RIGHT, 0, 0); const int offset = ui_scale == hal::UiScale::Smallest ? -2 : -10;
auto* region_button_image = lv_image_create(region_button); lv_obj_align_to(timeZoneLabel, region_button, LV_ALIGN_OUT_LEFT_MID, offset, 0);
lv_obj_add_event_cb(region_button, onConfigureTimeZonePressed, LV_EVENT_SHORT_CLICKED, nullptr);
lv_image_set_src(region_button_image, LV_SYMBOL_SETTINGS); // Language
auto* language_wrapper = lv_obj_create(main_wrapper); auto* language_wrapper = lv_obj_create(main_wrapper);
lv_obj_set_width(language_wrapper, LV_PCT(100)); lv_obj_set_width(language_wrapper, LV_PCT(100));

View File

@ -1,8 +1,8 @@
#include "Tactility/app/selectiondialog/SelectionDialog.h" #include <Tactility/app/selectiondialog/SelectionDialog.h>
#include "Tactility/lvgl/Style.h" #include <Tactility/lvgl/Style.h>
#include "Tactility/lvgl/Toolbar.h" #include <Tactility/lvgl/Toolbar.h>
#include "Tactility/lvgl/LvglSync.h" #include <Tactility/lvgl/LvglSync.h>
#include "Tactility/service/loader/Loader.h" #include <Tactility/service/loader/Loader.h>
#include <sstream> #include <sstream>
#include <vector> #include <vector>
@ -10,18 +10,16 @@
#include <lvgl.h> #include <lvgl.h>
#define TAG "text_viewer"
namespace tt::app::log { namespace tt::app::log {
class LogApp : public App { class LogApp : public App {
private: static constexpr auto* TAG = "LogApp";
LogLevel filterLevel = LogLevel::Info; LogLevel filterLevel = LogLevel::Info;
lv_obj_t* labelWidget = nullptr; lv_obj_t* labelWidget = nullptr;
static inline bool shouldShowLog(LogLevel filterLevel, LogLevel logLevel) { static bool shouldShowLog(LogLevel filterLevel, LogLevel logLevel) {
return filterLevel >= logLevel; return filterLevel >= logLevel;
} }
@ -67,7 +65,8 @@ private:
"Warning", "Warning",
"Error", "Error",
}; };
app::selectiondialog::start("Log Level", items);
selectiondialog::start("Log Level", items);
} }
public: public:

View File

@ -1,5 +1,5 @@
#include "Tactility/TactilityConfig.h" #include <Tactility/TactilityConfig.h>
#include "Tactility/lvgl/Toolbar.h" #include <Tactility/lvgl/Toolbar.h>
#include <Tactility/Assets.h> #include <Tactility/Assets.h>
#include <Tactility/hal/Device.h> #include <Tactility/hal/Device.h>
@ -106,8 +106,8 @@ static void addMemoryBar(lv_obj_t* parent, const char* label, uint64_t free, uin
uint64_t used = total - free; uint64_t used = total - free;
auto* container = lv_obj_create(parent); auto* container = lv_obj_create(parent);
lv_obj_set_size(container, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(container, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(container, 0, 0); lv_obj_set_style_pad_all(container, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(container, 0, 0); lv_obj_set_style_border_width(container, 0, LV_STATE_DEFAULT);
lv_obj_set_flex_flow(container, LV_FLEX_FLOW_ROW); lv_obj_set_flex_flow(container, LV_FLEX_FLOW_ROW);
lv_obj_set_style_bg_opa(container, 0, LV_STATE_DEFAULT); lv_obj_set_style_bg_opa(container, 0, LV_STATE_DEFAULT);
@ -203,7 +203,15 @@ static void addDevices(lv_obj_t* parent) {
} }
} }
class SystemInfoApp : public App { static lv_obj_t* createTab(lv_obj_t* tabview, const char* name) {
auto* tab = lv_tabview_add_tab(tabview, name);
lv_obj_set_flex_flow(tab, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(tab, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(tab, 0, LV_STATE_DEFAULT);
return tab;
}
class SystemInfoApp final : public App {
void onShow(AppContext& app, lv_obj_t* parent) override { void onShow(AppContext& app, lv_obj_t* parent) override {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
@ -212,7 +220,7 @@ class SystemInfoApp : public App {
// This wrapper automatically has its children added vertically underneath eachother // This wrapper automatically has its children added vertically underneath eachother
auto* wrapper = lv_obj_create(parent); auto* wrapper = lv_obj_create(parent);
lv_obj_set_style_border_width(wrapper, 0, 0); lv_obj_set_style_border_width(wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN);
lv_obj_set_width(wrapper, LV_PCT(100)); lv_obj_set_width(wrapper, LV_PCT(100));
lv_obj_set_flex_grow(wrapper, 1); lv_obj_set_flex_grow(wrapper, 1);
@ -224,20 +232,11 @@ class SystemInfoApp : public App {
// Tabs // Tabs
auto* memory_tab = lv_tabview_add_tab(tabview, "Memory"); auto* memory_tab = createTab(tabview, "Memory");
lv_obj_set_flex_flow(memory_tab, LV_FLEX_FLOW_COLUMN); auto* storage_tab = createTab(tabview, "Storage");
lv_obj_set_style_pad_row(memory_tab, 0, LV_STATE_DEFAULT); auto* tasks_tab = createTab(tabview, "Tasks");
auto* storage_tab = lv_tabview_add_tab(tabview, "Storage"); auto* devices_tab = createTab(tabview, "Devices");
lv_obj_set_flex_flow(storage_tab, LV_FLEX_FLOW_COLUMN); auto* about_tab = createTab(tabview, "About");
lv_obj_set_style_pad_row(storage_tab, 0, LV_STATE_DEFAULT);
auto* tasks_tab = lv_tabview_add_tab(tabview, "Tasks");
lv_obj_set_flex_flow(tasks_tab, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(tasks_tab, 4, LV_STATE_DEFAULT);
auto* devices_tab = lv_tabview_add_tab(tabview, "Devices");
lv_obj_set_flex_flow(devices_tab, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(devices_tab, 4, LV_STATE_DEFAULT);
auto* about_tab = lv_tabview_add_tab(tabview, "About");
lv_obj_set_flex_flow(about_tab, LV_FLEX_FLOW_COLUMN);
// Memory tab content // Memory tab content

View File

@ -80,16 +80,16 @@ class WifiApSettings : public App {
auto* auto_connect_wrapper = lv_obj_create(wrapper); auto* auto_connect_wrapper = lv_obj_create(wrapper);
lv_obj_set_size(auto_connect_wrapper, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(auto_connect_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(auto_connect_wrapper, 0, 0); lv_obj_set_style_pad_all(auto_connect_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_gap(auto_connect_wrapper, 0, 0); lv_obj_set_style_pad_gap(auto_connect_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(auto_connect_wrapper, 0, 0); lv_obj_set_style_border_width(auto_connect_wrapper, 0, LV_STATE_DEFAULT);
auto* auto_connect_label = lv_label_create(auto_connect_wrapper); auto* auto_connect_label = lv_label_create(auto_connect_wrapper);
lv_label_set_text(auto_connect_label, "Auto-connect"); lv_label_set_text(auto_connect_label, "Auto-connect");
lv_obj_align(auto_connect_label, LV_ALIGN_TOP_LEFT, 0, 6); lv_obj_align(auto_connect_label, LV_ALIGN_TOP_LEFT, 0, 6);
auto* auto_connect_switch = lv_switch_create(auto_connect_wrapper); auto* auto_connect_switch = lv_switch_create(auto_connect_wrapper);
lv_obj_add_event_cb(auto_connect_switch, onToggleAutoConnect, LV_EVENT_VALUE_CHANGED, (void*)&paremeters); lv_obj_add_event_cb(auto_connect_switch, onToggleAutoConnect, LV_EVENT_VALUE_CHANGED, &paremeters);
lv_obj_align(auto_connect_switch, LV_ALIGN_TOP_RIGHT, 0, 0); lv_obj_align(auto_connect_switch, LV_ALIGN_TOP_RIGHT, 0, 0);
auto* forget_button = lv_button_create(wrapper); auto* forget_button = lv_button_create(wrapper);

View File

@ -83,9 +83,9 @@ void View::createBottomButtons(lv_obj_t* parent) {
auto* button_container = lv_obj_create(parent); auto* button_container = lv_obj_create(parent);
lv_obj_set_width(button_container, LV_PCT(100)); lv_obj_set_width(button_container, LV_PCT(100));
lv_obj_set_height(button_container, LV_SIZE_CONTENT); lv_obj_set_height(button_container, LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(button_container, 0, 0); lv_obj_set_style_pad_all(button_container, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_gap(button_container, 0, 0); lv_obj_set_style_pad_gap(button_container, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(button_container, 0, 0); lv_obj_set_style_border_width(button_container, 0, LV_STATE_DEFAULT);
remember_switch = lv_switch_create(button_container); remember_switch = lv_switch_create(button_container);
lv_obj_add_state(remember_switch, LV_STATE_CHECKED); lv_obj_add_state(remember_switch, LV_STATE_CHECKED);
@ -124,17 +124,17 @@ void View::init(AppContext& app, lv_obj_t* parent) {
auto* ssid_wrapper = lv_obj_create(wrapper); auto* ssid_wrapper = lv_obj_create(wrapper);
lv_obj_set_width(ssid_wrapper, LV_PCT(100)); lv_obj_set_width(ssid_wrapper, LV_PCT(100));
lv_obj_set_height(ssid_wrapper, LV_SIZE_CONTENT); lv_obj_set_height(ssid_wrapper, LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(ssid_wrapper, 0, 0); lv_obj_set_style_pad_all(ssid_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_gap(ssid_wrapper, 0, 0); lv_obj_set_style_pad_gap(ssid_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(ssid_wrapper, 0, 0); lv_obj_set_style_border_width(ssid_wrapper, 0, LV_STATE_DEFAULT);
auto* ssid_label_wrapper = lv_obj_create(ssid_wrapper); auto* ssid_label_wrapper = lv_obj_create(ssid_wrapper);
lv_obj_set_width(ssid_label_wrapper, LV_PCT(50)); lv_obj_set_width(ssid_label_wrapper, LV_PCT(50));
lv_obj_set_height(ssid_label_wrapper, LV_SIZE_CONTENT); lv_obj_set_height(ssid_label_wrapper, LV_SIZE_CONTENT);
lv_obj_align(ssid_label_wrapper, LV_ALIGN_LEFT_MID, 0, 0); lv_obj_align(ssid_label_wrapper, LV_ALIGN_LEFT_MID, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(ssid_label_wrapper, 0, 0); lv_obj_set_style_border_width(ssid_label_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_left(ssid_label_wrapper, 0, 0); lv_obj_set_style_pad_left(ssid_label_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(ssid_label_wrapper, 0, 0); lv_obj_set_style_pad_right(ssid_label_wrapper, 0, LV_STATE_DEFAULT);
auto* ssid_label = lv_label_create(ssid_label_wrapper); auto* ssid_label = lv_label_create(ssid_label_wrapper);
lv_label_set_text(ssid_label, "Network:"); lv_label_set_text(ssid_label, "Network:");
@ -145,7 +145,7 @@ void View::init(AppContext& app, lv_obj_t* parent) {
lv_obj_set_width(ssid_textarea, LV_PCT(50)); lv_obj_set_width(ssid_textarea, LV_PCT(50));
ssid_error = lv_label_create(wrapper); ssid_error = lv_label_create(wrapper);
lv_obj_set_style_text_color(ssid_error, lv_color_make(255, 50, 50), 0); lv_obj_set_style_text_color(ssid_error, lv_color_make(255, 50, 50), LV_STATE_DEFAULT);
lv_obj_add_flag(ssid_error, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(ssid_error, LV_OBJ_FLAG_HIDDEN);
// Password // Password
@ -153,17 +153,17 @@ void View::init(AppContext& app, lv_obj_t* parent) {
auto* password_wrapper = lv_obj_create(wrapper); auto* password_wrapper = lv_obj_create(wrapper);
lv_obj_set_width(password_wrapper, LV_PCT(100)); lv_obj_set_width(password_wrapper, LV_PCT(100));
lv_obj_set_height(password_wrapper, LV_SIZE_CONTENT); lv_obj_set_height(password_wrapper, LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(password_wrapper, 0, 0); lv_obj_set_style_pad_all(password_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_gap(password_wrapper, 0, 0); lv_obj_set_style_pad_gap(password_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(password_wrapper, 0, 0); lv_obj_set_style_border_width(password_wrapper, 0, LV_STATE_DEFAULT);
auto* password_label_wrapper = lv_obj_create(password_wrapper); auto* password_label_wrapper = lv_obj_create(password_wrapper);
lv_obj_set_width(password_label_wrapper, LV_PCT(50)); lv_obj_set_width(password_label_wrapper, LV_PCT(50));
lv_obj_set_height(password_label_wrapper, LV_SIZE_CONTENT); lv_obj_set_height(password_label_wrapper, LV_SIZE_CONTENT);
lv_obj_align_to(password_label_wrapper, password_wrapper, LV_ALIGN_LEFT_MID, 0, 0); lv_obj_align_to(password_label_wrapper, password_wrapper, LV_ALIGN_LEFT_MID, 0, 0);
lv_obj_set_style_border_width(password_label_wrapper, 0, 0); lv_obj_set_style_border_width(password_label_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_left(password_label_wrapper, 0, 0); lv_obj_set_style_pad_left(password_label_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(password_label_wrapper, 0, 0); lv_obj_set_style_pad_right(password_label_wrapper, 0, LV_STATE_DEFAULT);
auto* password_label = lv_label_create(password_label_wrapper); auto* password_label = lv_label_create(password_label_wrapper);
lv_label_set_text(password_label, "Password:"); lv_label_set_text(password_label, "Password:");
@ -175,12 +175,12 @@ void View::init(AppContext& app, lv_obj_t* parent) {
lv_obj_set_width(password_textarea, LV_PCT(50)); lv_obj_set_width(password_textarea, LV_PCT(50));
password_error = lv_label_create(wrapper); password_error = lv_label_create(wrapper);
lv_obj_set_style_text_color(password_error, lv_color_make(255, 50, 50), 0); lv_obj_set_style_text_color(password_error, lv_color_make(255, 50, 50), LV_STATE_DEFAULT);
lv_obj_add_flag(password_error, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(password_error, LV_OBJ_FLAG_HIDDEN);
// Connection error // Connection error
connection_error = lv_label_create(wrapper); connection_error = lv_label_create(wrapper);
lv_obj_set_style_text_color(connection_error, lv_color_make(255, 50, 50), 0); lv_obj_set_style_text_color(connection_error, lv_color_make(255, 50, 50), LV_STATE_DEFAULT);
lv_obj_add_flag(connection_error, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(connection_error, LV_OBJ_FLAG_HIDDEN);
// Bottom buttons // Bottom buttons

View File

@ -95,14 +95,16 @@ static void showDetails(lv_event_t* event) {
} }
void View::createSsidListItem(const service::wifi::ApRecord& record, bool isConnecting) { void View::createSsidListItem(const service::wifi::ApRecord& record, bool isConnecting) {
auto ui_scale = hal::getConfiguration()->uiScale;
auto* wrapper = lv_obj_create(networks_list); auto* wrapper = lv_obj_create(networks_list);
lv_obj_add_event_cb(wrapper, &connect, LV_EVENT_SHORT_CLICKED, bindings); lv_obj_add_event_cb(wrapper, &connect, LV_EVENT_SHORT_CLICKED, bindings);
lv_obj_set_user_data(wrapper, bindings); lv_obj_set_user_data(wrapper, bindings);
lv_obj_set_size(wrapper, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(wrapper, 0, 0); lv_obj_set_style_pad_all(wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_gap(wrapper, 0, 0); lv_obj_set_style_pad_gap(wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_margin_all(wrapper, 0, 0); lv_obj_set_style_margin_all(wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(wrapper, 0, 0); lv_obj_set_style_border_width(wrapper, 0, LV_STATE_DEFAULT);
auto* label = lv_label_create(wrapper); auto* label = lv_label_create(wrapper);
lv_obj_align(label, LV_ALIGN_LEFT_MID, 0, 0); lv_obj_align(label, LV_ALIGN_LEFT_MID, 0, 0);
@ -111,9 +113,17 @@ void View::createSsidListItem(const service::wifi::ApRecord& record, bool isConn
lv_obj_set_width(label, LV_PCT(70)); lv_obj_set_width(label, LV_PCT(70));
auto* info_wrapper = lv_obj_create(wrapper); auto* info_wrapper = lv_obj_create(wrapper);
lv_obj_set_style_pad_all(info_wrapper, 0, 0); lv_obj_set_style_margin_all(info_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_margin_all(info_wrapper, 0, 0); lv_obj_set_align(info_wrapper, LV_ALIGN_RIGHT_MID);
lv_obj_set_size(info_wrapper, 36, 36);
if (ui_scale == hal::UiScale::Smallest) {
lv_obj_set_size(info_wrapper, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_pad_hor(info_wrapper, 4, LV_STATE_DEFAULT);
} else {
lv_obj_set_size(info_wrapper, 36, 36);
lv_obj_set_style_pad_all(info_wrapper, 0, LV_STATE_DEFAULT);
}
lv_obj_set_style_border_color(info_wrapper, lv_theme_get_color_primary(info_wrapper), 0); lv_obj_set_style_border_color(info_wrapper, lv_theme_get_color_primary(info_wrapper), 0);
lv_obj_add_event_cb(info_wrapper, &showDetails, LV_EVENT_SHORT_CLICKED, bindings); lv_obj_add_event_cb(info_wrapper, &showDetails, LV_EVENT_SHORT_CLICKED, bindings);
lv_obj_align(info_wrapper, LV_ALIGN_RIGHT_MID, 0, 0); lv_obj_align(info_wrapper, LV_ALIGN_RIGHT_MID, 0, 0);
@ -124,26 +134,28 @@ void View::createSsidListItem(const service::wifi::ApRecord& record, bool isConn
auto* ssid_label = lv_label_create(info_wrapper); auto* ssid_label = lv_label_create(info_wrapper);
lv_label_set_text(ssid_label, record.ssid.c_str()); lv_label_set_text(ssid_label, record.ssid.c_str());
lv_obj_add_flag(ssid_label, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(ssid_label, LV_OBJ_FLAG_HIDDEN);
lv_obj_set_style_text_color(info_label, lv_theme_get_color_primary(info_wrapper), 0); lv_obj_set_style_text_color(info_label, lv_theme_get_color_primary(info_wrapper), LV_STATE_DEFAULT);
lv_obj_align(info_label, LV_ALIGN_CENTER, 0, 0); lv_obj_align(info_label, LV_ALIGN_CENTER, 0, 0);
if (isConnecting) { if (isConnecting) {
auto* connecting_spinner = tt::lvgl::spinner_create(wrapper); auto* connecting_spinner = lvgl::spinner_create(wrapper);
lv_obj_align_to(connecting_spinner, info_wrapper, LV_ALIGN_OUT_LEFT_MID, -8, 0); auto spinner_offset_x = (ui_scale == hal::UiScale::Smallest) ? -2 : -8;
lv_obj_align_to(connecting_spinner, info_wrapper, LV_ALIGN_OUT_LEFT_MID, spinner_offset_x, 0);
} else { } else {
auto percentage = mapRssiToPercentage(record.rssi); auto percentage = mapRssiToPercentage(record.rssi);
std::string auth_info; std::string auth_info;
if (record.auth_mode == WIFI_AUTH_OPEN) { if (record.auth_mode == WIFI_AUTH_OPEN) {
auth_info = "(open) "; auth_info = "(open)";
} else { } else {
auth_info = ""; auth_info = "";
} }
auto info = std::format("{}{}%", auth_info, percentage); auto signal = std::format("{}{}%", auth_info, percentage);
auto* open_label = lv_label_create(wrapper); auto* signal_label = lv_label_create(wrapper);
lv_label_set_text(open_label, info.c_str()); lv_label_set_text(signal_label, signal.c_str());
lv_obj_align(open_label, LV_ALIGN_RIGHT_MID, -42, 0); auto info_label_offset = (ui_scale == hal::UiScale::Smallest) ? -4 : -16;
lv_obj_align_to(signal_label, info_wrapper, LV_ALIGN_OUT_LEFT_MID, info_label_offset, 0);
} }
} }
@ -178,7 +190,7 @@ void View::updateNetworkList() {
state->withApRecords([this, &connection_target](const std::vector<service::wifi::ApRecord>& apRecords){ state->withApRecords([this, &connection_target](const std::vector<service::wifi::ApRecord>& apRecords){
bool is_connected = !connection_target.empty() && bool is_connected = !connection_target.empty() &&
state->getRadioState() == service::wifi::RadioState::ConnectionActive; state->getRadioState() == ConnectionActive;
bool added_connected = false; bool added_connected = false;
if (is_connected && !apRecords.empty()) { if (is_connected && !apRecords.empty()) {
for (auto &record : apRecords) { for (auto &record : apRecords) {
@ -198,7 +210,7 @@ void View::updateNetworkList() {
if (used_ssids.find(record.ssid) == used_ssids.end()) { if (used_ssids.find(record.ssid) == used_ssids.end()) {
bool connection_target_match = (record.ssid == connection_target); bool connection_target_match = (record.ssid == connection_target);
bool is_connecting = connection_target_match bool is_connecting = connection_target_match
&& state->getRadioState() == service::wifi::RadioState::ConnectionPending && && state->getRadioState() == ConnectionPending &&
!connection_target.empty(); !connection_target.empty();
bool skip = connection_target_match && added_connected; bool skip = connection_target_match && added_connected;
if (!skip) { if (!skip) {
@ -272,6 +284,8 @@ void View::updateEnableOnBootToggle() {
// region Main // region Main
void View::init(const AppContext& app, lv_obj_t* parent) { void View::init(const AppContext& app, lv_obj_t* parent) {
auto ui_scale = hal::getConfiguration()->uiScale;
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT); lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT);
@ -290,45 +304,52 @@ void View::init(const AppContext& app, lv_obj_t* parent) {
// Wrappers // Wrappers
lv_obj_t* secondary_flex = lv_obj_create(parent); auto* flex_wrapper = lv_obj_create(parent);
lv_obj_set_width(secondary_flex, LV_PCT(100)); lv_obj_set_width(flex_wrapper, LV_PCT(100));
lv_obj_set_flex_grow(secondary_flex, 1); lv_obj_set_flex_grow(flex_wrapper, 1);
lv_obj_set_flex_flow(secondary_flex, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(flex_wrapper, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_border_width(secondary_flex, 0, 0); lv_obj_set_style_border_width(flex_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_all(secondary_flex, 0, 0); lv_obj_set_style_pad_all(flex_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_gap(secondary_flex, 0, 0); lv_obj_set_style_pad_gap(flex_wrapper, 0, LV_STATE_DEFAULT);
lvgl::obj_set_style_bg_invisible(secondary_flex); lvgl::obj_set_style_bg_invisible(flex_wrapper);
// align() methods don't work on flex, so we need this extra wrapper // Fixed size content wrapper: align() methods don't work on flex, so we need this extra wrapper
lv_obj_t* wrapper = lv_obj_create(secondary_flex);
lv_obj_set_size(wrapper, LV_PCT(100), LV_SIZE_CONTENT); auto* content_wrapper = lv_obj_create(flex_wrapper);
lvgl::obj_set_style_bg_invisible(wrapper); lv_obj_set_size(content_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_border_width(wrapper, 0, 0); lvgl::obj_set_style_bg_invisible(content_wrapper);
lv_obj_set_style_border_width(content_wrapper, 0, LV_STATE_DEFAULT);
// Enable on boot // Enable on boot
lv_obj_t* enable_label = lv_label_create(wrapper); auto* enable_on_boot_wrapper = lv_obj_create(content_wrapper);
lv_label_set_text(enable_label, "Enable on boot"); lv_obj_set_size(enable_on_boot_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_align(enable_label, LV_ALIGN_TOP_LEFT, 0, 6); lv_obj_set_style_pad_all(enable_on_boot_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(enable_on_boot_wrapper, 0, LV_STATE_DEFAULT);
enable_on_boot_switch = lv_switch_create(wrapper); auto* enable_label = lv_label_create(enable_on_boot_wrapper);
lv_label_set_text(enable_label, "Enable on boot");
lv_obj_align(enable_label, LV_ALIGN_LEFT_MID, 0, 0);
enable_on_boot_switch = lv_switch_create(enable_on_boot_wrapper);
lv_obj_add_event_cb(enable_on_boot_switch, on_enable_on_boot_switch_changed, LV_EVENT_VALUE_CHANGED, bindings); lv_obj_add_event_cb(enable_on_boot_switch, on_enable_on_boot_switch_changed, LV_EVENT_VALUE_CHANGED, bindings);
lv_obj_align(enable_on_boot_switch, LV_ALIGN_TOP_RIGHT, 0, 0); lv_obj_align(enable_on_boot_switch, LV_ALIGN_RIGHT_MID, 0, 0);
// Networks // Networks
networks_list = lv_obj_create(wrapper); networks_list = lv_obj_create(content_wrapper);
lv_obj_set_flex_flow(networks_list, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(networks_list, LV_FLEX_FLOW_COLUMN);
lv_obj_set_width(networks_list, LV_PCT(100)); lv_obj_set_width(networks_list, LV_PCT(100));
lv_obj_set_height(networks_list, LV_SIZE_CONTENT); lv_obj_set_height(networks_list, LV_SIZE_CONTENT);
lv_obj_set_style_pad_top(networks_list, 0, 0); lv_obj_set_style_pad_top(networks_list, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(networks_list, 0, 0); lv_obj_set_style_pad_bottom(networks_list, 0, LV_STATE_DEFAULT);
lv_obj_align(networks_list, LV_ALIGN_TOP_LEFT, 0, 44); const int network_list_y_offset = ui_scale == hal::UiScale::Smallest ? 22 : 44;
lv_obj_align(networks_list, LV_ALIGN_TOP_LEFT, 0, network_list_y_offset);
connect_to_hidden = lv_button_create(secondary_flex); connect_to_hidden = lv_button_create(flex_wrapper);
lv_obj_set_width(connect_to_hidden, LV_PCT(100)); lv_obj_set_width(connect_to_hidden, LV_PCT(100));
lv_obj_set_style_margin_bottom(connect_to_hidden, 8, 0); lv_obj_set_style_margin_bottom(connect_to_hidden, 8, LV_STATE_DEFAULT);
lv_obj_set_style_margin_hor(connect_to_hidden, 12, 0); lv_obj_set_style_margin_hor(connect_to_hidden, 12, LV_STATE_DEFAULT);
lv_obj_add_event_cb(connect_to_hidden, onConnectToHiddenClicked, LV_EVENT_SHORT_CLICKED, bindings); lv_obj_add_event_cb(connect_to_hidden, onConnectToHiddenClicked, LV_EVENT_SHORT_CLICKED, bindings);
auto* connect_to_hidden_label = lv_label_create(connect_to_hidden); auto* connect_to_hidden_label = lv_label_create(connect_to_hidden);
lv_label_set_text(connect_to_hidden_label, "Connect to hidden SSID"); lv_label_set_text(connect_to_hidden_label, "Connect to hidden SSID");

View File

@ -13,6 +13,7 @@
#include <Tactility/settings/Time.h> #include <Tactility/settings/Time.h>
#include <lvgl.h> #include <lvgl.h>
#include <Tactility/Tactility.h>
namespace tt::lvgl { namespace tt::lvgl {
@ -167,15 +168,15 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) {
auto* statusbar = (Statusbar*)obj; auto* statusbar = (Statusbar*)obj;
lv_obj_set_width(obj, LV_PCT(100)); lv_obj_set_width(obj, LV_PCT(100));
lv_obj_set_height(obj, STATUSBAR_HEIGHT); lv_obj_set_style_pad_ver(obj, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_all(obj, 0, 0); lv_obj_set_style_pad_hor(obj, 2, LV_STATE_DEFAULT);
lv_obj_center(obj); lv_obj_center(obj);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW); 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); lv_obj_set_flex_align(obj, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
statusbar->time = lv_label_create(obj); statusbar->time = lv_label_create(obj);
lv_obj_set_style_text_color(statusbar->time, lv_color_white(), 0); lv_obj_set_style_text_color(statusbar->time, lv_color_white(), LV_STATE_DEFAULT);
lv_obj_set_style_margin_left(statusbar->time, 4, 0); lv_obj_set_style_margin_left(statusbar->time, 4, LV_STATE_DEFAULT);
update_time(statusbar); update_time(statusbar);
auto* left_spacer = lv_obj_create(obj); auto* left_spacer = lv_obj_create(obj);
@ -187,7 +188,7 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) {
for (int i = 0; i < STATUSBAR_ICON_LIMIT; ++i) { for (int i = 0; i < STATUSBAR_ICON_LIMIT; ++i) {
auto* image = lv_image_create(obj); auto* image = lv_image_create(obj);
lv_obj_set_size(image, STATUSBAR_ICON_SIZE, STATUSBAR_ICON_SIZE); lv_obj_set_size(image, STATUSBAR_ICON_SIZE, STATUSBAR_ICON_SIZE);
lv_obj_set_style_pad_all(image, 0, 0); lv_obj_set_style_pad_all(image, 0, LV_STATE_DEFAULT);
obj_set_style_bg_blacken(image); obj_set_style_bg_blacken(image);
statusbar->icons[i] = image; statusbar->icons[i] = image;

View File

@ -1,29 +1,21 @@
#define LV_USE_PRIVATE_API 1 // For actual lv_obj_t declaration #define LV_USE_PRIVATE_API 1 // For actual lv_obj_t declaration
#include "Tactility/lvgl/Toolbar.h" #include <Tactility/lvgl/Toolbar.h>
#include "Tactility/service/loader/Loader.h" #include <Tactility/service/loader/Loader.h>
#include "Tactility/lvgl/Style.h" #include <Tactility/lvgl/Style.h>
#include "Tactility/lvgl/Spinner.h" #include <Tactility/lvgl/Spinner.h>
namespace tt::lvgl { namespace tt::lvgl {
static int getToolbarHeight(hal::UiScale uiScale) { static int getToolbarHeight(hal::UiScale uiScale) {
if (uiScale == hal::UiScale::Smallest) { if (uiScale == hal::UiScale::Smallest) {
return 20; return 22;
} else { } else {
return 40; return 40;
} }
} }
static int getToolbarFontHeight(hal::UiScale uiScale) {
if (uiScale == hal::UiScale::Smallest) {
return 14;
} else {
return 18;
}
}
static const _lv_font_t* getToolbarFont(hal::UiScale uiScale) { static const _lv_font_t* getToolbarFont(hal::UiScale uiScale) {
if (uiScale == hal::UiScale::Smallest) { if (uiScale == hal::UiScale::Smallest) {
return &lv_font_montserrat_14; return &lv_font_montserrat_14;
@ -32,6 +24,20 @@ static const _lv_font_t* getToolbarFont(hal::UiScale uiScale) {
} }
} }
/**
* 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) {
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
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;
}
typedef struct { typedef struct {
lv_obj_t obj; lv_obj_t obj;
lv_obj_t* title_label; lv_obj_t* title_label;
@ -43,7 +49,7 @@ typedef struct {
static void toolbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj); static void toolbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj);
static lv_obj_class_t toolbar_class = { static const lv_obj_class_t toolbar_class = {
.base_class = &lv_obj_class, .base_class = &lv_obj_class,
.constructor_cb = &toolbar_constructor, .constructor_cb = &toolbar_constructor,
.destructor_cb = nullptr, .destructor_cb = nullptr,
@ -51,7 +57,7 @@ static lv_obj_class_t toolbar_class = {
.user_data = nullptr, .user_data = nullptr,
.name = nullptr, .name = nullptr,
.width_def = LV_PCT(100), .width_def = LV_PCT(100),
.height_def = 40, .height_def = LV_SIZE_CONTENT,
.editable = false, .editable = false,
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
.instance_size = sizeof(Toolbar), .instance_size = sizeof(Toolbar),
@ -74,45 +80,63 @@ lv_obj_t* toolbar_create(lv_obj_t* parent, const std::string& title) {
LV_LOG_INFO("begin"); LV_LOG_INFO("begin");
auto ui_scale = hal::getConfiguration()->uiScale; auto ui_scale = hal::getConfiguration()->uiScale;
auto toolbar_height = getToolbarHeight(ui_scale); auto toolbar_height = getToolbarHeight(ui_scale);
toolbar_class.height_def = toolbar_height;
lv_obj_t* obj = lv_obj_class_create_obj(&toolbar_class, parent); lv_obj_t* obj = lv_obj_class_create_obj(&toolbar_class, parent);
lv_obj_class_init_obj(obj); lv_obj_class_init_obj(obj);
lv_obj_set_height(obj, toolbar_height);
auto* toolbar = (Toolbar*)obj; 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_all(obj, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_gap(obj, 0, LV_STATE_DEFAULT);
lv_obj_center(obj); lv_obj_center(obj);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW); lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
toolbar->close_button = lv_button_create(obj); 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);
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);
}
lv_obj_set_size(toolbar->close_button, toolbar_height - 4, toolbar_height - 4);
lv_obj_set_style_pad_all(toolbar->close_button, 0, LV_STATE_DEFAULT); lv_obj_set_style_pad_all(toolbar->close_button, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_gap(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); toolbar->close_button_image = lv_image_create(toolbar->close_button);
lv_obj_align(toolbar->close_button_image, LV_ALIGN_CENTER, 0, 0); lv_obj_align(toolbar->close_button_image, LV_ALIGN_CENTER, 0, 0);
toolbar->title_label = lv_label_create(obj); auto* title_wrapper = lv_obj_create(obj);
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_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_scale), LV_STATE_DEFAULT);
lv_label_set_text(toolbar->title_label, title.c_str()); lv_label_set_text(toolbar->title_label, title.c_str());
lv_label_set_long_mode(toolbar->title_label, LV_LABEL_LONG_MODE_SCROLL); 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); lv_obj_set_style_text_align(toolbar->title_label, LV_TEXT_ALIGN_LEFT, LV_STATE_DEFAULT);
lv_obj_set_flex_grow(toolbar->title_label, 1); lv_obj_align(toolbar->title_label, LV_ALIGN_LEFT_MID, 0, 0);
int32_t title_offset_x = (toolbar_height - getToolbarFontHeight(ui_scale) - 8) / 4 * 3; lv_obj_set_width(toolbar->title_label, LV_PCT(100));
// Margin top doesn't work
lv_obj_set_style_pad_top(toolbar->title_label, title_offset_x, LV_STATE_DEFAULT);
lv_obj_set_style_margin_left(toolbar->title_label, 8, LV_STATE_DEFAULT);
// Hack for margin bug where buttons in flex get rendered more narrowly
lv_obj_set_style_margin_right(toolbar->title_label, -8, LV_STATE_DEFAULT);
toolbar->action_container = lv_obj_create(obj); toolbar->action_container = lv_obj_create(obj);
lv_obj_set_width(toolbar->action_container, LV_SIZE_CONTENT); 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_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_all(toolbar->action_container, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(toolbar->action_container, 0, LV_STATE_DEFAULT); lv_obj_set_style_border_width(toolbar->action_container, 0, LV_STATE_DEFAULT);
lv_obj_set_flex_align(toolbar->action_container, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START); lv_obj_set_style_bg_opa(toolbar->action_container, 0, LV_STATE_DEFAULT);
toolbar_set_nav_action(obj, LV_SYMBOL_CLOSE, &stop_app, nullptr); toolbar_set_nav_action(obj, LV_SYMBOL_CLOSE, &stop_app, nullptr);
@ -124,28 +148,35 @@ lv_obj_t* toolbar_create(lv_obj_t* parent, const app::AppContext& app) {
} }
void toolbar_set_title(lv_obj_t* obj, const std::string& title) { void toolbar_set_title(lv_obj_t* obj, const std::string& title) {
auto* toolbar = (Toolbar*)obj; auto* toolbar = reinterpret_cast<Toolbar*>(obj);
lv_label_set_text(toolbar->title_label, title.c_str()); lv_label_set_text(toolbar->title_label, title.c_str());
} }
void toolbar_set_nav_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callback, void* user_data) { void toolbar_set_nav_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callback, void* user_data) {
auto* toolbar = (Toolbar*)obj; auto* toolbar = reinterpret_cast<Toolbar*>(obj);
lv_obj_add_event_cb(toolbar->close_button, callback, LV_EVENT_SHORT_CLICKED, user_data); lv_obj_add_event_cb(toolbar->close_button, callback, LV_EVENT_SHORT_CLICKED, user_data);
lv_image_set_src(toolbar->close_button_image, icon); // e.g. LV_SYMBOL_CLOSE lv_image_set_src(toolbar->close_button_image, icon); // e.g. LV_SYMBOL_CLOSE
} }
lv_obj_t* toolbar_add_button_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callback, void* user_data) { lv_obj_t* toolbar_add_button_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callback, void* user_data) {
auto* toolbar = (Toolbar*)obj; auto* toolbar = reinterpret_cast<Toolbar*>(obj);
tt_check(toolbar->action_count < TOOLBAR_ACTION_LIMIT, "max actions reached"); tt_check(toolbar->action_count < TOOLBAR_ACTION_LIMIT, "max actions reached");
toolbar->action_count++; toolbar->action_count++;
auto ui_scale = hal::getConfiguration()->uiScale; auto ui_scale = hal::getConfiguration()->uiScale;
auto toolbar_height = getToolbarHeight(ui_scale); auto toolbar_height = getToolbarHeight(ui_scale);
lv_obj_t* action_button = lv_button_create(toolbar->action_container); auto* wrapper = create_action_wrapper(toolbar->action_container);
lv_obj_set_size(action_button, toolbar_height - 4, toolbar_height - 4);
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);
}
lv_obj_set_style_pad_all(action_button, 0, LV_STATE_DEFAULT); lv_obj_set_style_pad_all(action_button, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_gap(action_button, 0, LV_STATE_DEFAULT); lv_obj_align(action_button, LV_ALIGN_CENTER, 0, 0);
lv_obj_add_event_cb(action_button, callback, LV_EVENT_SHORT_CLICKED, user_data); lv_obj_add_event_cb(action_button, callback, LV_EVENT_SHORT_CLICKED, user_data);
lv_obj_t* action_button_image = lv_image_create(action_button); lv_obj_t* action_button_image = lv_image_create(action_button);
lv_image_set_src(action_button_image, icon); lv_image_set_src(action_button_image, icon);
@ -155,16 +186,24 @@ lv_obj_t* toolbar_add_button_action(lv_obj_t* obj, const char* icon, lv_event_cb
} }
lv_obj_t* toolbar_add_switch_action(lv_obj_t* obj) { lv_obj_t* toolbar_add_switch_action(lv_obj_t* obj) {
auto* toolbar = (Toolbar*)obj; auto* toolbar = reinterpret_cast<Toolbar*>(obj);
lv_obj_t* widget = lv_switch_create(toolbar->action_container);
lv_obj_set_style_margin_top(widget, 4, LV_STATE_DEFAULT); // Because aligning doesn't work auto* wrapper = create_action_wrapper(toolbar->action_container);
lv_obj_set_style_margin_right(widget, 4, LV_STATE_DEFAULT); lv_obj_set_style_pad_hor(wrapper, 4, LV_STATE_DEFAULT);
lv_obj_t* widget = lv_switch_create(wrapper);
lv_obj_set_align(widget, LV_ALIGN_CENTER);
return widget; return widget;
} }
lv_obj_t* toolbar_add_spinner_action(lv_obj_t* obj) { lv_obj_t* toolbar_add_spinner_action(lv_obj_t* obj) {
auto* toolbar = (Toolbar*)obj; auto* toolbar = reinterpret_cast<Toolbar*>(obj);
return spinner_create(toolbar->action_container);
auto* wrapper = create_action_wrapper(toolbar->action_container);
auto* spinner = spinner_create(wrapper);
lv_obj_set_align(spinner, LV_ALIGN_CENTER);
return spinner;
} }
} // namespace } // namespace

View File

@ -13,6 +13,7 @@ lv_obj_t* __wrap_lv_button_create(lv_obj_t* parent) {
if (tt::hal::getConfiguration()->uiScale == tt::hal::UiScale::Smallest) { if (tt::hal::getConfiguration()->uiScale == tt::hal::UiScale::Smallest) {
lv_obj_set_style_pad_all(button, 2, LV_STATE_DEFAULT); lv_obj_set_style_pad_all(button, 2, LV_STATE_DEFAULT);
lv_obj_set_style_radius(button, 2, LV_STATE_DEFAULT);
} }
return button; return button;

View File

@ -13,17 +13,15 @@ void __wrap_lv_obj_set_flex_flow(lv_obj_t* obj, lv_flex_flow_t flow) {
__real_lv_obj_set_flex_flow(obj, flow); __real_lv_obj_set_flex_flow(obj, flow);
if (tt::hal::getConfiguration()->uiScale == tt::hal::UiScale::Smallest) { if (tt::hal::getConfiguration()->uiScale == tt::hal::UiScale::Smallest) {
lv_obj_set_style_pad_row(obj, 2, LV_STATE_DEFAULT); lv_obj_set_style_pad_gap(obj, 2, LV_STATE_DEFAULT);
lv_obj_set_style_pad_column(obj, 2, LV_STATE_DEFAULT);
} }
} }
lv_obj_t* __wrap_lv_obj_create(lv_obj_t* parent) { lv_obj_t* __wrap_lv_obj_create(lv_obj_t* parent) {
auto obj = __real_lv_obj_create(parent); auto obj = __real_lv_obj_create(parent);
if (tt::hal::getConfiguration()->uiScale == tt::hal::UiScale::Smallest) { if (tt::hal::getConfiguration()->uiScale == tt::hal::UiScale::Smallest) {
lv_obj_set_style_pad_row(obj, 2, LV_STATE_DEFAULT);
lv_obj_set_style_pad_column(obj, 2, LV_STATE_DEFAULT);
lv_obj_set_style_pad_all(obj, 2, LV_STATE_DEFAULT); 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); lv_obj_set_style_radius(obj, 3, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 1, LV_STATE_DEFAULT); lv_obj_set_style_border_width(obj, 1, LV_STATE_DEFAULT);
} }

View File

@ -56,8 +56,9 @@ int32_t GuiService::guiMain() {
lv_obj_t* GuiService::createAppViews(lv_obj_t* parent) { lv_obj_t* GuiService::createAppViews(lv_obj_t* parent) {
lv_obj_send_event(statusbarWidget, LV_EVENT_DRAW_MAIN, nullptr); lv_obj_send_event(statusbarWidget, LV_EVENT_DRAW_MAIN, nullptr);
lv_obj_t* child_container = lv_obj_create(parent); lv_obj_t* child_container = lv_obj_create(parent);
lv_obj_set_style_pad_all(child_container, 0, 0); lv_obj_set_style_pad_all(child_container, 0, LV_STATE_DEFAULT);
lv_obj_set_width(child_container, LV_PCT(100)); lv_obj_set_width(child_container, LV_PCT(100));
lv_obj_set_style_border_width(child_container, 0, LV_STATE_DEFAULT);
lv_obj_set_flex_grow(child_container, 1); lv_obj_set_flex_grow(child_container, 1);
if (softwareKeyboardIsEnabled()) { if (softwareKeyboardIsEnabled()) {
@ -132,21 +133,23 @@ bool GuiService::onStart(TT_UNUSED ServiceContext& service) {
lvgl::lock(portMAX_DELAY); lvgl::lock(portMAX_DELAY);
keyboardGroup = lv_group_create(); keyboardGroup = lv_group_create();
lvgl::obj_set_style_bg_blacken(screen_root); lv_obj_set_style_border_width(screen_root, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_all(screen_root, 0, LV_STATE_DEFAULT);
lv_obj_t* vertical_container = lv_obj_create(screen_root); lv_obj_t* vertical_container = lv_obj_create(screen_root);
lv_obj_set_size(vertical_container, LV_PCT(100), LV_PCT(100)); lv_obj_set_size(vertical_container, LV_PCT(100), LV_PCT(100));
lv_obj_set_flex_flow(vertical_container, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(vertical_container, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_all(vertical_container, 0, 0); lv_obj_set_style_pad_all(vertical_container, 0, LV_STATE_DEFAULT);
lv_obj_set_style_pad_gap(vertical_container, 0, 0); lv_obj_set_style_pad_gap(vertical_container, 0, LV_STATE_DEFAULT);
lvgl::obj_set_style_bg_blacken(vertical_container); lv_obj_set_style_bg_color(vertical_container, lv_color_black(), LV_STATE_DEFAULT);
lv_obj_set_style_border_width(vertical_container, 0, LV_STATE_DEFAULT);
lv_obj_set_style_radius(vertical_container, 0, LV_STATE_DEFAULT);
statusbarWidget = lvgl::statusbar_create(vertical_container); statusbarWidget = lvgl::statusbar_create(vertical_container);
auto* app_container = lv_obj_create(vertical_container); auto* app_container = lv_obj_create(vertical_container);
lv_obj_set_style_pad_all(app_container, 0, 0); lv_obj_set_style_pad_all(app_container, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(app_container, 0, 0); lv_obj_set_style_border_width(app_container, 0, LV_STATE_DEFAULT);
lvgl::obj_set_style_bg_blacken(app_container);
lv_obj_set_width(app_container, LV_PCT(100)); lv_obj_set_width(app_container, LV_PCT(100));
lv_obj_set_flex_grow(app_container, 1); lv_obj_set_flex_grow(app_container, 1);
lv_obj_set_flex_flow(app_container, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(app_container, LV_FLEX_FLOW_COLUMN);

View File

@ -1,3 +1,7 @@
/**
* WARNING: This is only used by the simulator!
*/
/** /**
* @file lv_conf.h * @file lv_conf.h
* Configuration file for v9.0.0 * Configuration file for v9.0.0