From 9351076c89cad62e8a33fd2e809e7e81eaa37fca Mon Sep 17 00:00:00 2001 From: gornekich Date: Tue, 19 Apr 2022 22:23:50 +0300 Subject: [PATCH] [FL-2475] Text Box add three dots trim option (#1136) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * introduce text box debug application * text box: add strip to dots option * applications: update text box usage Co-authored-by: あく --- applications/applications.c | 5 + applications/applications.mk | 6 + .../archive/scenes/archive_scene_delete.c | 3 +- applications/debug_tools/text_box_test.c | 126 ++++++++++++++++++ applications/gui/elements.c | 55 ++++---- applications/gui/elements.h | 26 ++-- applications/gui/modules/widget.c | 7 +- applications/gui/modules/widget.h | 26 ++-- .../widget_elements/widget_element_i.h | 3 +- .../widget_elements/widget_element_text_box.c | 8 +- .../scene/ibutton_scene_delete_confirm.cpp | 2 +- .../ibutton/scene/ibutton_scene_info.cpp | 2 +- applications/nfc/scenes/nfc_scene_delete.c | 3 +- .../nfc/scenes/nfc_scene_device_info.c | 2 +- .../nfc/scenes/nfc_scene_emulate_uid.c | 2 +- .../subghz/scenes/subghz_scene_delete_raw.c | 2 +- applications/subghz/views/subghz_read_raw.c | 10 +- 17 files changed, 226 insertions(+), 62 deletions(-) create mode 100644 applications/debug_tools/text_box_test.c mode change 100755 => 100644 applications/gui/modules/widget.c diff --git a/applications/applications.c b/applications/applications.c index aba50fee..6b4c6321 100644 --- a/applications/applications.c +++ b/applications/applications.c @@ -43,6 +43,7 @@ extern int32_t usb_test_app(void* p); extern int32_t vibro_test_app(void* p); extern int32_t bt_hid_app(void* p); extern int32_t battery_test_app(void* p); +extern int32_t text_box_test_app(void* p); // Plugins extern int32_t music_player_app(void* p); @@ -304,6 +305,10 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = { #ifdef APP_BATTERY_TEST {.app = battery_test_app, .name = "Battery Test", .stack_size = 1024, .icon = NULL}, #endif + +#ifdef APP_TEXT_BOX_TEST + {.app = text_box_test_app, .name = "Text Box Test", .stack_size = 1024, .icon = NULL}, +#endif }; const size_t FLIPPER_DEBUG_APPS_COUNT = COUNT_OF(FLIPPER_DEBUG_APPS); diff --git a/applications/applications.mk b/applications/applications.mk index c6295fd8..efe6aa42 100644 --- a/applications/applications.mk +++ b/applications/applications.mk @@ -165,6 +165,12 @@ CFLAGS += -DAPP_DISPLAY_TEST SRV_GUI = 1 endif +APP_TEXT_BOX_TEST ?= 0 +ifeq ($(APP_TEXT_BOX_TEST), 1) +CFLAGS += -DAPP_TEXT_BOX_TEST +SRV_GUI = 1 +endif + APP_BATTERY_TEST ?= 0 ifeq ($(APP_BATTERY_TEST), 1) CFLAGS += -DAPP_BATTERY_TEST diff --git a/applications/archive/scenes/archive_scene_delete.c b/applications/archive/scenes/archive_scene_delete.c index e5bc5df3..fbcba777 100644 --- a/applications/archive/scenes/archive_scene_delete.c +++ b/applications/archive/scenes/archive_scene_delete.c @@ -33,7 +33,8 @@ void archive_scene_delete_on_enter(void* context) { char delete_str[64]; snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", name); - widget_add_text_box_element(app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str); + widget_add_text_box_element( + app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str, false); view_dispatcher_switch_to_view(app->view_dispatcher, ArchiveViewWidget); } diff --git a/applications/debug_tools/text_box_test.c b/applications/debug_tools/text_box_test.c new file mode 100644 index 00000000..1c7ce18b --- /dev/null +++ b/applications/debug_tools/text_box_test.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include + +#define TAG "TextBoxTest" + +static void text_box_center_top_secondary_128x22(Canvas* canvas) { + canvas_draw_frame(canvas, 0, 0, 128, 22); + elements_text_box(canvas, 0, 0, 128, 22, AlignCenter, AlignTop, "secondary font test", false); +} + +static void text_box_right_bottom_bold_128x22(Canvas* canvas) { + canvas_draw_frame(canvas, 0, 0, 128, 22); + elements_text_box( + canvas, 0, 0, 128, 22, AlignRight, AlignBottom, "\e#Bold font test\e#", false); +} + +static void text_box_left_center_mixed_80x50(Canvas* canvas) { + canvas_draw_frame(canvas, 0, 0, 80, 50); + elements_text_box( + canvas, + 0, + 0, + 80, + 50, + AlignLeft, + AlignCenter, + "\e#Never\e# gonna give you up\n\e!Never\e! gonna let you down", + false); +} + +static void text_box_center_center_secondary_110x44(Canvas* canvas) { + canvas_draw_frame(canvas, 4, 20, 110, 30); + elements_text_box( + canvas, + 4, + 20, + 110, + 30, + AlignCenter, + AlignCenter, + "Loooooooooooooo0000000ooong file name from happy 100500 Flipper 0wners", + true); +} + +static void (*text_box_test_render[])(Canvas* canvas) = { + text_box_center_top_secondary_128x22, + text_box_right_bottom_bold_128x22, + text_box_left_center_mixed_80x50, + text_box_center_center_secondary_110x44, +}; + +typedef struct { + uint32_t idx; +} TextBoxTestState; + +static void text_box_test_render_callback(Canvas* canvas, void* ctx) { + TextBoxTestState* state = acquire_mutex((ValueMutex*)ctx, 25); + canvas_clear(canvas); + + text_box_test_render[state->idx](canvas); + + release_mutex((ValueMutex*)ctx, state); +} + +static void text_box_test_input_callback(InputEvent* input_event, void* ctx) { + osMessageQueueId_t event_queue = ctx; + osMessageQueuePut(event_queue, input_event, 0, osWaitForever); +} + +int32_t text_box_test_app(void* p) { + osMessageQueueId_t event_queue = osMessageQueueNew(32, sizeof(InputEvent), NULL); + furi_check(event_queue); + + TextBoxTestState _state = {.idx = 0}; + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, &_state, sizeof(TextBoxTestState))) { + FURI_LOG_E(TAG, "Cannot create mutex"); + return 0; + } + + ViewPort* view_port = view_port_alloc(); + + view_port_draw_callback_set(view_port, text_box_test_render_callback, &state_mutex); + view_port_input_callback_set(view_port, text_box_test_input_callback, event_queue); + + // Open GUI and register view_port + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + uint32_t test_renders_num = SIZEOF_ARRAY(text_box_test_render); + InputEvent event; + while(osMessageQueueGet(event_queue, &event, NULL, osWaitForever) == osOK) { + TextBoxTestState* state = acquire_mutex_block(&state_mutex); + + if(event.type == InputTypeShort) { + if(event.key == InputKeyRight) { + if(state->idx < test_renders_num - 1) { + state->idx++; + } + } else if(event.key == InputKeyLeft) { + if(state->idx > 0) { + state->idx--; + } + } else if(event.key == InputKeyBack) { + release_mutex(&state_mutex, state); + break; + } + } + + release_mutex(&state_mutex, state); + view_port_update(view_port); + } + + // remove & free all stuff created by app + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + osMessageQueueDelete(event_queue); + delete_mutex(&state_mutex); + + furi_record_close("gui"); + + return 0; +} diff --git a/applications/gui/elements.c b/applications/gui/elements.c index 289e4d8e..48d70b3a 100644 --- a/applications/gui/elements.c +++ b/applications/gui/elements.c @@ -547,7 +547,8 @@ void elements_text_box( uint8_t height, Align horizontal, Align vertical, - const char* text) { + const char* text, + bool strip_to_dots) { furi_assert(canvas); ElementTextBoxLine line[ELEMENTS_MAX_LINES_NUM]; @@ -571,6 +572,7 @@ void elements_text_box( uint8_t total_height_default = 0; uint16_t i = 0; bool full_text_processed = false; + uint16_t dots_width = canvas_string_width(canvas, "..."); canvas_set_font(canvas, FontSecondary); @@ -663,31 +665,29 @@ void elements_text_box( } // Set vertical alignment for all lines - if(full_text_processed) { - if(total_height_default < height) { - if(vertical == AlignTop) { - line[0].y = y + line[0].height; - } else if(vertical == AlignCenter) { - line[0].y = y + line[0].height + (height - total_height_default) / 2; - } else if(vertical == AlignBottom) { - line[0].y = y + line[0].height + (height - total_height_default); - } - if(line_num > 1) { - for(uint8_t i = 1; i < line_num; i++) { - line[i].y = line[i - 1].y + line[i - 1].leading_default; - } - } - } else if(line_num > 1) { - uint8_t free_pixel_num = height - total_height_min; - uint8_t fill_pixel = 0; - uint8_t j = 1; + if(total_height_default < height) { + if(vertical == AlignTop) { line[0].y = y + line[0].height; - while(fill_pixel < free_pixel_num) { - line[j].y = line[j - 1].y + line[j - 1].leading_min + 1; - fill_pixel++; - j = j % (line_num - 1) + 1; + } else if(vertical == AlignCenter) { + line[0].y = y + line[0].height + (height - total_height_default) / 2; + } else if(vertical == AlignBottom) { + line[0].y = y + line[0].height + (height - total_height_default); + } + if(line_num > 1) { + for(uint8_t i = 1; i < line_num; i++) { + line[i].y = line[i - 1].y + line[i - 1].leading_default; } } + } else if(line_num > 1) { + uint8_t free_pixel_num = height - total_height_min; + uint8_t fill_pixel = 0; + uint8_t j = 1; + line[0].y = y + line[0].height; + while(fill_pixel < free_pixel_num) { + line[j].y = line[j - 1].y + line[j - 1].leading_min + 1; + fill_pixel++; + j = j % (line_num - 1) + 1; + } } // Draw line by line @@ -733,10 +733,17 @@ void elements_text_box( canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]); canvas_invert_color(canvas); } else { + if((i == line_num - 1) && strip_to_dots) { + uint8_t next_symbol_width = canvas_glyph_width(canvas, line[i].text[j]); + if(line[i].x + next_symbol_width + dots_width > x + width) { + canvas_draw_str(canvas, line[i].x, line[i].y, "..."); + break; + } + } canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]); } line[i].x += canvas_glyph_width(canvas, line[i].text[j]); } } canvas_set_font(canvas, FontSecondary); -} \ No newline at end of file +} diff --git a/applications/gui/elements.h b/applications/gui/elements.h index 33b5fc66..2b8b8d3f 100644 --- a/applications/gui/elements.h +++ b/applications/gui/elements.h @@ -194,17 +194,18 @@ void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width); /** Draw text box element * - * @param canvas Canvas instance - * @param x x coordinate - * @param y y coordinate - * @param width width to fit text - * @param height height to fit text - * @param horizontal Align instance - * @param vertical Align instance - * @param[in] text Formatted text. The following formats are available: - * "\e#Bold text\e#" - bold font is used - * "\e*Monospaced text\e*" - monospaced font is used - * "\e#Inversed text\e#" - white text on black background + * @param canvas Canvas instance + * @param x x coordinate + * @param y y coordinate + * @param width width to fit text + * @param height height to fit text + * @param horizontal Align instance + * @param vertical Align instance + * @param[in] text Formatted text. The following formats are available: + * "\e#Bold text\e#" - bold font is used + * "\e*Monospaced text\e*" - monospaced font is used + * "\e#Inversed text\e#" - white text on black background + * @param strip_to_dots Strip text to ... if does not fit to width */ void elements_text_box( Canvas* canvas, @@ -214,7 +215,8 @@ void elements_text_box( uint8_t height, Align horizontal, Align vertical, - const char* text); + const char* text, + bool strip_to_dots); #ifdef __cplusplus } diff --git a/applications/gui/modules/widget.c b/applications/gui/modules/widget.c old mode 100755 new mode 100644 index 09729ee6..8d7acb01 --- a/applications/gui/modules/widget.c +++ b/applications/gui/modules/widget.c @@ -154,10 +154,11 @@ void widget_add_text_box_element( uint8_t height, Align horizontal, Align vertical, - const char* text) { + const char* text, + bool strip_to_dots) { furi_assert(widget); - WidgetElement* text_box_element = - widget_element_text_box_create(x, y, width, height, horizontal, vertical, text); + WidgetElement* text_box_element = widget_element_text_box_create( + x, y, width, height, horizontal, vertical, text, strip_to_dots); widget_add_element(widget, text_box_element); } diff --git a/applications/gui/modules/widget.h b/applications/gui/modules/widget.h index 98a89435..55af59d7 100755 --- a/applications/gui/modules/widget.h +++ b/applications/gui/modules/widget.h @@ -81,17 +81,18 @@ void widget_add_string_element( /** Add Text Box Element * - * @param widget Widget instance - * @param x x coordinate - * @param y y coordinate - * @param width width to fit text - * @param height height to fit text - * @param horizontal Align instance - * @param vertical Align instance - * @param[in] text Formatted text. The following formats are available: - * "\e#Bold text\e#" - bold font is used - * "\e*Monospaced text\e*" - monospaced font is used - * "\e#Inversed text\e#" - white text on black background + * @param widget Widget instance + * @param x x coordinate + * @param y y coordinate + * @param width width to fit text + * @param height height to fit text + * @param horizontal Align instance + * @param vertical Align instance + * @param[in] text Formatted text. The following formats are available: + * "\e#Bold text\e#" - bold font is used + * "\e*Monospaced text\e*" - monospaced font is used + * "\e#Inversed text\e#" - white text on black background + * @param strip_to_dots Strip text to ... if does not fit to width */ void widget_add_text_box_element( Widget* widget, @@ -101,7 +102,8 @@ void widget_add_text_box_element( uint8_t height, Align horizontal, Align vertical, - const char* text); + const char* text, + bool strip_to_dots); /** Add Button Element * diff --git a/applications/gui/modules/widget_elements/widget_element_i.h b/applications/gui/modules/widget_elements/widget_element_i.h index d7b4e463..bcbd4afd 100755 --- a/applications/gui/modules/widget_elements/widget_element_i.h +++ b/applications/gui/modules/widget_elements/widget_element_i.h @@ -60,7 +60,8 @@ WidgetElement* widget_element_text_box_create( uint8_t height, Align horizontal, Align vertical, - const char* text); + const char* text, + bool strip_to_dots); /** Create button element */ WidgetElement* widget_element_button_create( diff --git a/applications/gui/modules/widget_elements/widget_element_text_box.c b/applications/gui/modules/widget_elements/widget_element_text_box.c index a4dee5f6..4750f8f8 100644 --- a/applications/gui/modules/widget_elements/widget_element_text_box.c +++ b/applications/gui/modules/widget_elements/widget_element_text_box.c @@ -10,6 +10,7 @@ typedef struct { Align horizontal; Align vertical; string_t text; + bool strip_to_dots; } GuiTextBoxModel; static void gui_text_box_draw(Canvas* canvas, WidgetElement* element) { @@ -26,7 +27,8 @@ static void gui_text_box_draw(Canvas* canvas, WidgetElement* element) { model->height, model->horizontal, model->vertical, - string_get_cstr(model->text)); + string_get_cstr(model->text), + model->strip_to_dots); } } @@ -46,7 +48,8 @@ WidgetElement* widget_element_text_box_create( uint8_t height, Align horizontal, Align vertical, - const char* text) { + const char* text, + bool strip_to_dots) { furi_assert(text); // Allocate and init model @@ -58,6 +61,7 @@ WidgetElement* widget_element_text_box_create( model->horizontal = horizontal; model->vertical = vertical; string_init_set_str(model->text, text); + model->strip_to_dots = strip_to_dots; // Allocate and init Element WidgetElement* gui_string = malloc(sizeof(WidgetElement)); diff --git a/applications/ibutton/scene/ibutton_scene_delete_confirm.cpp b/applications/ibutton/scene/ibutton_scene_delete_confirm.cpp index 1c96b4df..8e782e33 100755 --- a/applications/ibutton/scene/ibutton_scene_delete_confirm.cpp +++ b/applications/ibutton/scene/ibutton_scene_delete_confirm.cpp @@ -21,7 +21,7 @@ void iButtonSceneDeleteConfirm::on_enter(iButtonApp* app) { app->set_text_store("\e#Delete %s?\e#", ibutton_key_get_name_p(key)); widget_add_text_box_element( - widget, 0, 0, 128, 27, AlignCenter, AlignCenter, app->get_text_store()); + widget, 0, 0, 128, 27, AlignCenter, AlignCenter, app->get_text_store(), false); widget_add_button_element(widget, GuiButtonTypeLeft, "Cancel", widget_callback, app); widget_add_button_element(widget, GuiButtonTypeRight, "Delete", widget_callback, app); diff --git a/applications/ibutton/scene/ibutton_scene_info.cpp b/applications/ibutton/scene/ibutton_scene_info.cpp index ae39774f..e51b2095 100755 --- a/applications/ibutton/scene/ibutton_scene_info.cpp +++ b/applications/ibutton/scene/ibutton_scene_info.cpp @@ -9,7 +9,7 @@ void iButtonSceneInfo::on_enter(iButtonApp* app) { app->set_text_store("%s", ibutton_key_get_name_p(key)); widget_add_text_box_element( - widget, 0, 0, 128, 28, AlignCenter, AlignCenter, app->get_text_store()); + widget, 0, 0, 128, 28, AlignCenter, AlignCenter, app->get_text_store(), false); switch(ibutton_key_get_type(key)) { case iButtonKeyDS1990: diff --git a/applications/nfc/scenes/nfc_scene_delete.c b/applications/nfc/scenes/nfc_scene_delete.c index 29328743..e8ba3e44 100755 --- a/applications/nfc/scenes/nfc_scene_delete.c +++ b/applications/nfc/scenes/nfc_scene_delete.c @@ -13,7 +13,8 @@ void nfc_scene_delete_on_enter(void* context) { // Setup Custom Widget view char temp_str[64]; snprintf(temp_str, sizeof(temp_str), "\e#Delete %s?\e#", nfc->dev->dev_name); - widget_add_text_box_element(nfc->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, temp_str); + widget_add_text_box_element( + nfc->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, temp_str, false); widget_add_button_element( nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_delete_widget_callback, nfc); widget_add_button_element( diff --git a/applications/nfc/scenes/nfc_scene_device_info.c b/applications/nfc/scenes/nfc_scene_device_info.c index 78e56c09..10851e86 100644 --- a/applications/nfc/scenes/nfc_scene_device_info.c +++ b/applications/nfc/scenes/nfc_scene_device_info.c @@ -36,7 +36,7 @@ void nfc_scene_device_info_on_enter(void* context) { (nfc->dev->format == NfcDeviceSaveFormatBankCard); // Setup Custom Widget view widget_add_text_box_element( - nfc->widget, 0, 0, 128, 22, AlignCenter, AlignTop, nfc->dev->dev_name); + nfc->widget, 0, 0, 128, 22, AlignCenter, AlignTop, nfc->dev->dev_name, false); widget_add_button_element( nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc); if(data_display_supported) { diff --git a/applications/nfc/scenes/nfc_scene_emulate_uid.c b/applications/nfc/scenes/nfc_scene_emulate_uid.c index 3eeb5706..362cb128 100755 --- a/applications/nfc/scenes/nfc_scene_emulate_uid.c +++ b/applications/nfc/scenes/nfc_scene_emulate_uid.c @@ -47,7 +47,7 @@ static void nfc_scene_emulate_uid_widget_config(Nfc* nfc, bool data_received) { } string_strim(info_str); widget_add_text_box_element( - widget, 56, 43, 70, 21, AlignLeft, AlignTop, string_get_cstr(info_str)); + widget, 56, 43, 70, 21, AlignLeft, AlignTop, string_get_cstr(info_str), true); string_clear(info_str); if(data_received) { widget_add_button_element( diff --git a/applications/subghz/scenes/subghz_scene_delete_raw.c b/applications/subghz/scenes/subghz_scene_delete_raw.c index df57926c..d24ab8d8 100644 --- a/applications/subghz/scenes/subghz_scene_delete_raw.c +++ b/applications/subghz/scenes/subghz_scene_delete_raw.c @@ -24,7 +24,7 @@ void subghz_scene_delete_raw_on_enter(void* context) { char delete_str[64]; snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", subghz->file_name); widget_add_text_box_element( - subghz->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str); + subghz->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str, false); widget_add_string_element( subghz->widget, 38, 25, AlignLeft, AlignTop, FontSecondary, "RAW signal"); diff --git a/applications/subghz/views/subghz_read_raw.c b/applications/subghz/views/subghz_read_raw.c index c5b1f09b..3a261268 100644 --- a/applications/subghz/views/subghz_read_raw.c +++ b/applications/subghz/views/subghz_read_raw.c @@ -236,7 +236,15 @@ void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) { elements_button_center(canvas, "Send"); elements_button_right(canvas, "More"); elements_text_box( - canvas, 4, 12, 110, 44, AlignCenter, AlignCenter, string_get_cstr(model->file_name)); + canvas, + 4, + 12, + 110, + 44, + AlignCenter, + AlignCenter, + string_get_cstr(model->file_name), + true); break; case SubGhzReadRAWStatusTX: