├── .gitignore ├── README.md ├── app-font-browser-screenshot.png ├── appinfo.json ├── src └── app_font_browser.c └── wscript /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # app-font-browser 2 | 3 | ![screenshot](app-font-browser-screenshot.png) 4 | 5 | This example show how to load and display all the Pebble built-in system fonts. 6 | 7 | It also uses the [`Clicks`](https://developer.getpebble.com/docs/c/group___clicks.html) API to allow the user to cycle through each font in both 8 | directions. 9 | -------------------------------------------------------------------------------- /app-font-browser-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-examples/app-font-browser/48cf409b5dec1a32845a5ca0f47ecdbe757f2da4/app-font-browser-screenshot.png -------------------------------------------------------------------------------- /appinfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "75f25e48-c4c0-402f-a935-fcb7857d1ed4", 3 | "shortName": "Font Browser", 4 | "longName": "Font Browser", 5 | "companyName": "Pebble Technology", 6 | "versionCode": 2, 7 | "versionLabel": "2.0", 8 | "watchapp": { 9 | "watchface": false 10 | }, 11 | "appKeys": {}, 12 | "resources": { 13 | "media": [] 14 | }, 15 | "targetPlatforms": [ 16 | "aplite", 17 | "basalt", 18 | "chalk", 19 | "diorite", 20 | "emery" 21 | ], 22 | "sdkVersion": "3" 23 | } 24 | -------------------------------------------------------------------------------- /src/app_font_browser.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This app uses two Windows: 3 | * - s_main_window which uses a MenuLayer to display a list of fonts; 4 | * - s_font_window. 5 | * 6 | * The font window uses three TextLayers: 7 | * - to display a sample of text at the top of the screen (s_text_layer), 8 | * - to display at the bottom of the screen the name (s_font_name_layer), 9 | * - and variant (s_font_variant_layer) of the currently selected font. 10 | */ 11 | 12 | #include "pebble.h" 13 | 14 | #define NUM_FONTS 23 15 | #define NUM_MESSAGES 3 16 | 17 | typedef struct { 18 | char *name; 19 | char *variant; 20 | char *res; 21 | } PebbleFont; 22 | 23 | static PebbleFont pebble_fonts[] = { 24 | { .name = "Gothic", .variant = "14", .res = FONT_KEY_GOTHIC_14 }, 25 | { .name = "Gothic", .variant = "14 Bold", .res = FONT_KEY_GOTHIC_14_BOLD }, 26 | { .name = "Gothic", .variant = "18", .res = FONT_KEY_GOTHIC_18 }, 27 | { .name = "Gothic", .variant = "18 Bold", .res = FONT_KEY_GOTHIC_18_BOLD }, 28 | { .name = "Gothic", .variant = "24", .res = FONT_KEY_GOTHIC_24 }, 29 | { .name = "Gothic", .variant = "24 Bold", .res = FONT_KEY_GOTHIC_24_BOLD }, 30 | { .name = "Gothic", .variant = "28", .res = FONT_KEY_GOTHIC_28 }, 31 | { .name = "Gothic", .variant = "28 Bold", .res = FONT_KEY_GOTHIC_28_BOLD }, 32 | 33 | { .name = "Bitham", .variant = "30 Black", .res = FONT_KEY_BITHAM_30_BLACK }, 34 | { .name = "Bitham", .variant = "42 Bold", .res = FONT_KEY_BITHAM_42_BOLD }, 35 | { .name = "Bitham", .variant = "42 Light", .res = FONT_KEY_BITHAM_42_LIGHT }, 36 | { .name = "Bitham", .variant = "34 Medium Numbers", .res = FONT_KEY_BITHAM_34_MEDIUM_NUMBERS }, 37 | { .name = "Bitham", .variant = "42 Medium Numbers", .res = FONT_KEY_BITHAM_42_MEDIUM_NUMBERS }, 38 | 39 | { .name = "Roboto", .variant = "21 Condensed", .res = FONT_KEY_ROBOTO_CONDENSED_21 }, 40 | { .name = "Roboto", .variant = "49 Bold Subset", .res = FONT_KEY_ROBOTO_BOLD_SUBSET_49 }, 41 | 42 | { .name = "Droid", .variant = "28 Bold", .res = FONT_KEY_DROID_SERIF_28_BOLD }, 43 | 44 | { .name = "LECO", .variant = "20 Bold Numbers", .res = FONT_KEY_LECO_20_BOLD_NUMBERS }, 45 | { .name = "LECO", .variant = "26 Bold Numbers AM/PM", .res = FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM }, 46 | { .name = "LECO", .variant = "28 Light Numbers", .res = FONT_KEY_LECO_28_LIGHT_NUMBERS }, 47 | { .name = "LECO", .variant = "32 Bold Numbers", .res = FONT_KEY_LECO_32_BOLD_NUMBERS }, 48 | { .name = "LECO", .variant = "36 Bold Numbers", .res = FONT_KEY_LECO_36_BOLD_NUMBERS }, 49 | { .name = "LECO", .variant = "38 Bold Numbers", .res = FONT_KEY_LECO_38_BOLD_NUMBERS }, 50 | { .name = "LECO", .variant = "42 Numbers", .res = FONT_KEY_LECO_42_NUMBERS }, 51 | }; 52 | 53 | static char *s_messages[] = { 54 | "0123456789", 55 | "abcdef ABCDEF", 56 | "09:42" 57 | }; 58 | 59 | static Window *s_main_window, *s_font_window; 60 | static MenuLayer *s_menu_layer; 61 | static TextLayer *s_text_layer, *s_font_name_layer, *s_font_variant_layer, *s_font_size_layer; 62 | 63 | /* Store the index of the currently selected font and text message. */ 64 | static int s_current_font; 65 | static int s_current_message; 66 | 67 | /* Text buffer for the size required to display font and message */ 68 | static char s_size_text[256] = "size"; 69 | 70 | static uint16_t get_num_rows(struct MenuLayer* menu_layer, uint16_t section_index, void *callback_context) { 71 | return NUM_FONTS; 72 | } 73 | 74 | static void draw_row(GContext *ctx, const Layer *cell_layer, MenuIndex *cell_index, void *callback_context) { 75 | PebbleFont *font_array = (PebbleFont*) callback_context; 76 | PebbleFont *this_font = &font_array[cell_index->row]; 77 | 78 | menu_cell_basic_draw(ctx, cell_layer, this_font->name, this_font->variant, NULL); 79 | } 80 | 81 | static void select_click(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *callback_context) { 82 | s_current_font = cell_index->row; 83 | 84 | window_stack_push(s_font_window, true); 85 | } 86 | 87 | static void main_window_load(Window *window) { 88 | Layer *window_layer = window_get_root_layer(window); 89 | 90 | s_menu_layer = menu_layer_create(layer_get_bounds(window_layer)); 91 | menu_layer_set_callbacks(s_menu_layer, pebble_fonts, (MenuLayerCallbacks) { 92 | .get_num_rows = get_num_rows, 93 | .draw_row = draw_row, 94 | .select_click = select_click 95 | }); 96 | menu_layer_set_click_config_onto_window(s_menu_layer, window); 97 | 98 | layer_add_child(window_layer, menu_layer_get_layer(s_menu_layer)); 99 | } 100 | 101 | static void main_window_unload(Window *window) { 102 | menu_layer_destroy(s_menu_layer); 103 | } 104 | 105 | /* 106 | * Update the font window with the currently selected font. 107 | * 108 | * This is called: 109 | * - When the s_font_window is initialized 110 | * - When the user presses up/down/select in the font window 111 | */ 112 | static void show_selected_font_and_message() { 113 | PebbleFont *font = &pebble_fonts[s_current_font]; 114 | 115 | // Update the font and text for the demo message 116 | text_layer_set_font(s_text_layer, fonts_get_system_font(font->res)); 117 | text_layer_set_text(s_text_layer, s_messages[s_current_message]); 118 | 119 | // Update the font name and font variant at the bottom of the screen 120 | text_layer_set_text(s_font_name_layer, font->name); 121 | text_layer_set_text(s_font_variant_layer, font->variant); 122 | 123 | // Update Font Size Layer 124 | GSize textSize = text_layer_get_content_size(s_text_layer); 125 | snprintf(s_size_text, sizeof(s_size_text), "H: %d W: %d", textSize.h, textSize.w); 126 | 127 | text_layer_set_text(s_font_size_layer, s_size_text); 128 | } 129 | 130 | static void select_click_handler(ClickRecognizerRef recognizer, void *context) { 131 | s_current_message++; 132 | if ((unsigned)s_current_message >= NUM_MESSAGES) { 133 | s_current_message = 0; 134 | } 135 | 136 | show_selected_font_and_message(); 137 | } 138 | 139 | static void up_click_handler(ClickRecognizerRef recognizer, void *context) { 140 | s_current_font--; 141 | if (s_current_font < 0) { 142 | s_current_font = NUM_FONTS - 1; 143 | } 144 | 145 | MenuIndex idx = menu_layer_get_selected_index(s_menu_layer); 146 | idx.row = s_current_font; 147 | menu_layer_set_selected_index(s_menu_layer, idx, MenuRowAlignCenter, false); 148 | 149 | show_selected_font_and_message(); 150 | } 151 | 152 | static void down_click_handler(ClickRecognizerRef recognizer, void *context) { 153 | s_current_font++; 154 | if ((unsigned)s_current_font >= NUM_FONTS) { 155 | s_current_font = 0; 156 | } 157 | 158 | MenuIndex idx = menu_layer_get_selected_index(s_menu_layer); 159 | idx.row = s_current_font; 160 | menu_layer_set_selected_index(s_menu_layer, idx, MenuRowAlignCenter, false); 161 | 162 | show_selected_font_and_message(); 163 | } 164 | 165 | static void click_config_provider(void *context) { 166 | window_single_click_subscribe(BUTTON_ID_SELECT, (ClickHandler)select_click_handler); 167 | window_single_click_subscribe(BUTTON_ID_UP, (ClickHandler)up_click_handler); 168 | window_single_click_subscribe(BUTTON_ID_DOWN, (ClickHandler)down_click_handler); 169 | } 170 | 171 | static void font_window_load(Window *window) { 172 | // Define some event handlers for clicks 173 | window_set_click_config_provider(window, (ClickConfigProvider) click_config_provider); 174 | 175 | Layer *window_layer = window_get_root_layer(window); 176 | GRect window_bounds = layer_get_bounds(window_layer); 177 | 178 | // Arrange the three text layers on top of each other 179 | window_bounds.size.h -= PBL_IF_RECT_ELSE(60, 70); 180 | s_text_layer = text_layer_create(window_bounds); 181 | 182 | window_bounds.origin.x = 2; 183 | window_bounds.size.w -= 4; 184 | 185 | window_bounds.origin.y += window_bounds.size.h; 186 | window_bounds.size.h = PBL_IF_RECT_ELSE(20, 16); 187 | 188 | GFont text_alignment = fonts_get_system_font(PBL_IF_RECT_ELSE(FONT_KEY_GOTHIC_18_BOLD, FONT_KEY_GOTHIC_14_BOLD)); 189 | 190 | const uint8_t text_separation = 2; 191 | 192 | s_font_size_layer = text_layer_create(window_bounds); 193 | text_layer_set_font(s_font_size_layer, text_alignment); 194 | 195 | window_bounds.origin.y += window_bounds.size.h - text_separation; 196 | 197 | s_font_name_layer = text_layer_create(window_bounds); 198 | text_layer_set_font(s_font_name_layer, text_alignment); 199 | 200 | window_bounds.origin.y += window_bounds.size.h - text_separation; 201 | 202 | s_font_variant_layer = text_layer_create(window_bounds); 203 | text_layer_set_font(s_font_variant_layer, text_alignment); 204 | 205 | // Add the child layer to the current window (s_font_window) 206 | layer_add_child(window_layer, text_layer_get_layer(s_text_layer)); 207 | layer_add_child(window_layer, text_layer_get_layer(s_font_name_layer)); 208 | layer_add_child(window_layer, text_layer_get_layer(s_font_variant_layer)); 209 | layer_add_child(window_layer, text_layer_get_layer(s_font_size_layer)); 210 | #ifdef PBL_ROUND 211 | text_layer_enable_screen_text_flow_and_paging(s_text_layer, 5); 212 | text_layer_enable_screen_text_flow_and_paging(s_font_size_layer, 5); 213 | text_layer_set_text_alignment(s_text_layer, GTextAlignmentCenter); 214 | text_layer_set_text_alignment(s_font_name_layer, GTextAlignmentCenter); 215 | text_layer_set_text_alignment(s_font_variant_layer, GTextAlignmentCenter); 216 | text_layer_set_text_alignment(s_font_size_layer, GTextAlignmentCenter); 217 | #endif 218 | // Finally, update the text and font in the layers 219 | show_selected_font_and_message(); 220 | } 221 | 222 | static void font_window_unload(Window *window) { 223 | layer_remove_child_layers(window_get_root_layer(window)); 224 | 225 | text_layer_destroy(s_text_layer); 226 | text_layer_destroy(s_font_name_layer); 227 | text_layer_destroy(s_font_variant_layer); 228 | } 229 | 230 | static void init() { 231 | s_main_window = window_create(); 232 | window_set_window_handlers(s_main_window, (WindowHandlers) { 233 | .load = main_window_load, 234 | .unload = main_window_unload 235 | }); 236 | 237 | s_font_window = window_create(); 238 | window_set_window_handlers(s_font_window, (WindowHandlers) { 239 | .load = font_window_load, 240 | .unload = font_window_unload 241 | }); 242 | 243 | window_stack_push(s_main_window, true); 244 | } 245 | 246 | static void deinit() { 247 | window_destroy(s_font_window); 248 | window_destroy(s_main_window); 249 | } 250 | 251 | int main(void) { 252 | init(); 253 | app_event_loop(); 254 | deinit(); 255 | } 256 | -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # This file is the default set of rules to compile a Pebble project. 4 | # 5 | # Feel free to customize this to your needs. 6 | # 7 | 8 | import os.path 9 | 10 | top = '.' 11 | out = 'build' 12 | 13 | def options(ctx): 14 | ctx.load('pebble_sdk') 15 | 16 | def configure(ctx): 17 | ctx.load('pebble_sdk') 18 | 19 | def build(ctx): 20 | ctx.load('pebble_sdk') 21 | 22 | build_worker = os.path.exists('worker_src') 23 | binaries = [] 24 | 25 | for p in ctx.env.TARGET_PLATFORMS: 26 | ctx.set_env(ctx.all_envs[p]) 27 | ctx.set_group(ctx.env.PLATFORM_NAME) 28 | app_elf='{}/pebble-app.elf'.format(ctx.env.BUILD_DIR) 29 | ctx.pbl_program(source=ctx.path.ant_glob('src/**/*.c'), 30 | target=app_elf) 31 | 32 | if build_worker: 33 | worker_elf='{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR) 34 | binaries.append({'platform': p, 'app_elf': app_elf, 'worker_elf': worker_elf}) 35 | ctx.pbl_worker(source=ctx.path.ant_glob('worker_src/**/*.c'), 36 | target=worker_elf) 37 | else: 38 | binaries.append({'platform': p, 'app_elf': app_elf}) 39 | 40 | ctx.set_group('bundle') 41 | ctx.pbl_bundle(binaries=binaries, js=ctx.path.ant_glob('src/js/**/*.js')) 42 | --------------------------------------------------------------------------------