Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Data/system/fonts/MaterialSymbolsRounded.codepoints
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,38 @@ check e668
circle ef4a
close e5cd
cloud f15c
content_copy e14d
content_cut e14e
content_paste e14f
create_new_folder e2cc
delete e92e
deployed_code f720
description e873
devices e326
display_settings eb97
download f090
draft e66d
edit f097
edit_note e745
eject e8fb
electric_bolt ec1c
folder e2c7
folder_open e2c8
forum e8af
gamepad e30f
hard_disk f3da
help e8fd
hub e9f4
image e3f4
info e88e
keyboard_alt f028
keyboard_arrow_up e316
language ea07
lightbulb e90f
lightstrip fff75
link e250
lists e9b9
location_off e0c7
location_on f1db
logo_dev ead6
mail e159
Expand All @@ -54,11 +67,16 @@ network_wifi_2_bar ebd6
network_wifi_3_bar ebe1
note_add e89c
palette e40a
phone_android f2db
play_arrow e037
power_settings_new f8c7
refresh e5d5
save e161
save_as eb60
sd_card e623
sd_card_alert f057
search ef7a
select_window_2 f4c8
settings e8b8
signal_wifi_0_bar f0b0
signal_wifi_4_bar f065
Expand Down
Binary file modified Data/system/fonts/MaterialSymbolsRounded.ttf
Binary file not shown.
4 changes: 2 additions & 2 deletions Devices/m5stack-tab5/Source/devices/devices_v2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ void tab5_create_devices_v2(Device* i2c0) {
.ldo_voltage_mv = 2500,
.dsi_bus_id = 0,
.num_data_lanes = 2,
.lane_bit_rate_mbps = 965, // ST7123 lane bitrate per M5Stack BSP
.dpi_clock_freq_mhz = 70,
.lane_bit_rate_mbps = 1040, // Deviates from M5Stack BSP to fix flickering
.dpi_clock_freq_mhz = 80,
.hsync_pulse_width = 2,
.hsync_back_porch = 40,
.hsync_front_porch = 40,
Expand Down
4 changes: 2 additions & 2 deletions Devices/m5stack-tab5/Source/devices/devices_v3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ void tab5_create_devices_v3(Device* i2c0) {
.ldo_voltage_mv = 2500,
.dsi_bus_id = 0,
.num_data_lanes = 2,
.lane_bit_rate_mbps = 965, // ST7121 lane bitrate per M5Stack BSP (same as ST7123)
.dpi_clock_freq_mhz = 70,
.lane_bit_rate_mbps = 1040, // Deviates from M5Stack BSP to fix flickering
.dpi_clock_freq_mhz = 80,
.hsync_pulse_width = 2,
.hsync_back_porch = 40,
.hsync_front_porch = 40,
Expand Down
4 changes: 4 additions & 0 deletions Modules/lvgl-module/generate-icons.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ def generate_icon_names(codepoint_map: dict, codepoint_names: list, variable_nam
"play_arrow",
"eject",
"create_new_folder",
# Notes app
"save",
"save_as",
"folder_open",
]

# Get more from https://fonts.google.com/icons?icon.set=Material+Symbols&icon.style=Rounded
Expand Down
2 changes: 1 addition & 1 deletion Modules/lvgl-module/include/lvgl/icons/names.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ extern "C" {
#endif

/** Changes whenever the icon TTF subset, its codepoints file or the icon name lists change */
#define LVGL_ICON_FONT_VERSION 0xB2450B17u
#define LVGL_ICON_FONT_VERSION 0xD5578751u

// Icon names per icon font, resolved through the .codepoints file next to the TTF
extern const char* const lvgl_icon_shared_names[];
Expand Down
3 changes: 3 additions & 0 deletions Modules/lvgl-module/include/lvgl/icons/shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,6 @@
#define LVGL_ICON_SHARED_PLAY_ARROW "\xEE\x80\xB7"
#define LVGL_ICON_SHARED_EJECT "\xEE\xA3\xBB"
#define LVGL_ICON_SHARED_CREATE_NEW_FOLDER "\xEE\x8B\x8C"
#define LVGL_ICON_SHARED_SAVE "\xEE\x85\xA1"
#define LVGL_ICON_SHARED_SAVE_AS "\xEE\xAD\xA0"
#define LVGL_ICON_SHARED_FOLDER_OPEN "\xEE\x8B\x88"
3 changes: 3 additions & 0 deletions Modules/lvgl-module/source/icon_names.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ const char* const lvgl_icon_shared_names[] = {
"play_arrow",
"eject",
"create_new_folder",
"save",
"save_as",
"folder_open",
};

const size_t lvgl_icon_shared_name_count = sizeof(lvgl_icon_shared_names) / sizeof(lvgl_icon_shared_names[0]);
Expand Down
170 changes: 118 additions & 52 deletions Tactility/Source/app/notes/Notes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
#include <lvgl_window_manager/window_manager.h>

#include <lvgl.h>
#include <lvgl/fonts.h>
#include <lvgl/grid_navigation.h>
#include <lvgl/icons/shared.h>
#include <lvgl/insets.h>
#include <lvgl/lvgl.h>
#include <lvgl/widgets/card.h>
#include <lvgl/widgets/icon_button.h>
#include <lvgl/widgets/toolbar.h>
#include <tactility/check.h>
#include <tactility/log.h>
Expand All @@ -33,7 +39,9 @@ struct Context {
TaskEventGroup* eventGroup = nullptr;

lv_obj_t* uiCurrentFileName = nullptr;
lv_obj_t* uiDropDownMenu = nullptr;
lv_obj_t* uiToolbar = nullptr;
lv_obj_t* uiOverflowButton = nullptr;
lv_obj_t* uiOverlay = nullptr;
lv_obj_t* uiNoteText = nullptr;

std::string filePath;
Expand Down Expand Up @@ -77,67 +85,111 @@ bool saveFile(Context* ctx, const std::string& path) {
return result;
}

void appNotesEventCb(lv_event_t* e) {
void hideOverlay(Context* ctx) {
if (lv_obj_is_hidden(ctx->uiOverlay)) {
return;
}
lv_obj_set_hidden(ctx->uiOverlay, true);

// Keys continue on the button that opened the overlay
lv_group_t* group = lv_group_get_default();
if (group != nullptr && lv_group_get_focused(group) == ctx->uiOverlay) {
lv_group_focus_obj(ctx->uiToolbar);
lv_gridnav_set_focused(ctx->uiToolbar, ctx->uiOverflowButton, LV_ANIM_OFF);
}
}

void onNewPressed(lv_event_t* e) {
auto* ctx = static_cast<Context*>(lv_event_get_user_data(e));
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t* obj = lv_event_get_target_obj(e);

if (code == LV_EVENT_VALUE_CHANGED) {
if (obj == ctx->uiDropDownMenu) {
switch (lv_dropdown_get_selected(obj)) {
case 0: // New
resetFileContent(ctx);
break;
case 1: // Save
if (!ctx->filePath.empty()) {
lvgl_lock();
ctx->saveBuffer = lv_textarea_get_text(ctx->uiNoteText);
lvgl_unlock();
saveFile(ctx, ctx->filePath);
}
break;
case 2: // Save as...
lvgl_lock();
ctx->saveBuffer = lv_textarea_get_text(ctx->uiNoteText);
lvgl_unlock();
ctx->saveFileLaunchId = fileselection::startForExistingOrNewFile(ctx->appInstanceId, ctx->saveResultStream, ctx->saveResultBuffer, sizeof(ctx->saveResultBuffer), ctx->eventGroup);
LOG_I(TAG, "launched with id %u", ctx->saveFileLaunchId);
break;
case 3: // Load
ctx->loadFileLaunchId = fileselection::startForExistingFile(ctx->appInstanceId, ctx->loadResultStream, ctx->loadResultBuffer, sizeof(ctx->loadResultBuffer), ctx->eventGroup);
LOG_I(TAG, "launched with id %u", ctx->loadFileLaunchId);
break;
}
} else {
auto* cont = lv_event_get_current_target_obj(e);
if (obj == cont) return;
if (lv_obj_get_child(cont, 1)) {
ctx->saveFileLaunchId = fileselection::startForExistingOrNewFile(ctx->appInstanceId, ctx->saveResultStream, ctx->saveResultBuffer, sizeof(ctx->saveResultBuffer), ctx->eventGroup);
LOG_I(TAG, "launched with id %u", ctx->saveFileLaunchId);
} else { //Reset
resetFileContent(ctx);
}
hideOverlay(ctx);
resetFileContent(ctx);
}

void onSavePressed(lv_event_t* e) {
auto* ctx = static_cast<Context*>(lv_event_get_user_data(e));
hideOverlay(ctx);
ctx->saveBuffer = lv_textarea_get_text(ctx->uiNoteText);
saveFile(ctx, ctx->filePath);
}

void onSaveAsPressed(lv_event_t* e) {
auto* ctx = static_cast<Context*>(lv_event_get_user_data(e));
hideOverlay(ctx);
ctx->saveBuffer = lv_textarea_get_text(ctx->uiNoteText);
ctx->saveFileLaunchId = fileselection::startForExistingOrNewFile(ctx->appInstanceId, ctx->saveResultStream, ctx->saveResultBuffer, sizeof(ctx->saveResultBuffer), ctx->eventGroup);
LOG_I(TAG, "launched with id %u", ctx->saveFileLaunchId);
}

void onOpenPressed(lv_event_t* e) {
auto* ctx = static_cast<Context*>(lv_event_get_user_data(e));
hideOverlay(ctx);
ctx->loadFileLaunchId = fileselection::startForExistingFile(ctx->appInstanceId, ctx->loadResultStream, ctx->loadResultBuffer, sizeof(ctx->loadResultBuffer), ctx->eventGroup);
LOG_I(TAG, "launched with id %u", ctx->loadFileLaunchId);
}

void addOverlayButton(Context* ctx, const char* icon, lv_event_cb_t callback) {
auto* button = lvgl_icon_button_create(ctx->uiOverlay);
auto* label = lv_label_create(button);
lv_obj_set_style_text_font(label, lvgl_get_shared_icon_default_font(), LV_STATE_DEFAULT);
lv_label_set_text(label, icon);
lv_obj_add_event_cb(button, callback, LV_EVENT_SHORT_CLICKED, ctx);
}

void showOverlay(Context* ctx) {
lv_obj_clean(ctx->uiOverlay);
addOverlayButton(ctx, LVGL_ICON_SHARED_NOTE_ADD, onNewPressed);
// Saving needs a file, which "save as" picks
if (!ctx->filePath.empty()) {
addOverlayButton(ctx, LVGL_ICON_SHARED_SAVE, onSavePressed);
}
addOverlayButton(ctx, LVGL_ICON_SHARED_SAVE_AS, onSaveAsPressed);
addOverlayButton(ctx, LVGL_ICON_SHARED_FOLDER_OPEN, onOpenPressed);

lv_obj_set_hidden(ctx->uiOverlay, false);
lv_obj_align_to(ctx->uiOverlay, ctx->uiToolbar, LV_ALIGN_OUT_BOTTOM_MID, 0, 0);

lv_group_t* group = lv_group_get_default();
if (group != nullptr) {
lv_group_focus_obj(ctx->uiOverlay);
lv_gridnav_set_focused(ctx->uiOverlay, lv_obj_get_child(ctx->uiOverlay, 0), LV_ANIM_OFF);
// Touch shows no selection, until a key moves the focus
lv_indev_t* indev = lv_indev_active();
if (indev != nullptr && lv_indev_get_type(indev) == LV_INDEV_TYPE_POINTER) {
lvgl_focus_hide_key_selection(group);
}
}
}

void onOverflowPressed(lv_event_t* e) {
auto* ctx = static_cast<Context*>(lv_event_get_user_data(e));
if (lv_obj_is_hidden(ctx->uiOverlay)) {
showOverlay(ctx);
} else {
hideOverlay(ctx);
}
}

void onOverlayKey(lv_event_t* e) {
if (lv_event_get_key(e) == LV_KEY_ESC) {
hideOverlay(static_cast<Context*>(lv_event_get_user_data(e)));
}
}

void onNoteTextPressed(lv_event_t* e) {
hideOverlay(static_cast<Context*>(lv_event_get_user_data(e)));
}

void createWidgets(lv_obj_t* parent, void* userData) {
auto* ctx = static_cast<Context*>(userData);

lv_obj_set_scrollable(parent, false);
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT);

lv_obj_t* toolbar = lvgl_toolbar_create(parent, "Notes");
lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0);

ctx->uiDropDownMenu = lv_dropdown_create(toolbar);
lv_dropdown_set_options(ctx->uiDropDownMenu, LV_SYMBOL_FILE " New File\n" LV_SYMBOL_SAVE " Save\n" LV_SYMBOL_SAVE " Save As...\n" LV_SYMBOL_DIRECTORY " Open File");
lv_dropdown_set_text(ctx->uiDropDownMenu, "Menu");
lv_dropdown_set_symbol(ctx->uiDropDownMenu, LV_SYMBOL_DOWN);
lv_dropdown_set_selected_highlight(ctx->uiDropDownMenu, false);
lv_obj_align(ctx->uiDropDownMenu, LV_ALIGN_RIGHT_MID, 0, 0);
lv_obj_add_event_cb(ctx->uiDropDownMenu, appNotesEventCb, LV_EVENT_VALUE_CHANGED, ctx);
ctx->uiToolbar = lvgl_toolbar_create(parent, "Notes");
lv_obj_align(ctx->uiToolbar, LV_ALIGN_TOP_MID, 0, 0);
ctx->uiOverflowButton = lvgl_toolbar_add_text_button_action(ctx->uiToolbar, LVGL_ICON_SHARED_MORE_VERT, onOverflowPressed, ctx);
lv_obj_set_style_text_font(ctx->uiOverflowButton, lvgl_get_shared_icon_default_font(), LV_STATE_DEFAULT);

lv_obj_t* wrapper = lv_obj_create(parent);
lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN);
Expand All @@ -158,17 +210,18 @@ void createWidgets(lv_obj_t* parent, void* userData) {
lv_obj_set_style_bg_color(ctx->uiNoteText, lv_color_hex(0x262626), LV_PART_MAIN);
}
lv_textarea_set_placeholder_text(ctx->uiNoteText, "Notes...");
lv_obj_add_event_cb(ctx->uiNoteText, onNoteTextPressed, LV_EVENT_PRESSED, ctx);

lv_obj_t* footer = lv_obj_create(wrapper);
lv_obj_set_flex_flow(footer, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(footer, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
// Shows the screen background
lv_obj_set_style_bg_opa(footer, LV_OPA_TRANSP, LV_PART_MAIN);
if (lv_display_get_color_format(lv_obj_get_display(parent)) == LV_COLOR_FORMAT_L8) {
lv_obj_set_style_bg_color(footer, lv_color_hex(0xEEEEEE), LV_PART_MAIN);
lv_obj_set_style_border_width(footer, 1, LV_PART_MAIN);
lv_obj_set_style_border_color(footer, lv_theme_get_color_secondary(footer), LV_PART_MAIN);
lv_obj_set_style_border_side(footer, LV_BORDER_SIDE_TOP, LV_PART_MAIN);
} else {
lv_obj_set_style_bg_color(footer, lv_color_hex(0x262626), LV_PART_MAIN);
lv_obj_set_style_border_width(footer, 0, LV_PART_MAIN);
}
lv_obj_set_width(footer, LV_PCT(100));
Expand All @@ -183,6 +236,19 @@ void createWidgets(lv_obj_t* parent, void* userData) {
lv_label_set_text(ctx->uiCurrentFileName, "Untitled");
lv_obj_align(ctx->uiCurrentFileName, LV_ALIGN_CENTER, 0, 0);

// Floating over the text area, below the toolbar
ctx->uiOverlay = lvgl_card_create(parent);
lv_obj_set_floating(ctx->uiOverlay, true);
lv_obj_set_size(ctx->uiOverlay, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_flex_flow(ctx->uiOverlay, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(ctx->uiOverlay, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_scrollable(ctx->uiOverlay, false);
lvgl_obj_add_edge_padding(ctx->uiOverlay);
// The arrow keys move between the overlay's buttons as one row
lvgl_grid_navigation_add(ctx->uiOverlay);
lv_obj_add_event_cb(ctx->uiOverlay, onOverlayKey, LV_EVENT_KEY, ctx);
lv_obj_set_hidden(ctx->uiOverlay, true);

if (!ctx->filePath.empty()) {
openFile(ctx, ctx->filePath);
}
Expand Down
Loading