/* SPDX-FileCopyrightText: 2024 Google LLC */ /* SPDX-License-Identifier: Apache-2.0 */ #include "neat_bar.h" #include #if PBL_PLATFORM_EMERY const int8_t ICON_X_OFFSET = 0; const uint16_t UP_Y = 45; const uint16_t SELECT_Y = 105; const uint16_t DOWN_Y = 165; #elif PBL_PLATFORM_GABBRO const int8_t ICON_X_OFFSET = -1; const uint16_t UP_Y = 80; const uint16_t SELECT_Y = 121; const uint16_t DOWN_Y = 162; #else const int8_t ICON_X_OFFSET = 0; const uint16_t UP_Y = 50; const uint16_t SELECT_Y = 90; const uint16_t DOWN_Y = 120; #endif static const GRect s_base_grect_rect = GRect(PBL_DISPLAY_WIDTH - ACTION_BAR_WIDTH, 0, ACTION_BAR_WIDTH, PBL_DISPLAY_HEIGHT); static NeatBarLayer *s_current_neat_bar = NULL; static Animation *s_animate_move(NeatBarLayer *neat_bar, uint8_t button_index, bool move_on_y_axis, int16_t distance) { BitmapLayer *bitmap_layer = NULL; switch (button_index) { case BUTTON_ID_UP: bitmap_layer = neat_bar->up_layer; break; case BUTTON_ID_SELECT: bitmap_layer = neat_bar->select_layer; break; case BUTTON_ID_DOWN: bitmap_layer = neat_bar->down_layer; break; default: return NULL; } if (!bitmap_layer) { return NULL; } Layer *layer = bitmap_layer_get_layer(bitmap_layer); GRect from_frame = layer_get_frame(layer); GRect to_frame = from_frame; if (move_on_y_axis) { to_frame.origin.y += distance; } else { to_frame.origin.x += distance; } PropertyAnimation *prop_anim = property_animation_create_layer_frame(layer, &from_frame, &to_frame); return property_animation_get_animation(prop_anim); } static void s_icon_down_handler(NeatBarLayer *neat_bar, uint8_t button_index) { Animation *anim = s_animate_move(neat_bar, button_index, false, -4); animation_set_duration(anim, 80); animation_schedule(anim); } static void s_icon_up_handler(NeatBarLayer *neat_bar, uint8_t button_index) { Animation *anim = s_animate_move(neat_bar, button_index, false, 4); animation_set_duration(anim, 80); animation_schedule(anim); } static void s_up_down_handler(ClickRecognizerRef recognizer, void *context) { s_icon_down_handler(context, BUTTON_ID_UP); } static void s_up_up_handler(ClickRecognizerRef recognizer, void *context) { s_icon_up_handler(context, BUTTON_ID_UP); } static void s_down_down_handler(ClickRecognizerRef recognizer, void *context) { s_icon_down_handler(context, BUTTON_ID_DOWN); } static void s_down_up_handler(ClickRecognizerRef recognizer, void *context) { s_icon_up_handler(context, BUTTON_ID_DOWN); } static void s_select_down_handler(ClickRecognizerRef recognizer, void *context) { s_icon_down_handler(context, BUTTON_ID_SELECT); } static void s_select_up_handler(ClickRecognizerRef recognizer, void *context) { s_icon_up_handler(context, BUTTON_ID_SELECT); } static void s_main_layer_update_proc(Layer *layer, GContext *ctx) { graphics_context_set_antialiased(ctx, false); graphics_context_set_fill_color(ctx, s_current_neat_bar->background_color); graphics_fill_rect(ctx, layer_get_bounds(layer), s_current_neat_bar->corner_radius, s_current_neat_bar->x_offset > 0 ? GCornersAll : GCornersLeft); } static void s_base_click_config_provider(void *config_context) { NeatBarLayer *neat_bar = config_context; void *context = neat_bar->context ? neat_bar->context : neat_bar; window_raw_click_subscribe(BUTTON_ID_UP, s_up_down_handler, s_up_up_handler, neat_bar); window_raw_click_subscribe(BUTTON_ID_DOWN, s_down_down_handler, s_down_up_handler, neat_bar); window_raw_click_subscribe(BUTTON_ID_SELECT, s_select_down_handler, s_select_up_handler, neat_bar); window_set_click_context(BUTTON_ID_UP, context); window_set_click_context(BUTTON_ID_DOWN, context); window_set_click_context(BUTTON_ID_SELECT, context); if (neat_bar->click_config_provider) { neat_bar->click_config_provider(context); } } inline static void s_update_click_config_provider(NeatBarLayer *neat_bar) { if (neat_bar->window) { window_set_click_config_provider_with_context(neat_bar->window, s_base_click_config_provider, neat_bar); } } static void s_recenter_icons(NeatBarLayer *neat_bar) { int16_t y_offset = neat_bar->main_grect.origin.y; int8_t spread = neat_bar->spread; if (neat_bar->up_layer) { layer_set_frame(bitmap_layer_get_layer(neat_bar->up_layer), GRect(ICON_X_OFFSET, UP_Y - spread - y_offset, neat_bar->main_grect.size.w, 18)); } if (neat_bar->select_layer) { layer_set_frame(bitmap_layer_get_layer(neat_bar->select_layer), GRect(ICON_X_OFFSET, SELECT_Y - y_offset, neat_bar->main_grect.size.w, 18)); } if (neat_bar->down_layer) { layer_set_frame(bitmap_layer_get_layer(neat_bar->down_layer), GRect(ICON_X_OFFSET, DOWN_Y + spread - y_offset, neat_bar->main_grect.size.w, 18)); } } void neat_bar_layer_set_theme_default_rect(NeatBarLayer *neat_bar) { neat_bar_layer_set_floating(neat_bar, false); neat_bar->main_grect = s_base_grect_rect; layer_set_frame(neat_bar->main_layer, neat_bar->main_grect); neat_bar_layer_set_corner_radius(neat_bar, 0); s_recenter_icons(neat_bar); } void neat_bar_layer_set_theme_legacy_rect(NeatBarLayer *neat_bar) { neat_bar_layer_set_floating(neat_bar, false); neat_bar->main_grect = GRect(s_base_grect_rect.origin.x, 4, s_base_grect_rect.size.w, s_base_grect_rect.size.h - 8); layer_set_frame(neat_bar->main_layer, neat_bar->main_grect); neat_bar_layer_set_corner_radius(neat_bar, 3); s_recenter_icons(neat_bar); } void neat_bar_layer_add_to_window(NeatBarLayer *neat_bar, Window *window) { layer_add_child(window_get_root_layer(window), neat_bar->main_layer); neat_bar->window = window; s_update_click_config_provider(neat_bar); } void neat_bar_layer_set_click_config_provider(NeatBarLayer *neat_bar, ClickConfigProvider click_config_provider) { neat_bar->click_config_provider = click_config_provider; s_update_click_config_provider(neat_bar); } void neat_bar_layer_set_floating(NeatBarLayer *neat_bar, uint16_t x_offset) { if (x_offset == neat_bar->x_offset) { return; } neat_bar->x_offset = x_offset; neat_bar->main_grect.origin.x = PBL_DISPLAY_WIDTH - neat_bar->main_grect.size.w - x_offset; layer_set_frame(neat_bar->main_layer, neat_bar->main_grect); layer_mark_dirty(neat_bar->main_layer); } void neat_bar_layer_set_width(NeatBarLayer *neat_bar, uint16_t width) { if (width == neat_bar->main_grect.size.w) { return; } neat_bar->main_grect.origin.x = PBL_DISPLAY_WIDTH - width; neat_bar->main_grect.size.w = width; layer_set_frame(neat_bar->main_layer, neat_bar->main_grect); if (neat_bar->x_offset > 0) { uint16_t x = neat_bar->x_offset; neat_bar->x_offset = 0; // force update neat_bar_layer_set_floating(neat_bar, x); } else { layer_mark_dirty(neat_bar->main_layer); } s_recenter_icons(neat_bar); } void neat_bar_layer_set_height(NeatBarLayer *neat_bar, uint16_t height) { if (height == neat_bar->main_grect.size.h) { return; } neat_bar->main_grect.size.h = height; layer_set_frame(neat_bar->main_layer, neat_bar->main_grect); layer_mark_dirty(neat_bar->main_layer); } void neat_bar_layer_set_y_offset(NeatBarLayer *neat_bar, uint16_t y_offset) { if (y_offset == neat_bar->main_grect.origin.y) { return; } neat_bar->main_grect.origin.y = y_offset; layer_set_frame(neat_bar->main_layer, neat_bar->main_grect); layer_mark_dirty(neat_bar->main_layer); s_recenter_icons(neat_bar); } void neat_bar_layer_set_corner_radius(NeatBarLayer *neat_bar, uint16_t corner_radius) { if (corner_radius == neat_bar->corner_radius) { return; } neat_bar->corner_radius = corner_radius; layer_mark_dirty(neat_bar->main_layer); } void neat_bar_layer_set_background_color(NeatBarLayer *neat_bar, GColor background_color) { if (gcolor_equal(background_color, neat_bar->background_color)) { return; } neat_bar->background_color = background_color; layer_mark_dirty(neat_bar->main_layer); } void neat_bar_layer_set_spread(NeatBarLayer *neat_bar, int8_t spread) { if (spread == neat_bar->spread) { return; } neat_bar->spread = spread; s_recenter_icons(neat_bar); } void neat_bar_layer_set_icon(NeatBarLayer *neat_bar, ButtonId button_id, GBitmap *icon) { BitmapLayer *current_bitmap_layer; uint16_t y_pos; switch (button_id) { case BUTTON_ID_UP: current_bitmap_layer = neat_bar->up_layer; y_pos = UP_Y; break; case BUTTON_ID_DOWN: current_bitmap_layer = neat_bar->down_layer; y_pos = DOWN_Y; break; default: current_bitmap_layer = neat_bar->select_layer; y_pos = SELECT_Y; } if (!current_bitmap_layer) { int16_t adjusted_y = (button_id == BUTTON_ID_UP) ? y_pos - neat_bar->spread : (button_id == BUTTON_ID_DOWN) ? y_pos + neat_bar->spread : y_pos; current_bitmap_layer = bitmap_layer_create(GRect(ICON_X_OFFSET, adjusted_y, neat_bar->main_grect.size.w, 18)); layer_add_child(neat_bar->main_layer, bitmap_layer_get_layer(current_bitmap_layer)); switch (button_id) { case BUTTON_ID_UP: neat_bar->up_layer = current_bitmap_layer; break; case BUTTON_ID_DOWN: neat_bar->down_layer = current_bitmap_layer; break; default: neat_bar->select_layer = current_bitmap_layer; break; } } bitmap_layer_set_alignment(current_bitmap_layer, GAlignCenter); bitmap_layer_set_bitmap(current_bitmap_layer, icon); bitmap_layer_set_compositing_mode(current_bitmap_layer, GCompOpSet); } NeatBarLayer *neat_bar_layer_create() { NeatBarLayer *neat_bar = malloc(sizeof(NeatBarLayer)); neat_bar->main_grect = s_base_grect_rect; neat_bar->main_layer = layer_create(s_base_grect_rect); neat_bar->up_layer = NULL; neat_bar->down_layer = NULL; neat_bar->select_layer = NULL; neat_bar->click_config_provider = NULL; neat_bar->background_color = GColorBlack; neat_bar->corner_radius = 0; neat_bar->x_offset = 0; neat_bar->spread = 0; neat_bar->context = NULL; layer_set_update_proc(neat_bar->main_layer, s_main_layer_update_proc); s_current_neat_bar = neat_bar; return neat_bar; }