Skip to content

Commit 93be638

Browse files
committed
fw/applib/ui/menu_layer: add scroll wrap around capability
Signed-off-by: Paul Chanvin <[email protected]>
1 parent fc3167d commit 93be638

File tree

13 files changed

+94
-1
lines changed

13 files changed

+94
-1
lines changed

src/fw/applib/ui/menu_layer.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,57 @@ static void prv_menu_select_long_click_handler(ClickRecognizerRef recognizer,
7676
}
7777
}
7878

79+
static inline uint16_t prv_menu_layer_get_num_sections(MenuLayer *menu_layer);
80+
static inline uint16_t prv_menu_layer_get_num_rows(MenuLayer *menu_layer, uint16_t section_index);
81+
82+
//! Handle the menu scroll wrap around
83+
//! @param menu_layer reference to the current MenuLayer
84+
//! @param recognizer reference to the ClickRecognizer struct
85+
//! @param scrolling_up `true` if scrolling up, `false` if scrolling down
86+
//! @return `true` if a wrap around has been applied
87+
static bool prv_menu_scroll_handle_wrap_around(MenuLayer *menu_layer, ClickRecognizerRef recognizer, bool scrolling_up) {
88+
const uint8_t current_scroll_action = scrolling_up ? MenuLayerRepeatScrollingUp : MenuLayerRepeatScrollingDown;
89+
const bool is_repeating = click_recognizer_is_repeating(recognizer);
90+
91+
if (is_repeating) {
92+
menu_layer->cache.button_repeat_scrolling = current_scroll_action;
93+
return false;
94+
}
95+
menu_layer->cache.button_repeat_scrolling = MenuLayerNoRepeatScrolling;
96+
97+
MenuIndex current_index = menu_layer->selection.index;
98+
int last_index_section = prv_menu_layer_get_num_sections(menu_layer) - 1;
99+
int last_index_row = prv_menu_layer_get_num_rows(menu_layer, last_index_section) - 1;
100+
MenuIndex first_index = MenuIndex(0, 0);
101+
MenuIndex last_index = MenuIndex(last_index_section, last_index_row);
102+
MenuIndex *wraparound_dest_index;
103+
if ((menu_index_compare(&current_index, &first_index) == 0) && scrolling_up) {
104+
wraparound_dest_index = &last_index;
105+
} else if ((menu_index_compare(&current_index, &last_index) == 0) && !scrolling_up) {
106+
wraparound_dest_index = &first_index;
107+
} else {
108+
return false;
109+
}
110+
111+
const bool animated = true;
112+
menu_layer_set_selected_index(menu_layer, *wraparound_dest_index, MenuRowAlignCenter, animated);
113+
return true;
114+
}
115+
79116
void menu_up_click_handler(ClickRecognizerRef recognizer, MenuLayer *menu_layer) {
80117
const bool up = true;
118+
if (menu_layer->scroll_wrap_around && prv_menu_scroll_handle_wrap_around(menu_layer, recognizer, up)) {
119+
return;
120+
}
81121
const bool animated = true;
82122
menu_layer_set_selected_next(menu_layer, up, MenuRowAlignCenter, animated);
83123
}
84124

85125
void menu_down_click_handler(ClickRecognizerRef recognizer, MenuLayer *menu_layer) {
86126
const bool up = false;
127+
if (menu_layer->scroll_wrap_around && prv_menu_scroll_handle_wrap_around(menu_layer, recognizer, up)) {
128+
return;
129+
}
87130
const bool animated = true;
88131
menu_layer_set_selected_next(menu_layer, up, MenuRowAlignCenter, animated);
89132
}
@@ -1293,3 +1336,14 @@ void menu_layer_set_center_focused(MenuLayer *menu_layer, bool center_focused) {
12931336
prv_set_center_focused(menu_layer, center_focused);
12941337
menu_layer_update_caches(menu_layer);
12951338
}
1339+
1340+
bool menu_layer_get_scroll_wrap_around(MenuLayer *menu_layer) {
1341+
return menu_layer->scroll_wrap_around;
1342+
}
1343+
1344+
void menu_layer_set_scroll_wrap_around(MenuLayer *menu_layer, bool scroll_wrap_around) {
1345+
if (!menu_layer) {
1346+
return;
1347+
}
1348+
menu_layer->scroll_wrap_around = scroll_wrap_around;
1349+
}

src/fw/applib/ui/menu_layer.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,12 @@ enum {
338338
_Static_assert(MenuLayerColor_Count == 2, "Bad enum MenuLayerColor");
339339
#endif
340340

341+
enum {
342+
MenuLayerNoRepeatScrolling = 0,
343+
MenuLayerRepeatScrollingUp = 1,
344+
MenuLayerRepeatScrollingDown = 2,
345+
};
346+
341347
//! Data structure of a MenuLayer.
342348
//! @note a `MenuLayer *` can safely be casted to a `Layer *` and
343349
//! `ScrollLayer *` and can thus be used with all other functions that take a
@@ -359,6 +365,8 @@ typedef struct MenuLayer {
359365
//! @internal
360366
//! Cell index + geometry cache of a cell that was in frame during the last redraw
361367
MenuCellSpan cursor;
368+
369+
uint8_t button_repeat_scrolling:2;
362370
} cache;
363371
//! @internal
364372
//! Selected cell index + geometery cache of the selected cell
@@ -401,10 +409,14 @@ typedef struct MenuLayer {
401409
//! independent of the scrolling animation.
402410
bool selection_animation_disabled:1;
403411

412+
//! If true, the MenuLayer will be able to wrap around the first element and the last element
413+
//! when scrolling.
414+
bool scroll_wrap_around:1;
415+
404416
//! Add some padding to keep track of the \ref MenuLayer size budget.
405417
//! As long as the size stays within this budget, 2.x apps can safely use the 3.x MenuLayer type.
406418
//! When padding is removed, the assertion below should also be removed.
407-
uint8_t padding[44];
419+
uint8_t padding[40];
408420
} MenuLayer;
409421

410422
//! Padding used below the last item in pixels
@@ -612,6 +624,19 @@ bool menu_layer_get_center_focused(MenuLayer *menu_layer);
612624
//! @see \ref menu_layer_get_center_focused
613625
void menu_layer_set_center_focused(MenuLayer *menu_layer, bool center_focused);
614626

627+
//! True, if the \ref MenuLayer can wrap around the first and last element.
628+
//! @see \ref menu_layer_set_scroll_wrap_around
629+
bool menu_layer_get_scroll_wrap_around(MenuLayer *menu_layer);
630+
631+
//! Controls if the \ref MenuLayer can wrap around from the first element to the last when going
632+
//! up and from the last element to the first when going down.
633+
//! Even enabled, wrap around will stay disabled when holding down the navigation buttons (up or down).
634+
//! Defaults to false for every platform
635+
//! @param menu_layer The menu layer for which to enable or disable the behavior.
636+
//! @param scroll_wrap_around true = enable the wrap around, false = disable it.
637+
//! @see \ref menu_layer_get_scroll_wrap_around
638+
void menu_layer_set_scroll_wrap_around(MenuLayer *menu_layer, bool scroll_wrap_around);
639+
615640

616641
//! @} // end addtogroup MenuLayer
617642
//! @} // end addtogroup Layer

src/fw/apps/system_apps/alarms/alarm_editor.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ static void prv_setup_day_picker_window(AlarmEditorData *data) {
259259
ALARMS_APP_HIGHLIGHT_COLOR,
260260
GColorWhite);
261261
menu_layer_set_click_config_onto_window(&data->day_picker_menu_layer, &data->day_picker_window);
262+
menu_layer_set_scroll_wrap_around(&data->day_picker_menu_layer, true);
262263
layer_add_child(&data->day_picker_window.layer,
263264
menu_layer_get_layer(&data->day_picker_menu_layer));
264265
if (!alarm_get_kind(data->alarm_id, &data->alarm_kind)) {
@@ -414,6 +415,7 @@ static void prv_setup_custom_day_picker_window(AlarmEditorData *data) {
414415
GColorWhite);
415416
menu_layer_set_click_config_onto_window(&data->custom_day_picker_menu_layer,
416417
&data->custom_day_picker_window);
418+
menu_layer_set_scroll_wrap_around(&data->custom_day_picker_menu_layer, true);
417419
layer_add_child(&data->custom_day_picker_window.layer,
418420
menu_layer_get_layer(&data->custom_day_picker_menu_layer));
419421
gbitmap_init_with_resource(&data->selected_icon, RESOURCE_ID_CHECKBOX_ICON_CHECKED);

src/fw/apps/system_apps/alarms/alarms.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ static void prv_handle_init(void) {
402402

403403
menu_layer_set_highlight_colors(&data->menu_layer, ALARMS_APP_HIGHLIGHT_COLOR, GColorWhite);
404404
menu_layer_set_click_config_onto_window(&data->menu_layer, &data->window);
405+
menu_layer_set_scroll_wrap_around(&data->menu_layer, true);
405406
layer_add_child(&data->window.layer, menu_layer_get_layer(&data->menu_layer));
406407

407408
status_bar_layer_init(&data->status_layer);

src/fw/apps/system_apps/health/health_detail_card.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,7 @@ HealthDetailCard *health_detail_card_create(const HealthDetailCardConfig *config
391391
menu_layer_set_normal_colors(menu_layer, detail_card->bg_color, GColorWhite);
392392
menu_layer_set_highlight_colors(menu_layer, detail_card->bg_color, GColorBlack);
393393
menu_layer_set_click_config_onto_window(menu_layer, &detail_card->window);
394+
menu_layer_set_scroll_wrap_around(menu_layer, true);
394395
layer_add_child(&detail_card->window.layer, menu_layer_get_layer(menu_layer));
395396

396397
// setup content indicators

src/fw/apps/system_apps/launcher/default/launcher_menu_layer.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ void launcher_menu_layer_init(LauncherMenuLayer *launcher_menu_layer,
202202
.get_cell_height = prv_menu_layer_get_cell_height,
203203
.selection_will_change = prv_menu_layer_selection_will_change,
204204
});
205+
menu_layer_set_scroll_wrap_around(menu_layer, true);
205206

206207
// Only setup the content indicator on round
207208
#if PBL_ROUND

src/fw/apps/system_apps/notifications_app.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,7 @@ static void prv_window_load(Window *window) {
701701
GColorWhite);
702702

703703
menu_layer_set_click_config_onto_window(menu_layer, window);
704+
menu_layer_set_scroll_wrap_around(menu_layer, true);
704705
layer_add_child(&window->layer, menu_layer_get_layer(menu_layer));
705706

706707
TextLayer *text_layer = &data->text_layer;

src/fw/apps/system_apps/send_text/send_text.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ static void prv_init(void) {
325325

326326
menu_layer_set_highlight_colors(&data->menu_layer, SEND_TEXT_APP_HIGHLIGHT_COLOR, GColorWhite);
327327
menu_layer_set_click_config_onto_window(&data->menu_layer, &data->window);
328+
menu_layer_set_scroll_wrap_around(&data->menu_layer, true);
328329
layer_add_child(&data->window.layer, menu_layer_get_layer(&data->menu_layer));
329330

330331
StatusBarLayer *status_layer = &data->status_layer;

src/fw/apps/system_apps/settings/settings.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ static void prv_window_load(Window *window) {
9292
shell_prefs_get_settings_menu_highlight_color(),
9393
GColorWhite);
9494
menu_layer_set_click_config_onto_window(menu_layer, &data->window);
95+
menu_layer_set_scroll_wrap_around(menu_layer, true);
9596

9697
layer_add_child(&data->window.layer, menu_layer_get_layer(menu_layer));
9798
}

src/fw/apps/system_apps/settings/settings_system.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ static void prv_information_window_load(Window *window) {
305305
});
306306
menu_layer_set_highlight_colors(menu_layer, shell_prefs_get_settings_menu_highlight_color(), GColorWhite);
307307
menu_layer_set_click_config_onto_window(menu_layer, &data->window);
308+
menu_layer_set_scroll_wrap_around(menu_layer, true);
308309

309310
layer_add_child(&data->window.layer, menu_layer_get_layer(menu_layer));
310311
}
@@ -723,6 +724,7 @@ static void prv_debugging_window_load(Window *window) {
723724
});
724725
menu_layer_set_highlight_colors(menu_layer, shell_prefs_get_settings_menu_highlight_color(), GColorWhite);
725726
menu_layer_set_click_config_onto_window(menu_layer, &data->window);
727+
menu_layer_set_scroll_wrap_around(menu_layer, true);
726728

727729
layer_add_child(&data->window.layer, menu_layer_get_layer(menu_layer));
728730
}
@@ -1169,6 +1171,7 @@ static void prv_certification_window_load(Window *window) {
11691171
});
11701172
menu_layer_set_highlight_colors(menu_layer, shell_prefs_get_settings_menu_highlight_color(), GColorWhite);
11711173
menu_layer_set_click_config_onto_window(menu_layer, &data->window);
1174+
menu_layer_set_scroll_wrap_around(menu_layer, true);
11721175

11731176
layer_add_child(&data->window.layer, menu_layer_get_layer(menu_layer));
11741177
}

0 commit comments

Comments
 (0)