Make namespaces more consistent (#92)
This commit is contained in:
committed by
GitHub
parent
ca5d4b8226
commit
a312bd5527
@@ -0,0 +1,20 @@
|
||||
#include "app/display/DisplayPreferences.h"
|
||||
#include "lvgl.h"
|
||||
#include "hal/Configuration.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
void init(const hal::Configuration* config) {
|
||||
hal::SetBacklightDuty set_backlight_duty = config->display.setBacklightDuty;
|
||||
if (set_backlight_duty != nullptr) {
|
||||
int32_t backlight_duty = app::display::preferences_get_backlight_duty();
|
||||
set_backlight_duty(backlight_duty);
|
||||
}
|
||||
|
||||
lv_display_rotation_t rotation = app::display::preferences_get_rotation();
|
||||
if (rotation != lv_disp_get_rotation(lv_disp_get_default())) {
|
||||
lv_disp_set_rotation(lv_disp_get_default(), static_cast<lv_display_rotation_t>(rotation));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,65 @@
|
||||
#include "LabelUtils.h"
|
||||
#include "TactilityCore.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
#define TAG "tt_lv_label"
|
||||
|
||||
static long file_get_size(FILE* file) {
|
||||
long original_offset = ftell(file);
|
||||
|
||||
if (fseek(file, 0, SEEK_END) != 0) {
|
||||
TT_LOG_E(TAG, "fseek failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
long file_size = ftell(file);
|
||||
if (file_size == -1) {
|
||||
TT_LOG_E(TAG, "Could not get file length");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fseek(file, original_offset, SEEK_SET) != 0) {
|
||||
TT_LOG_E(TAG, "fseek Failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return file_size;
|
||||
}
|
||||
|
||||
static char* str_alloc_from_file(const char* filepath) {
|
||||
FILE* file = fopen(filepath, "rb");
|
||||
|
||||
if (file == nullptr) {
|
||||
TT_LOG_E(TAG, "Failed to open %s", filepath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
long content_length = file_get_size(file);
|
||||
|
||||
auto* text_buffer = static_cast<char*>(malloc(content_length + 1));
|
||||
if (text_buffer == nullptr) {
|
||||
TT_LOG_E(TAG, "Insufficient memory. Failed to allocate %ldl bytes.", content_length);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int buffer;
|
||||
uint32_t buffer_offset = 0;
|
||||
text_buffer[0] = 0;
|
||||
while ((buffer = fgetc(file)) != EOF && buffer_offset < content_length) {
|
||||
text_buffer[buffer_offset] = (char)buffer;
|
||||
buffer_offset++;
|
||||
}
|
||||
text_buffer[buffer_offset] = 0;
|
||||
|
||||
fclose(file);
|
||||
return text_buffer;
|
||||
}
|
||||
|
||||
void label_set_text_file(lv_obj_t* label, const char* filepath) {
|
||||
char* text = str_alloc_from_file(filepath);
|
||||
lv_label_set_text(label, text);
|
||||
free(text);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "lvgl.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
void label_set_text_file(lv_obj_t* label, const char* filepath);
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "LvglKeypad.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
static lv_indev_t* keyboard_device = NULL;
|
||||
|
||||
bool keypad_is_available() {
|
||||
return keyboard_device != NULL;
|
||||
}
|
||||
|
||||
void keypad_set_indev(lv_indev_t* device) {
|
||||
keyboard_device = device;
|
||||
}
|
||||
|
||||
void keypad_activate(lv_group_t* group) {
|
||||
if (keyboard_device != NULL) {
|
||||
lv_indev_set_group(keyboard_device, group);
|
||||
}
|
||||
}
|
||||
|
||||
void keypad_deactivate() {
|
||||
if (keyboard_device != NULL) {
|
||||
lv_indev_set_group(keyboard_device, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* This code relates to the hardware keyboard support also known as "keypads" in LVGL.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "lvgl.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
/**
|
||||
* @return true if LVGL is configured with a keypad
|
||||
*/
|
||||
bool keypad_is_available();
|
||||
|
||||
/**
|
||||
* Set the keypad.
|
||||
* @param device the keypad device
|
||||
*/
|
||||
void keypad_set_indev(lv_indev_t* device);
|
||||
|
||||
/**
|
||||
* Activate the keypad for a widget group.
|
||||
* @param group
|
||||
*/
|
||||
void keypad_activate(lv_group_t* group);
|
||||
|
||||
/**
|
||||
* Deactivate the keypad for the current widget group (if any).
|
||||
* You don't have to call this after calling _activate() because widget
|
||||
* cleanup automatically removes itself from the group it belongs to.
|
||||
*/
|
||||
void keypad_deactivate();
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "LvglSync.h"
|
||||
|
||||
#include "Check.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
static LvglLock lock_singleton = nullptr;
|
||||
static LvglUnlock unlock_singleton = nullptr;
|
||||
|
||||
void syncSet(LvglLock lock, LvglUnlock unlock) {
|
||||
lock_singleton = lock;
|
||||
unlock_singleton = unlock;
|
||||
}
|
||||
|
||||
bool lock(uint32_t timeout_ticks) {
|
||||
tt_check(lock_singleton);
|
||||
return lock_singleton(timeout_ticks);
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
tt_check(unlock_singleton);
|
||||
unlock_singleton();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
typedef bool (*LvglLock)(uint32_t timeout_ticks);
|
||||
typedef void (*LvglUnlock)();
|
||||
|
||||
void syncSet(LvglLock lock, LvglUnlock unlock);
|
||||
bool lock(uint32_t timeout_ticks);
|
||||
void unlock();
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "Spacer.h"
|
||||
#include "Style.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
lv_obj_t* spacer_create(lv_obj_t* parent, int32_t width, int32_t height) {
|
||||
lv_obj_t* spacer = lv_obj_create(parent);
|
||||
lv_obj_set_size(spacer, width, height);
|
||||
obj_set_style_bg_invisible(spacer);
|
||||
return spacer;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "lvgl.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
lv_obj_t* spacer_create(lv_obj_t* parent, int32_t width, int32_t height);
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,226 @@
|
||||
#define LV_USE_PRIVATE_API 1 // For actual lv_obj_t declaration
|
||||
#include "Statusbar.h"
|
||||
|
||||
#include "Mutex.h"
|
||||
#include "Pubsub.h"
|
||||
#include "TactilityCore.h"
|
||||
#include "lvgl/Spacer.h"
|
||||
#include "lvgl/Style.h"
|
||||
|
||||
#include "LvglSync.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
#define TAG "statusbar"
|
||||
|
||||
typedef struct {
|
||||
const char* image;
|
||||
bool visible;
|
||||
bool claimed;
|
||||
} StatusbarIcon;
|
||||
|
||||
typedef struct {
|
||||
Mutex* mutex;
|
||||
PubSub* pubsub;
|
||||
StatusbarIcon icons[STATUSBAR_ICON_LIMIT];
|
||||
} StatusbarData;
|
||||
|
||||
static StatusbarData statusbar_data = {
|
||||
.mutex = nullptr,
|
||||
.icons = {}
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
lv_obj_t obj;
|
||||
lv_obj_t* icons[STATUSBAR_ICON_LIMIT];
|
||||
lv_obj_t* battery_icon;
|
||||
PubSubSubscription* pubsub_subscription;
|
||||
} Statusbar;
|
||||
|
||||
static void statusbar_init() {
|
||||
statusbar_data.mutex = tt_mutex_alloc(MutexTypeRecursive);
|
||||
statusbar_data.pubsub = tt_pubsub_alloc();
|
||||
for (int i = 0; i < STATUSBAR_ICON_LIMIT; i++) {
|
||||
statusbar_data.icons[i].image = nullptr;
|
||||
statusbar_data.icons[i].visible = false;
|
||||
statusbar_data.icons[i].claimed = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void statusbar_ensure_initialized() {
|
||||
if (statusbar_data.mutex == nullptr) {
|
||||
statusbar_init();
|
||||
}
|
||||
}
|
||||
|
||||
static void statusbar_lock() {
|
||||
statusbar_ensure_initialized();
|
||||
tt_mutex_acquire(statusbar_data.mutex, TtWaitForever);
|
||||
}
|
||||
|
||||
static void statusbar_unlock() {
|
||||
tt_mutex_release(statusbar_data.mutex);
|
||||
}
|
||||
|
||||
static void statusbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj);
|
||||
static void statusbar_destructor(const lv_obj_class_t* class_p, lv_obj_t* obj);
|
||||
static void statusbar_event(const lv_obj_class_t* class_p, lv_event_t* event);
|
||||
|
||||
static void update_main(Statusbar* statusbar);
|
||||
|
||||
static const lv_obj_class_t statusbar_class = {
|
||||
.base_class = &lv_obj_class,
|
||||
.constructor_cb = &statusbar_constructor,
|
||||
.destructor_cb = &statusbar_destructor,
|
||||
.event_cb = &statusbar_event,
|
||||
.width_def = LV_PCT(100),
|
||||
.height_def = STATUSBAR_HEIGHT,
|
||||
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
|
||||
.instance_size = sizeof(Statusbar)
|
||||
};
|
||||
|
||||
static void statusbar_pubsub_event(TT_UNUSED const void* message, void* obj) {
|
||||
TT_LOG_I(TAG, "event");
|
||||
auto* statusbar = static_cast<Statusbar*>(obj);
|
||||
if (lock(ms_to_ticks(100))) {
|
||||
update_main(statusbar);
|
||||
lv_obj_invalidate(&statusbar->obj);
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
static void statusbar_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_TRACE_OBJ_CREATE("finished");
|
||||
auto* statusbar = (Statusbar*)obj;
|
||||
statusbar_ensure_initialized();
|
||||
statusbar->pubsub_subscription = tt_pubsub_subscribe(statusbar_data.pubsub, &statusbar_pubsub_event, statusbar);
|
||||
}
|
||||
|
||||
static void statusbar_destructor(TT_UNUSED const lv_obj_class_t* class_p, lv_obj_t* obj) {
|
||||
auto* statusbar = (Statusbar*)obj;
|
||||
tt_pubsub_unsubscribe(statusbar_data.pubsub, statusbar->pubsub_subscription);
|
||||
}
|
||||
|
||||
static void update_icon(lv_obj_t* image, const StatusbarIcon* icon) {
|
||||
if (icon->image != nullptr && icon->visible && icon->claimed) {
|
||||
lv_obj_set_style_image_recolor(image, lv_color_white(), 0);
|
||||
lv_obj_set_style_image_recolor_opa(image, 255, 0);
|
||||
lv_image_set_src(image, icon->image);
|
||||
lv_obj_remove_flag(image, LV_OBJ_FLAG_HIDDEN);
|
||||
} else {
|
||||
lv_obj_add_flag(image, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
lv_obj_t* statusbar_create(lv_obj_t* parent) {
|
||||
LV_LOG_INFO("begin");
|
||||
lv_obj_t* obj = lv_obj_class_create_obj(&statusbar_class, parent);
|
||||
lv_obj_class_init_obj(obj);
|
||||
|
||||
auto* statusbar = (Statusbar*)obj;
|
||||
|
||||
lv_obj_set_width(obj, LV_PCT(100));
|
||||
lv_obj_set_height(obj, STATUSBAR_HEIGHT);
|
||||
obj_set_style_no_padding(obj);
|
||||
lv_obj_center(obj);
|
||||
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
|
||||
|
||||
lv_obj_t* left_spacer = spacer_create(obj, 1, 1);
|
||||
lv_obj_set_flex_grow(left_spacer, 1);
|
||||
|
||||
statusbar_lock();
|
||||
for (int i = 0; i < STATUSBAR_ICON_LIMIT; ++i) {
|
||||
lv_obj_t* image = lv_image_create(obj);
|
||||
lv_obj_set_size(image, STATUSBAR_ICON_SIZE, STATUSBAR_ICON_SIZE);
|
||||
obj_set_style_no_padding(image);
|
||||
obj_set_style_bg_blacken(image);
|
||||
statusbar->icons[i] = image;
|
||||
|
||||
update_icon(image, &(statusbar_data.icons[i]));
|
||||
}
|
||||
statusbar_unlock();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void update_main(Statusbar* statusbar) {
|
||||
statusbar_lock();
|
||||
for (int i = 0; i < STATUSBAR_ICON_LIMIT; ++i) {
|
||||
update_icon(statusbar->icons[i], &(statusbar_data.icons[i]));
|
||||
}
|
||||
statusbar_unlock();
|
||||
}
|
||||
|
||||
static void statusbar_event(TT_UNUSED const lv_obj_class_t* class_p, lv_event_t* event) {
|
||||
// Call the ancestor's event handler
|
||||
lv_result_t result = lv_obj_event_base(&statusbar_class, event);
|
||||
if (result != LV_RES_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
lv_event_code_t code = lv_event_get_code(event);
|
||||
auto* obj = static_cast<lv_obj_t*>(lv_event_get_target(event));
|
||||
|
||||
if (code == LV_EVENT_VALUE_CHANGED) {
|
||||
lv_obj_invalidate(obj);
|
||||
} else if (code == LV_EVENT_DRAW_MAIN) {
|
||||
}
|
||||
}
|
||||
|
||||
int8_t statusbar_icon_add(const char* _Nullable image) {
|
||||
statusbar_lock();
|
||||
int8_t result = -1;
|
||||
for (int8_t i = 0; i < STATUSBAR_ICON_LIMIT; ++i) {
|
||||
if (!statusbar_data.icons[i].claimed) {
|
||||
statusbar_data.icons[i].claimed = true;
|
||||
statusbar_data.icons[i].visible = (image != nullptr);
|
||||
statusbar_data.icons[i].image = image;
|
||||
result = i;
|
||||
TT_LOG_I(TAG, "id %d: added", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
tt_pubsub_publish(statusbar_data.pubsub, nullptr);
|
||||
statusbar_unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
void statusbar_icon_remove(int8_t id) {
|
||||
TT_LOG_I(TAG, "id %d: remove", id);
|
||||
tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT);
|
||||
statusbar_lock();
|
||||
StatusbarIcon* icon = &statusbar_data.icons[id];
|
||||
icon->claimed = false;
|
||||
icon->visible = false;
|
||||
icon->image = nullptr;
|
||||
tt_pubsub_publish(statusbar_data.pubsub, nullptr);
|
||||
statusbar_unlock();
|
||||
}
|
||||
|
||||
void statusbar_icon_set_image(int8_t id, const char* image) {
|
||||
TT_LOG_I(TAG, "id %d: set image %s", id, image);
|
||||
tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT);
|
||||
statusbar_lock();
|
||||
StatusbarIcon* icon = &statusbar_data.icons[id];
|
||||
tt_check(icon->claimed);
|
||||
icon->image = image;
|
||||
tt_pubsub_publish(statusbar_data.pubsub, nullptr);
|
||||
statusbar_unlock();
|
||||
}
|
||||
|
||||
void statusbar_icon_set_visibility(int8_t id, bool visible) {
|
||||
TT_LOG_I(TAG, "id %d: set visibility %d", id, visible);
|
||||
tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT);
|
||||
statusbar_lock();
|
||||
StatusbarIcon* icon = &statusbar_data.icons[id];
|
||||
tt_check(icon->claimed);
|
||||
icon->visible = visible;
|
||||
tt_pubsub_publish(statusbar_data.pubsub, nullptr);
|
||||
statusbar_unlock();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "lvgl.h"
|
||||
#include "app/App.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
#define STATUSBAR_ICON_LIMIT 8
|
||||
#define STATUSBAR_ICON_SIZE 20
|
||||
#define STATUSBAR_HEIGHT (STATUSBAR_ICON_SIZE + 4) // 4 extra pixels for border and outline
|
||||
|
||||
lv_obj_t* statusbar_create(lv_obj_t* parent);
|
||||
int8_t statusbar_icon_add(const char* _Nullable image);
|
||||
void statusbar_icon_remove(int8_t id);
|
||||
void statusbar_icon_set_image(int8_t id, const char* image);
|
||||
void statusbar_icon_set_visibility(int8_t id, bool visible);
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "Style.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
void obj_set_style_bg_blacken(lv_obj_t* obj) {
|
||||
lv_obj_set_style_bg_color(obj, lv_color_black(), 0);
|
||||
lv_obj_set_style_border_color(obj, lv_color_black(), 0);
|
||||
}
|
||||
|
||||
void obj_set_style_bg_invisible(lv_obj_t* obj) {
|
||||
lv_obj_set_style_bg_opa(obj, 0, 0);
|
||||
lv_obj_set_style_border_width(obj, 0, 0);
|
||||
}
|
||||
|
||||
void obj_set_style_no_padding(lv_obj_t* obj) {
|
||||
lv_obj_set_style_pad_all(obj, LV_STATE_DEFAULT, 0);
|
||||
lv_obj_set_style_pad_gap(obj, LV_STATE_DEFAULT, 0);
|
||||
}
|
||||
|
||||
void obj_set_style_auto_padding(lv_obj_t* obj) {
|
||||
lv_obj_set_style_pad_top(obj, 8, 0);
|
||||
lv_obj_set_style_pad_bottom(obj, 8, 0);
|
||||
lv_obj_set_style_pad_left(obj, 16, 0);
|
||||
lv_obj_set_style_pad_right(obj, 16, 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "lvgl.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
void obj_set_style_bg_blacken(lv_obj_t* obj);
|
||||
void obj_set_style_bg_invisible(lv_obj_t* obj);
|
||||
void obj_set_style_no_padding(lv_obj_t* obj);
|
||||
|
||||
/**
|
||||
* This is to create automatic padding depending on the screen size.
|
||||
* The larger the screen, the more padding it gets.
|
||||
* TODO: It currently only applies a single basic padding, but will be improved later.
|
||||
*
|
||||
* @param obj
|
||||
*/
|
||||
void obj_set_style_auto_padding(lv_obj_t* obj);
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,124 @@
|
||||
#define LV_USE_PRIVATE_API 1 // For actual lv_obj_t declaration
|
||||
#include "Toolbar.h"
|
||||
#include "Tactility.h"
|
||||
|
||||
#include "service/loader/Loader.h"
|
||||
#include "lvgl/Spacer.h"
|
||||
#include "lvgl/Style.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
typedef struct {
|
||||
lv_obj_t obj;
|
||||
lv_obj_t* title_label;
|
||||
lv_obj_t* close_button;
|
||||
lv_obj_t* close_button_image;
|
||||
lv_obj_t* action_container;
|
||||
ToolbarAction* action_array[TOOLBAR_ACTION_LIMIT];
|
||||
uint8_t action_count;
|
||||
} Toolbar;
|
||||
|
||||
static void toolbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj);
|
||||
|
||||
static const lv_obj_class_t toolbar_class = {
|
||||
.base_class = &lv_obj_class,
|
||||
.constructor_cb = &toolbar_constructor,
|
||||
.destructor_cb = nullptr,
|
||||
.width_def = LV_PCT(100),
|
||||
.height_def = TOOLBAR_HEIGHT,
|
||||
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
|
||||
.instance_size = sizeof(Toolbar),
|
||||
};
|
||||
|
||||
static void stop_app(TT_UNUSED lv_event_t* event) {
|
||||
service::loader::stop_app();
|
||||
}
|
||||
|
||||
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");
|
||||
lv_obj_t* obj = lv_obj_class_create_obj(&toolbar_class, parent);
|
||||
lv_obj_class_init_obj(obj);
|
||||
|
||||
auto* toolbar = (Toolbar*)obj;
|
||||
|
||||
obj_set_style_no_padding(obj);
|
||||
lv_obj_center(obj);
|
||||
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
|
||||
|
||||
int32_t title_offset_x = (TOOLBAR_HEIGHT - TOOLBAR_TITLE_FONT_HEIGHT - 8) / 4 * 3;
|
||||
int32_t title_offset_y = (TOOLBAR_HEIGHT - TOOLBAR_TITLE_FONT_HEIGHT - 8) / 2;
|
||||
|
||||
toolbar->close_button = lv_button_create(obj);
|
||||
lv_obj_set_size(toolbar->close_button, TOOLBAR_HEIGHT - 4, TOOLBAR_HEIGHT - 4);
|
||||
obj_set_style_no_padding(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);
|
||||
|
||||
// Need spacer to avoid button press glitch animation
|
||||
spacer_create(obj, title_offset_x, 1);
|
||||
|
||||
lv_obj_t* label_container = lv_obj_create(obj);
|
||||
obj_set_style_no_padding(label_container);
|
||||
lv_obj_set_style_border_width(label_container, 0, 0);
|
||||
lv_obj_set_height(label_container, LV_PCT(100)); // 2% less due to 4px translate (it's not great, but it works)
|
||||
lv_obj_set_flex_grow(label_container, 1);
|
||||
|
||||
toolbar->title_label = lv_label_create(label_container);
|
||||
lv_obj_set_style_text_font(toolbar->title_label, &lv_font_montserrat_18, 0); // TODO replace with size 18
|
||||
lv_obj_set_height(toolbar->title_label, TOOLBAR_TITLE_FONT_HEIGHT);
|
||||
lv_label_set_text(toolbar->title_label, title.c_str());
|
||||
lv_obj_set_pos(toolbar->title_label, 0, title_offset_y);
|
||||
lv_obj_set_style_text_align(toolbar->title_label, LV_TEXT_ALIGN_LEFT, 0);
|
||||
|
||||
toolbar->action_container = lv_obj_create(obj);
|
||||
lv_obj_set_width(toolbar->action_container, LV_SIZE_CONTENT);
|
||||
lv_obj_set_style_pad_all(toolbar->action_container, 0, 0);
|
||||
lv_obj_set_style_border_width(toolbar->action_container, 0, 0);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
lv_obj_t* toolbar_create(lv_obj_t* parent, app::App app) {
|
||||
const app::Manifest& manifest = app::tt_app_get_manifest(app);
|
||||
lv_obj_t* toolbar = toolbar_create(parent, manifest.name);
|
||||
toolbar_set_nav_action(toolbar, LV_SYMBOL_CLOSE, &stop_app, nullptr);
|
||||
return toolbar;
|
||||
}
|
||||
|
||||
void toolbar_set_title(lv_obj_t* obj, const std::string& title) {
|
||||
auto* toolbar = (Toolbar*)obj;
|
||||
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) {
|
||||
auto* toolbar = (Toolbar*)obj;
|
||||
lv_obj_add_event_cb(toolbar->close_button, callback, LV_EVENT_CLICKED, user_data);
|
||||
lv_image_set_src(toolbar->close_button_image, icon); // e.g. LV_SYMBOL_CLOSE
|
||||
}
|
||||
|
||||
uint8_t toolbar_add_action(lv_obj_t* obj, const char* icon, const char* text, lv_event_cb_t callback, void* user_data) {
|
||||
auto* toolbar = (Toolbar*)obj;
|
||||
uint8_t id = toolbar->action_count;
|
||||
tt_check(toolbar->action_count < TOOLBAR_ACTION_LIMIT, "max actions reached");
|
||||
toolbar->action_count++;
|
||||
|
||||
lv_obj_t* action_button = lv_button_create(toolbar->action_container);
|
||||
lv_obj_set_size(action_button, TOOLBAR_HEIGHT - 4, TOOLBAR_HEIGHT - 4);
|
||||
obj_set_style_no_padding(action_button);
|
||||
lv_obj_add_event_cb(action_button, callback, LV_EVENT_CLICKED, user_data);
|
||||
lv_obj_t* action_button_image = lv_image_create(action_button);
|
||||
lv_image_set_src(action_button_image, icon);
|
||||
lv_obj_align(action_button_image, LV_ALIGN_CENTER, 0, 0);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "lvgl.h"
|
||||
#include "app/App.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
#define TOOLBAR_HEIGHT 40
|
||||
#define TOOLBAR_ACTION_LIMIT 8
|
||||
#define TOOLBAR_TITLE_FONT_HEIGHT 18
|
||||
|
||||
typedef void(*ToolbarActionCallback)(void* _Nullable context);
|
||||
|
||||
typedef struct {
|
||||
const char* icon;
|
||||
const char* text;
|
||||
ToolbarActionCallback callback;
|
||||
void* _Nullable callback_context;
|
||||
} ToolbarAction;
|
||||
|
||||
lv_obj_t* toolbar_create(lv_obj_t* parent, const std::string& title);
|
||||
lv_obj_t* toolbar_create(lv_obj_t* parent, app::App app);
|
||||
void toolbar_set_title(lv_obj_t* obj, const std::string& title);
|
||||
void toolbar_set_nav_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callback, void* user_data);
|
||||
uint8_t toolbar_add_action(lv_obj_t* obj, const char* icon, const char* text, lv_event_cb_t callback, void* user_data);
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user