/* * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "action_menu_demo.h" #include "applib/app.h" #include "applib/graphics/graphics.h" #include "applib/ui/action_menu_hierarchy.h" #include "applib/ui/action_menu_window.h" #include "applib/ui/action_menu_window_private.h" #include "applib/ui/app_window_stack.h" #include "applib/ui/text_layer.h" #include "applib/ui/window.h" #include "kernel/pbl_malloc.h" #include "process_state/app_state/app_state.h" #include "resource/resource_ids.auto.h" #include "system/passert.h" #include "util/size.h" #include static struct { // main window Window *main_window; TextLayer *info_layer; // action menu ActionMenu *action_menu; // result_window Window *result_window; TextLayer *result_layer; } *s_app_data; /////////////////////// // Result Window static void prv_result_window_load(Window *window) { Layer *root_layer = window_get_root_layer(window); s_app_data->result_layer = text_layer_create(GRect(0, 60, root_layer->bounds.size.w, 50)); text_layer_set_text_alignment(s_app_data->result_layer, GTextAlignmentCenter); text_layer_set_text(s_app_data->result_layer, "Result!"); layer_add_child(root_layer, (Layer *)s_app_data->result_layer); } static void prv_result_window_unload(Window *window) { window_destroy(window); } /////////////////////// // Action Menu Window static void prv_action_menu_did_close_cb(ActionMenu *action_menu, const ActionMenuItem *item, void *context) { ActionMenuLevel *root_level = action_menu_get_root_level(action_menu); action_menu_hierarchy_destroy(root_level, NULL, NULL); } static void prv_action_callback(ActionMenu *action_menu, const ActionMenuItem *action, void *context) { s_app_data->result_window = window_create(); window_set_window_handlers(s_app_data->result_window, &(WindowHandlers) { .load = prv_result_window_load, .unload = prv_result_window_unload, }); action_menu_set_result_window(action_menu, s_app_data->result_window); } static void prv_select_click_handler(ClickRecognizerRef recognizer, void *context) { // First Level ActionMenuLevel *first_level = action_menu_level_create(10); action_menu_level_add_action(first_level, "First!", prv_action_callback, NULL); action_menu_level_add_action(first_level, "Second!", prv_action_callback, NULL); // More Levels ActionMenuLevel *more_level = action_menu_level_create(1); action_menu_level_add_action(more_level, "That's it, folks!", prv_action_callback, NULL); action_menu_level_add_child(first_level, more_level, "More..."); // Levels with multiple lines of text ActionMenuLevel *multiline_level = action_menu_level_create(5); action_menu_level_add_action(multiline_level, "Sorry, I can't talk right now.", prv_action_callback, NULL); action_menu_level_add_action(multiline_level, "I can't talk just now, please text me if this is an emergency.", prv_action_callback, NULL); action_menu_level_add_action(multiline_level, "In a meeting, I will call you back when the meeting is over.", prv_action_callback, NULL); action_menu_level_add_action(multiline_level, "On my way, I will text you when I'm nearby.", prv_action_callback, NULL); action_menu_level_add_action(multiline_level, "I am busy.", prv_action_callback, NULL); action_menu_level_add_child(first_level, multiline_level, "Canned Responses"); // Level with multi-column values of various row lengths ActionMenuLevel *multicolumn_select = action_menu_level_create(3); static const char* thin_values[] = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "🍺" }; ActionMenuLevel *multicolumn_one = action_menu_level_create(2); action_menu_level_set_display_mode(multicolumn_one, ActionMenuLevelDisplayModeThin); for (size_t i = 0; i < 2; i++) { action_menu_level_add_action(multicolumn_one, thin_values[i], prv_action_callback, NULL); } // ah ah ah ActionMenuLevel *multicolumn_two = action_menu_level_create(5); action_menu_level_set_display_mode(multicolumn_two, ActionMenuLevelDisplayModeThin); for (size_t i = 0; i < 5; i++) { action_menu_level_add_action(multicolumn_two, thin_values[i], prv_action_callback, NULL); } // ah ah ah ActionMenuLevel *multicolumn_many = action_menu_level_create(ARRAY_LENGTH(thin_values)); action_menu_level_set_display_mode(multicolumn_many, ActionMenuLevelDisplayModeThin); for (size_t i = 0; i < ARRAY_LENGTH(thin_values); i++) { action_menu_level_add_action(multicolumn_many, thin_values[i], prv_action_callback, NULL); } // ah ah ah action_menu_level_add_child(multicolumn_select, multicolumn_one, "One row"); action_menu_level_add_child(multicolumn_select, multicolumn_two, "Two rows"); action_menu_level_add_child(multicolumn_select, multicolumn_many, "Many rows"); action_menu_level_add_child(first_level, multicolumn_select, "Columns"); first_level->separator_index = first_level->num_items - 1; ActionMenuConfig config = { .root_level = first_level, .context = NULL, .colors.background = GColorOxfordBlue, .colors.foreground = GColorOrange, .did_close = prv_action_menu_did_close_cb, }; s_app_data->action_menu = app_action_menu_open(&config); } /////////////////////// // Main Window static void prv_main_window_click_config_provider(void *context) { window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); } static void prv_main_window_load(Window *window) { Layer *root_layer = window_get_root_layer(window); s_app_data->info_layer = text_layer_create(GRect(0, 60, root_layer->bounds.size.w, 50)); text_layer_set_text_alignment(s_app_data->info_layer, GTextAlignmentCenter); text_layer_set_text(s_app_data->info_layer, "Press the select button"); layer_add_child(root_layer, (Layer *)s_app_data->info_layer); } //////////////////// // App boilerplate static void prv_init(void) { s_app_data = app_zalloc_check(sizeof(*s_app_data)); s_app_data->main_window = window_create(); window_set_window_handlers(s_app_data->main_window, &(WindowHandlers) { .load = prv_main_window_load, }); window_set_click_config_provider(s_app_data->main_window, prv_main_window_click_config_provider); app_window_stack_push(s_app_data->main_window, true /* animated */); } static void s_main(void) { prv_init(); app_event_loop(); } const PebbleProcessMd* action_menu_demo_get_app_info() { static const PebbleProcessMdSystem s_app_data = { .common = { .main_func = s_main, // UUID: 101a32d95-ef69-46d4-a0b9-854cc62f97f9 .uuid = {0x99, 0xa3, 0x2d, 0x95, 0xef, 0x69, 0x46, 0xd4, 0xa0, 0xb9, 0x85, 0x4c, 0xc6, 0x2f, 0x97, 0xf9}, }, .name = "Action Menu Demo", }; return (const PebbleProcessMd*) &s_app_data; }