diff --git a/applications/services/gui/modules/submenu.c b/applications/services/gui/modules/submenu.c index fa53cbde1ec..987d4981fa4 100644 --- a/applications/services/gui/modules/submenu.c +++ b/applications/services/gui/modules/submenu.c @@ -7,6 +7,7 @@ struct Submenu { View* view; + FuriTimer* locked_timer; }; @@ -15,6 +16,7 @@ typedef struct { uint32_t index; SubmenuItemCallback callback; void* callback_context; + bool locked; FuriString* locked_message; } SubmenuItem; @@ -64,6 +66,7 @@ typedef struct { FuriString* header; size_t position; size_t window_position; + bool locked_message_visible; bool is_vertical; } SubmenuModel; @@ -72,9 +75,9 @@ static void submenu_process_up(Submenu* submenu); static void submenu_process_down(Submenu* submenu); static void submenu_process_ok(Submenu* submenu); -static size_t submenu_items_on_screen(bool header, bool vertical) { - size_t res = (vertical) ? 8 : 4; - return (header) ? res - 1 : res; +static size_t submenu_items_on_screen(SubmenuModel* model) { + size_t res = (model->is_vertical) ? 8 : 4; + return (furi_string_empty(model->header)) ? res : res - 1; } static void submenu_view_draw_callback(Canvas* canvas, void* _model) { @@ -97,9 +100,9 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); SubmenuItemArray_next(it)) { const size_t item_position = position - model->window_position; - const size_t items_on_screen = - submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical); + const size_t items_on_screen = submenu_items_on_screen(model); uint8_t y_offset = furi_string_empty(model->header) ? 0 : item_height; + bool is_locked = SubmenuItemArray_cref(it)->locked; if(item_position < items_on_screen) { if(position == model->position) { @@ -115,7 +118,7 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { canvas_set_color(canvas, ColorBlack); } - if(SubmenuItemArray_cref(it)->locked) { + if(is_locked) { canvas_draw_icon( canvas, item_width - 10, @@ -123,10 +126,8 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { &I_Lock_7x8); } - FuriString* disp_str; - disp_str = furi_string_alloc_set(SubmenuItemArray_cref(it)->label); - elements_string_fit_width( - canvas, disp_str, item_width - (SubmenuItemArray_cref(it)->locked ? 21 : 11)); + FuriString* disp_str = furi_string_alloc_set(SubmenuItemArray_cref(it)->label); + elements_string_fit_width(canvas, disp_str, item_width - (is_locked ? 21 : 11)); canvas_draw_str( canvas, @@ -157,25 +158,14 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { canvas_draw_rframe(canvas, frame_x, frame_y, frame_width, frame_height, 3); canvas_draw_rframe(canvas, frame_x + 1, frame_y + 1, frame_width - 2, frame_height - 2, 2); - if(model->is_vertical) { - elements_multiline_text_aligned( - canvas, - 32, - 42, - AlignCenter, - AlignCenter, - furi_string_get_cstr( - SubmenuItemArray_get(model->items, model->position)->locked_message)); - } else { - elements_multiline_text_aligned( - canvas, - 84, - 32, - AlignCenter, - AlignCenter, - furi_string_get_cstr( - SubmenuItemArray_get(model->items, model->position)->locked_message)); - } + elements_multiline_text_aligned( + canvas, + (model->is_vertical) ? 32 : 84, + (model->is_vertical) ? 42 : 32, + AlignCenter, + AlignCenter, + furi_string_get_cstr( + SubmenuItemArray_get(model->items, model->position)->locked_message)); } } @@ -191,7 +181,7 @@ static bool submenu_view_input_callback(InputEvent* event, void* context) { { locked_message_visible = model->locked_message_visible; }, false); - if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && + if((event->type != InputTypePress && event->type != InputTypeRelease) && locked_message_visible) { with_view_model( submenu->view, SubmenuModel * model, { model->locked_message_visible = false; }, true); @@ -301,7 +291,7 @@ void submenu_add_lockable_item( furi_check(label); furi_check(submenu); if(locked) { - furi_assert(locked_message); + furi_check(locked_message); } with_view_model( @@ -367,8 +357,7 @@ void submenu_set_selected_item(Submenu* submenu, uint32_t index) { model->window_position -= 1; } - const size_t items_on_screen = - submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical); + const size_t items_on_screen = submenu_items_on_screen(model); if(items_size <= items_on_screen) { model->window_position = 0; @@ -387,8 +376,7 @@ void submenu_process_up(Submenu* submenu) { submenu->view, SubmenuModel * model, { - const size_t items_on_screen = - submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical); + const size_t items_on_screen = submenu_items_on_screen(model); const size_t items_size = SubmenuItemArray_size(model->items); if(model->position > 0) { @@ -411,8 +399,7 @@ void submenu_process_down(Submenu* submenu) { submenu->view, SubmenuModel * model, { - const size_t items_on_screen = - submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical); + const size_t items_on_screen = submenu_items_on_screen(model); const size_t items_size = SubmenuItemArray_size(model->items); if(model->position < items_size - 1) { @@ -470,10 +457,8 @@ void submenu_set_header(Submenu* submenu, const char* header) { void submenu_set_orientation(Submenu* submenu, ViewOrientation orientation) { furi_check(submenu); - const bool is_vertical = - (orientation == ViewOrientationVertical || orientation == ViewOrientationVerticalFlip) ? - true : - false; + const bool is_vertical = orientation == ViewOrientationVertical || + orientation == ViewOrientationVerticalFlip; view_set_orientation(submenu->view, orientation); @@ -487,8 +472,7 @@ void submenu_set_orientation(Submenu* submenu, ViewOrientation orientation) { // Need if _set_orientation is called after _set_selected_item size_t position = model->position; const size_t items_size = SubmenuItemArray_size(model->items); - const size_t items_on_screen = - submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical); + const size_t items_on_screen = submenu_items_on_screen(model); if(position >= items_size) { position = 0; diff --git a/applications/services/gui/modules/submenu.h b/applications/services/gui/modules/submenu.h index 0688b986762..e9b122f0c4d 100644 --- a/applications/services/gui/modules/submenu.h +++ b/applications/services/gui/modules/submenu.h @@ -87,14 +87,14 @@ void submenu_reset(Submenu* submenu); void submenu_set_selected_item(Submenu* submenu, uint32_t index); /** Set optional header for submenu - * Must be called before adding items OR after adding items but also call set_selected_item() after set_header() + * Must be called before adding items OR after adding items and before set_selected_item() * * @param submenu Submenu instance * @param header header to set */ void submenu_set_header(Submenu* submenu, const char* header); -/** Set Orientation +/** Set submenu orientation * * @param submenu Submenu instance * @param orientation either vertical or horizontal diff --git a/applications/services/gui/modules/variable_item_list.c b/applications/services/gui/modules/variable_item_list.c index 4f0f24d86a7..1ea10c2c8d9 100644 --- a/applications/services/gui/modules/variable_item_list.c +++ b/applications/services/gui/modules/variable_item_list.c @@ -45,9 +45,9 @@ static void variable_item_list_process_left(VariableItemList* variable_item_list static void variable_item_list_process_right(VariableItemList* variable_item_list); static void variable_item_list_process_ok(VariableItemList* variable_item_list); -static size_t variable_item_list_items_on_screen(bool header) { +static size_t variable_item_list_items_on_screen(VariableItemListModel* model) { size_t res = 4; - return (header) ? res - 1 : res; + return (furi_string_empty(model->header)) ? res : res - 1; } static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { @@ -63,15 +63,14 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str(canvas, 4, 11, furi_string_get_cstr(model->header)); } - canvas_set_font(canvas, FontSecondary); - uint8_t position = 0; VariableItemArray_it_t it; + + canvas_set_font(canvas, FontSecondary); for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it); VariableItemArray_next(it)) { uint8_t item_position = position - model->window_position; - uint8_t items_on_screen = - variable_item_list_items_on_screen(!furi_string_empty(model->header)); + uint8_t items_on_screen = variable_item_list_items_on_screen(model); uint8_t y_offset = furi_string_empty(model->header) ? 0 : item_height; if(item_position < items_on_screen) { @@ -85,7 +84,7 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { elements_slightly_rounded_box(canvas, 0, item_y + 1, item_width, item_height - 2); canvas_set_color(canvas, ColorWhite); scroll_counter = model->scroll_counter; - if(scroll_counter < 1) { + if(scroll_counter < 1) { // Show text beginning a little longer scroll_counter = 0; } else { scroll_counter -= 1; @@ -94,37 +93,40 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { canvas_set_color(canvas, ColorBlack); } - uint8_t temp_x_default = 73; - uint8_t temp_w_default = 66; - if(item->current_value_index == 0 && furi_string_empty(item->current_value_text)) { - // Only left text, no right text - canvas_draw_str(canvas, 6, item_text_y, furi_string_get_cstr(item->label)); - } else { - if(furi_string_size(item->current_value_text) < (size_t)4) { - temp_x_default = 80; - temp_w_default = 71; - } - elements_scrollable_text_line_str( - canvas, - 6, - item_text_y, - temp_w_default, - furi_string_get_cstr(item->label), - scroll_counter, - false, - false); + uint8_t value_pos_x = 73; + uint8_t label_width = 66; + if(item->locked) { + // Span label up to lock icon + value_pos_x = 110; + label_width = 100; + } else if(item->current_value_index == 0 && furi_string_empty(item->current_value_text)) { + // Only label text, no value text, show longer label + label_width = 109; + } else if(furi_string_size(item->current_value_text) < 4U) { + // Smaller value section for short values + value_pos_x = 80; + label_width = 71; } + elements_scrollable_text_line( + canvas, + 6, + item_text_y, + label_width, + item->label, + scroll_counter, + (position != model->position)); + if(item->locked) { - canvas_draw_icon(canvas, 110, item_text_y - 8, &I_Lock_7x8); + canvas_draw_icon(canvas, value_pos_x, item_text_y - 8, &I_Lock_7x8); } else { if(item->current_value_index > 0) { - canvas_draw_str(canvas, temp_x_default, item_text_y, "<"); + canvas_draw_str(canvas, value_pos_x, item_text_y, "<"); } elements_scrollable_text_line( canvas, - (115 + temp_x_default) / 2 + 1, + (115 + value_pos_x) / 2 + 1, item_text_y, 37, item->current_value_text, @@ -168,9 +170,10 @@ void variable_item_list_set_selected_item(VariableItemList* variable_item_list, VariableItemListModel * model, { uint8_t position = index; - const size_t items_size = VariableItemArray_size(model->items); + const size_t items_count = VariableItemArray_size(model->items); + uint8_t items_on_screen = variable_item_list_items_on_screen(model); - if(position >= items_size) { + if(position >= items_count) { position = 0; } @@ -181,13 +184,10 @@ void variable_item_list_set_selected_item(VariableItemList* variable_item_list, model->window_position -= 1; } - uint8_t items_on_screen = - variable_item_list_items_on_screen(!furi_string_empty(model->header)); - - if(items_size <= items_on_screen) { + if(items_count <= items_on_screen) { model->window_position = 0; } else { - const size_t pos = items_size - items_on_screen; + const size_t pos = items_count - items_on_screen; if(model->window_position > pos) { model->window_position = pos; } @@ -205,7 +205,7 @@ uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_it } void variable_item_list_set_header(VariableItemList* variable_item_list, const char* header) { - furi_assert(variable_item_list); + furi_check(variable_item_list); with_view_model( variable_item_list->view, @@ -232,7 +232,7 @@ static bool variable_item_list_input_callback(InputEvent* event, void* context) { locked_message_visible = model->locked_message_visible; }, false); - if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && + if((event->type != InputTypePress && event->type != InputTypeRelease) && locked_message_visible) { with_view_model( variable_item_list->view, @@ -295,8 +295,7 @@ void variable_item_list_process_up(VariableItemList* variable_item_list) { variable_item_list->view, VariableItemListModel * model, { - uint8_t items_on_screen = - variable_item_list_items_on_screen(!furi_string_empty(model->header)); + uint8_t items_on_screen = variable_item_list_items_on_screen(model); if(model->position > 0) { model->position--; @@ -319,8 +318,7 @@ void variable_item_list_process_down(VariableItemList* variable_item_list) { variable_item_list->view, VariableItemListModel * model, { - uint8_t items_on_screen = - variable_item_list_items_on_screen(!furi_string_empty(model->header)); + uint8_t items_on_screen = variable_item_list_items_on_screen(model); if(model->position < (VariableItemArray_size(model->items) - 1)) { model->position++; if((model->position - model->window_position) > (items_on_screen - 2) && @@ -416,6 +414,7 @@ void variable_item_list_process_ok(VariableItemList* variable_item_list) { } static void variable_item_list_scroll_timer_callback(void* context) { + furi_assert(context); VariableItemList* variable_item_list = context; with_view_model( variable_item_list->view, @@ -546,8 +545,8 @@ VariableItem* variable_item_list_add( } VariableItem* variable_item_list_get(VariableItemList* variable_item_list, uint8_t position) { + furi_check(variable_item_list); VariableItem* item = NULL; - furi_assert(variable_item_list); with_view_model( variable_item_list->view, @@ -589,6 +588,8 @@ void variable_item_set_values_count(VariableItem* item, uint8_t values_count) { } void variable_item_set_item_label(VariableItem* item, const char* label) { + furi_check(item); + furi_check(label); furi_string_set(item->label, label); } @@ -598,6 +599,7 @@ void variable_item_set_current_value_text(VariableItem* item, const char* curren } void variable_item_set_locked(VariableItem* item, bool locked, const char* locked_message) { + furi_check(item); item->locked = locked; if(locked_message) { furi_string_set(item->locked_message, locked_message); diff --git a/applications/services/gui/modules/variable_item_list.h b/applications/services/gui/modules/variable_item_list.h index ae9838fa1ac..030f7719e36 100644 --- a/applications/services/gui/modules/variable_item_list.h +++ b/applications/services/gui/modules/variable_item_list.h @@ -59,10 +59,10 @@ VariableItem* variable_item_list_add( VariableItemChangeCallback change_callback, void* context); -/** Get item in VariableItemList +/** Get pre-existing item instance in VariableItemList * * @param variable_item_list VariableItemList instance - * @param position index of the item to get + * @param position index of the item instance to get * * @return VariableItem* item instance */ @@ -84,7 +84,7 @@ void variable_item_list_set_selected_item(VariableItemList* variable_item_list, uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_item_list); /** Set optional header for variable item list - * Must be called before adding items OR after adding items but also call set_selected_item() after set_header() + * Must be called before adding items OR after adding items and before set_selected_item() * * @param variable_item_list VariableItemList instance * @param header header to set @@ -105,7 +105,7 @@ void variable_item_set_current_value_index(VariableItem* item, uint8_t current_v */ void variable_item_set_values_count(VariableItem* item, uint8_t values_count); -/** Set number of values for item +/** Set new label for item * * @param item VariableItem* instance * @param label The new label text @@ -120,6 +120,7 @@ void variable_item_set_item_label(VariableItem* item, const char* label); void variable_item_set_current_value_text(VariableItem* item, const char* current_value_text); /** Set item locked state and text + * Locked message is saved, you can set_locked(item, false, "Message") and then set_locked(item, true, NULL), it will use previous locked message * * @param item VariableItem* instance * @param locked Is item locked boolean