From 93ec947e7dd364de552a0c1051c6083fedf1fbaf Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Sun, 11 Oct 2026 03:00:22 -0400 Subject: [PATCH] Wire up back button --- src/c/jellyPod.c | 124 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 116 insertions(+), 8 deletions(-) diff --git a/src/c/jellyPod.c b/src/c/jellyPod.c index 422e0fb..92e950c 100644 --- a/src/c/jellyPod.c +++ b/src/c/jellyPod.c @@ -10,6 +10,15 @@ typedef enum { NAV_SCOPE_ALBUM_TRACKS = 4, } NavScope; +// TODO track last selected artist and album to restore when going deeper again +// TODO write to disk to restore exit point on re-open +typedef struct { + bool using_search; + char subscope_search[255]; // TODO tighten and only store first character + char subscope_artist[255]; + // const char *subscope_album; // future use +} History; + // constants #define MAX_ARTISTS 2048 // TODO paginate or push as high as possible #define MAX_TRACKS_PER_ALBUM 255 @@ -33,10 +42,14 @@ static Window *s_main_window; static CustomStatusBarLayer *s_status_bar_layer; static Layer *s_divider_layer; static MenuLayer *s_menu_layer; +static ClickConfigProvider s_base_click_config_provider; +static void *s_base_click_config_context; // globals static bool s_pkjs_ready = false; +static History s_nav_history; static NavScope s_current_menu_scope = NAV_SCOPE_MAIN; +static char s_pending_subscope[255]; static const char *s_current_menu_vanity[MAX_ARTISTS]; static uint16_t s_current_menu_seq_ids[MAX_TRACKS_PER_ALBUM]; static uint16_t s_menu_items_count = 0; @@ -44,6 +57,21 @@ static uint16_t s_menu_items_count = 0; // resources static GFont s_font_chicago; +static void append_to_subscope_history() { + switch (s_current_menu_scope) { + case NAV_SCOPE_ARTISTS: + if (s_nav_history.using_search) { + snprintf(s_nav_history.subscope_search, sizeof(s_nav_history.subscope_search), "%s", s_pending_subscope); + } + break; + case NAV_SCOPE_ALBUMS: + snprintf(s_nav_history.subscope_artist, sizeof(s_nav_history.subscope_artist), "%s", s_pending_subscope); + break; + default: + return; + } +} + ////////////////////// PKJS ////////////////////// static void outbox_failed_handler(DictionaryIterator *iter, AppMessageResult reason, void *context) { @@ -74,15 +102,28 @@ static void inbox_received_handler(DictionaryIterator *iter, void *context) { break; } } + NavScope last_menu_scope = s_current_menu_scope; s_current_menu_scope = t_new_scope->value->uint8; + bool moving_deeper = true; + if (s_current_menu_scope - last_menu_scope < 0) { + moving_deeper = false; + } + if (moving_deeper && last_menu_scope == NAV_SCOPE_SEARCH) { + s_nav_history.using_search = true; // set when actually selecting an item from the search menu + } else if (!moving_deeper && s_current_menu_scope == NAV_SCOPE_SEARCH) { + s_nav_history.using_search = false; // reset when returning to search menu + } + if (moving_deeper) { + append_to_subscope_history(); + } + menu_layer_reload_data(s_menu_layer); menu_layer_set_selected_index(s_menu_layer, MenuIndex(0, 0), MenuRowAlignTop, false); return; } } -// request a new menu scope from the PKJS side -static void to_pkjs_scope_change(NavScope scope, uint16_t *subscope_index) { +static void to_pkjs_move_deeper(NavScope scope, uint16_t *subscope_index) { if (!s_pkjs_ready) { return; } @@ -92,7 +133,10 @@ static void to_pkjs_scope_change(NavScope scope, uint16_t *subscope_index) { if (result != APP_MSG_OK) { return; } + if (subscope_index) { + snprintf(s_pending_subscope, sizeof(s_pending_subscope), + "%s", s_current_menu_vanity[*subscope_index]); dict_write_cstring(out, MESSAGE_KEY_OUT_MENU_NAV_SUBSCOPE, s_current_menu_vanity[*subscope_index]); } dict_write_uint8(out, MESSAGE_KEY_BIDIR_MENU_NAV_SCOPE, scope); @@ -103,6 +147,51 @@ static void to_pkjs_scope_change(NavScope scope, uint16_t *subscope_index) { } } +static void to_pkjs_move_back() { + if (!s_pkjs_ready) { + return; + } + + DictionaryIterator *out; + AppMessageResult result = app_message_outbox_begin(&out); + if (result != APP_MSG_OK) { + return; + } + + APP_LOG(APP_LOG_LEVEL_DEBUG, "Current nav scope: %d", s_current_menu_scope); + APP_LOG(APP_LOG_LEVEL_DEBUG, "Using search: %d", s_nav_history.using_search); + if ((s_current_menu_scope == NAV_SCOPE_ARTISTS && !s_nav_history.using_search) || s_current_menu_scope == NAV_SCOPE_SEARCH) { + // return to main menu + s_current_menu_scope = NAV_SCOPE_MAIN; + menu_layer_reload_data(s_menu_layer); + menu_layer_set_selected_index(s_menu_layer, MenuIndex(0, 0), MenuRowAlignTop, false); + } else { + // otherwise determine last subscope and go to previous scope + switch (s_current_menu_scope) { + case NAV_SCOPE_ARTISTS: // will only run if search is enabled + dict_write_uint8(out, MESSAGE_KEY_BIDIR_MENU_NAV_SCOPE, NAV_SCOPE_SEARCH); + break; + case NAV_SCOPE_ALBUMS: + if (s_nav_history.using_search) { + dict_write_cstring(out, MESSAGE_KEY_OUT_MENU_NAV_SUBSCOPE, s_nav_history.subscope_search); + } + dict_write_uint8(out, MESSAGE_KEY_BIDIR_MENU_NAV_SCOPE, NAV_SCOPE_ARTISTS); + break; + case NAV_SCOPE_ALBUM_TRACKS: + dict_write_cstring(out, MESSAGE_KEY_OUT_MENU_NAV_SUBSCOPE, s_nav_history.subscope_artist); + dict_write_uint8(out, MESSAGE_KEY_BIDIR_MENU_NAV_SCOPE, NAV_SCOPE_ALBUMS); + break; + default: + break; + } + } + + result = app_message_outbox_send(); + if (result != APP_MSG_OK) { + return; + } +} + static uint16_t menu_get_num_rows(MenuLayer *menu_layer, uint16_t section_index, void *context) { switch (s_current_menu_scope) { case NAV_SCOPE_MAIN: @@ -127,24 +216,26 @@ static void menu_select_click(struct MenuLayer *menu_layer, MenuIndex *clicked_i case NAV_SCOPE_MAIN: switch (clicked_index->row) { case 0: + // TODO tell PKJS to shuffle all songs break; case 1: + // TODO tell PKJS to shuffle artists break; case 2: - to_pkjs_scope_change(NAV_SCOPE_ARTISTS, NULL); + to_pkjs_move_deeper(NAV_SCOPE_ARTISTS, NULL); break; default: - to_pkjs_scope_change(NAV_SCOPE_SEARCH, NULL); + to_pkjs_move_deeper(NAV_SCOPE_SEARCH, NULL); } return; case NAV_SCOPE_SEARCH: case NAV_SCOPE_ARTISTS: - case NAV_SCOPE_ALBUMS: - to_pkjs_scope_change(s_current_menu_scope + 1, &clicked_index->row); + case NAV_SCOPE_ALBUMS: // TODO insert shuffle buttons in artist and album scopes + to_pkjs_move_deeper(s_current_menu_scope + 1, &clicked_index->row); break; case NAV_SCOPE_ALBUM_TRACKS: - default: - return; + // TODO tell PKJS to play track (and queue sequential tracks) + break; } } @@ -152,6 +243,19 @@ static void menu_selection_changed(struct MenuLayer *menu_layer, MenuIndex new_i speaker_play_tone(200, 10, 80, SpeakerWaveformSine); } +static void menu_back_click_handler(ClickRecognizerRef recognizer, void *context) { + if (s_current_menu_scope == NAV_SCOPE_MAIN) { + window_stack_pop(true); + } else { + to_pkjs_move_back(); + } +} + +static void menu_click_config_provider(void *context) { + s_base_click_config_provider(s_base_click_config_context); + window_single_click_subscribe(BUTTON_ID_BACK, menu_back_click_handler); +} + static void divider_layer_update_proc(Layer *layer, GContext *ctx) { graphics_context_set_fill_color(ctx, GColorBlack); graphics_fill_rect(ctx, layer_get_bounds(layer), 0, GCornerNone); @@ -163,6 +267,10 @@ static void main_window_load() { s_menu_layer = menu_layer_create(s_menu_grect); menu_layer_set_callbacks(s_menu_layer, NULL, (MenuLayerCallbacks){.get_num_rows = menu_get_num_rows, .draw_row = menu_draw_row, .select_click = menu_select_click, .selection_changed = menu_selection_changed}); menu_layer_set_click_config_onto_window(s_menu_layer, s_main_window); + s_base_click_config_provider = window_get_click_config_provider(s_main_window); + s_base_click_config_context = window_get_click_config_context(s_main_window); + window_set_click_config_provider_with_context(s_main_window, menu_click_config_provider, s_base_click_config_context); + #ifdef PBL_ROUND menu_layer_set_center_focused(s_menu_layer, false); #endif