From c70dd3ffcc0ab8406b5aec6e70120fbd041c6d8d Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Sun, 22 Mar 2026 11:13:33 -0400 Subject: [PATCH] Keep date and BT indicators centered at all times --- src/c/main.c | 138 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 97 insertions(+), 41 deletions(-) diff --git a/src/c/main.c b/src/c/main.c index a57b936..6a3c183 100644 --- a/src/c/main.c +++ b/src/c/main.c @@ -30,14 +30,27 @@ static GRect s_guy_head_grect_on_screen_resting = GRect(PBL_IF_ROUND_ELSE(0, -30 static GRect s_guy_butt_grect_on_screen_raised = GRect(PBL_IF_ROUND_ELSE(0, -30), PBL_IF_ROUND_ELSE(160, 144), 260, 115); static GRect s_guy_butt_grect_off_screen_raised = GRect(PBL_IF_ROUND_ELSE(260, 170), PBL_IF_ROUND_ELSE(160, 144), 260, 115); static GRect s_guy_butt_grect_on_screen_resting = GRect(PBL_IF_ROUND_ELSE(0, -30), PBL_IF_ROUND_ELSE(145, 129), 260, 115); +static const uint8_t s_date_pos_x = 0; +static const uint8_t s_date_pos_y = PBL_IF_ROUND_ELSE(121, 105); +static const uint8_t s_date_bt_width = PBL_IF_ROUND_ELSE(69, 39); +static const uint8_t s_date_height = 14; +static GRect s_date_grect_a = GRect(s_date_pos_x, s_date_pos_y, s_date_bt_width, s_date_height); +static const uint8_t s_bt_pos_x = PBL_IF_ROUND_ELSE(191, 161); +static const uint8_t s_bt_pos_y = PBL_IF_ROUND_ELSE(118, 102); +static const uint8_t s_bt_height = 24; +static GRect s_bt_grect_a = GRect(s_bt_pos_x, s_bt_pos_y, s_date_bt_width, s_bt_height); +static GRect s_date_grect_b; +static GRect s_bt_grect_b; // declare animation templates +static uint8_t s_animation_duration = 255; static Animation *s_head_raise_template; static Animation *s_head_out_template; static Animation *s_butt_raise_template; static Animation *s_butt_out_template; // declare lookup tables +static const uint8_t s_ones_offset[] = {0, 4, 7, 11, 15}; static const uint8_t s_head_count = 25; static const uint32_t s_random_heads[] = { RESOURCE_ID_H001, RESOURCE_ID_H002, RESOURCE_ID_H003, RESOURCE_ID_H004, @@ -75,6 +88,12 @@ static const GColor8 s_dark_bg_colors[] = { // Brown GColorRoseVale}; +static void destroy_animation_handler(Animation *animation, bool finished, void *context) { + animation_destroy(animation); +} + +static bool first_minute_update = true; + static void update_minute_1() { // get a tm structure time_t temp = time(NULL); @@ -88,19 +107,53 @@ static void update_minute_1() { static char s_date_buffer[16]; strftime(s_date_buffer, sizeof(s_date_buffer), "%m.%d", tick_time); + // update layers text_layer_set_text(s_time_layer, s_time_buffer); text_layer_set_text(s_date_layer, s_date_buffer); + // create date/bt centering animations + //// count "1"s in s_time_buffer + static uint8_t ones; + uint8_t i, new_ones; + for (i = 0, new_ones = 0; s_time_buffer[i]; i++) { + new_ones += (s_time_buffer[i] == '1'); + } + if (new_ones != ones) { // only animate if necessary + ones = new_ones; + //// calculate new positions + s_date_grect_b = GRect(s_ones_offset[ones], s_date_pos_y, s_date_bt_width, s_date_height); + s_bt_grect_b = GRect(s_bt_pos_x - s_ones_offset[ones], s_bt_pos_y, s_date_bt_width, s_bt_height); + if (!first_minute_update) { + //// configure animations + ////// date + PropertyAnimation *date_center_prop = property_animation_create_layer_frame(text_layer_get_layer(s_date_layer), &s_date_grect_a, &s_date_grect_b); + Animation *date_center_anim = property_animation_get_animation(date_center_prop); + animation_set_curve(date_center_anim, AnimationCurveEaseOut); + animation_set_duration(date_center_anim, s_animation_duration); + ////// bt + PropertyAnimation *s_bt_center_prop = property_animation_create_layer_frame(bitmap_layer_get_layer(s_bt_layer), &s_bt_grect_a, &s_bt_grect_b); + Animation *bt_center_anim = property_animation_get_animation(s_bt_center_prop); + animation_set_curve(bt_center_anim, AnimationCurveEaseOut); + animation_set_duration(bt_center_anim, s_animation_duration); + ////// create spawn animation (animate centering of both elements at the same time) + Animation *centering_spawn_anim = animation_spawn_create(date_center_anim, bt_center_anim, NULL); + ////// set the handler to destroy the spawn animation (and its children) when finished + animation_set_handlers(centering_spawn_anim, (AnimationHandlers){.stopped = destroy_animation_handler}, NULL); + ////// run centering spawn animation + animation_schedule(centering_spawn_anim); + } + ////// update current date/bt positions + s_date_grect_a = s_date_grect_b; + s_bt_grect_a = s_bt_grect_b; + } + first_minute_update = false; + // DEBUG memory usage layer static char s_memory_buffer[32]; snprintf(s_memory_buffer, sizeof(s_memory_buffer), "%d/%d", (int)heap_bytes_used(), (int)heap_bytes_free() + (int)heap_bytes_used()); text_layer_set_text(s_memory_layer, s_memory_buffer); } -static void destroy_animation_handler(Animation *animation, bool finished, void *context) { - animation_destroy(animation); -} - static void schedule_guy_animation(Animation *raise_tmpl, Animation *out_tmpl, AnimationStoppedHandler out_handler) { Animation *raise = animation_clone(raise_tmpl); Animation *out = animation_clone(out_tmpl); @@ -193,36 +246,11 @@ static void main_window_load(Window *window) { bitmap_layer_set_alignment(s_guy_butt_layer, GAlignTopLeft); bitmap_layer_set_bitmap(s_guy_butt_layer, s_butt_current); - // format time layer - s_time_layer = text_layer_create(GRect(0, PBL_IF_ROUND_ELSE(109, 93), PBL_IF_ROUND_ELSE(260, 200), 34)); - text_layer_set_background_color(s_time_layer, GColorClear); - text_layer_set_font(s_time_layer, s_custom_font); - text_layer_set_text_alignment(s_time_layer, GTextAlignmentCenter); - - // format date layer - s_date_layer = text_layer_create(GRect(0, PBL_IF_ROUND_ELSE(121, 105), PBL_IF_ROUND_ELSE(69, 39), 14)); - text_layer_set_background_color(s_date_layer, GColorClear); - text_layer_set_font(s_date_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD)); - text_layer_set_text_alignment(s_date_layer, GTextAlignmentCenter); - - // format BT indicator layer - s_bt_layer = bitmap_layer_create(GRect(PBL_IF_ROUND_ELSE(191, 161), PBL_IF_ROUND_ELSE(118, 102), PBL_IF_ROUND_ELSE(69, 39), 24)); - bitmap_layer_set_compositing_mode(s_bt_layer, GCompOpSet); - bitmap_layer_set_alignment(s_bt_layer, GAlignCenter); - bitmap_layer_set_bitmap(s_bt_layer, s_bt_icon); - // create time bar layer s_time_bar_layer = layer_create(GRect(0, PBL_IF_ROUND_ELSE(115, 99), PBL_IF_ROUND_ELSE(260, 200), 30)); layer_set_update_proc(s_time_bar_layer, time_bar_update_proc); text_layer_set_text_alignment(s_date_layer, GTextAlignmentCenter); - // DEBUG memory usage layer - s_memory_layer = text_layer_create(GRect(0, PBL_IF_ROUND_ELSE(220, 190), PBL_IF_ROUND_ELSE(260, 200), 14)); - text_layer_set_background_color(s_memory_layer, GColorClear); - text_layer_set_font(s_memory_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14)); - text_layer_set_text_color(s_memory_layer, GColorWhite); - PBL_IF_ROUND_ELSE(text_layer_set_text_alignment(s_memory_layer, GTextAlignmentCenter), text_layer_set_text_alignment(s_memory_layer, GTextAlignmentLeft)); - // add layers as children to window layer_add_child(window_layer, bitmap_layer_get_layer(s_guy_head_layer)); layer_add_child(window_layer, bitmap_layer_get_layer(s_guy_butt_layer)); @@ -239,22 +267,22 @@ static void main_window_load(Window *window) { PropertyAnimation *butt_raise_prop = property_animation_create_layer_frame(bitmap_layer_get_layer(s_guy_butt_layer), &s_guy_butt_grect_on_screen_resting, &s_guy_butt_grect_on_screen_raised); s_butt_raise_template = property_animation_get_animation(butt_raise_prop); animation_set_curve(s_butt_raise_template, AnimationCurveEaseOut); - animation_set_duration(s_butt_raise_template, 350); + animation_set_duration(s_butt_raise_template, s_animation_duration); // butt Out PropertyAnimation *butt_out_prop = property_animation_create_layer_frame(bitmap_layer_get_layer(s_guy_butt_layer), &s_guy_butt_grect_on_screen_raised, &s_guy_butt_grect_off_screen_raised); s_butt_out_template = property_animation_get_animation(butt_out_prop); animation_set_curve(s_butt_out_template, AnimationCurveEaseOut); - animation_set_duration(s_butt_out_template, 350); + animation_set_duration(s_butt_out_template, s_animation_duration); // head Raise PropertyAnimation *head_raise_prop = property_animation_create_layer_frame(bitmap_layer_get_layer(s_guy_head_layer), &s_guy_head_grect_on_screen_resting, &s_guy_head_grect_on_screen_raised); s_head_raise_template = property_animation_get_animation(head_raise_prop); animation_set_curve(s_head_raise_template, AnimationCurveEaseOut); - animation_set_duration(s_head_raise_template, 350); + animation_set_duration(s_head_raise_template, s_animation_duration); // head Out PropertyAnimation *head_out_prop = property_animation_create_layer_frame(bitmap_layer_get_layer(s_guy_head_layer), &s_guy_head_grect_on_screen_raised, &s_guy_head_grect_off_screen_raised); s_head_out_template = property_animation_get_animation(head_out_prop); animation_set_curve(s_head_out_template, AnimationCurveEaseOut); - animation_set_duration(s_head_out_template, 350); + animation_set_duration(s_head_out_template, s_animation_duration); } // free memory on Window close @@ -262,14 +290,8 @@ static void main_window_unload(Window *window) { // destroy layers bitmap_layer_destroy(s_guy_head_layer); bitmap_layer_destroy(s_guy_butt_layer); - text_layer_destroy(s_time_layer); - text_layer_destroy(s_date_layer); - bitmap_layer_destroy(s_bt_layer); layer_destroy(s_time_bar_layer); - // DEBUG memory usage layer - text_layer_destroy(s_memory_layer); - // Destroy animation templates animation_destroy(s_butt_raise_template); animation_destroy(s_butt_out_template); @@ -327,11 +349,37 @@ static void init() { // load bluetooth indicator s_bt_icon = gbitmap_create_with_resource(RESOURCE_ID_BT); - // show the Window on the watch, with animated=true - window_stack_push(s_main_window, true); + // pre-create layers used in update_minute_1 + //// time + s_time_layer = text_layer_create(GRect(0, PBL_IF_ROUND_ELSE(109, 93), PBL_IF_ROUND_ELSE(260, 200), 34)); + text_layer_set_background_color(s_time_layer, GColorClear); + text_layer_set_font(s_time_layer, s_custom_font); + text_layer_set_text_alignment(s_time_layer, GTextAlignmentCenter); + //// date + s_date_layer = text_layer_create(s_date_grect_a); + text_layer_set_background_color(s_date_layer, GColorClear); + text_layer_set_font(s_date_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD)); + text_layer_set_text_alignment(s_date_layer, GTextAlignmentCenter); + //// bt + s_bt_layer = bitmap_layer_create(s_bt_grect_a); + bitmap_layer_set_compositing_mode(s_bt_layer, GCompOpSet); + bitmap_layer_set_alignment(s_bt_layer, GAlignCenter); + bitmap_layer_set_bitmap(s_bt_layer, s_bt_icon); + //// DEBUG memory usage layer + s_memory_layer = text_layer_create(GRect(0, PBL_IF_ROUND_ELSE(220, 190), PBL_IF_ROUND_ELSE(260, 200), 14)); + text_layer_set_background_color(s_memory_layer, GColorClear); + text_layer_set_font(s_memory_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14)); + text_layer_set_text_color(s_memory_layer, GColorWhite); + PBL_IF_ROUND_ELSE(text_layer_set_text_alignment(s_memory_layer, GTextAlignmentCenter), text_layer_set_text_alignment(s_memory_layer, GTextAlignmentLeft)); // make sure the time is displayed from the start + // and that the date/bt indicators are centered update_minute_1(); + layer_set_frame(text_layer_get_layer(s_date_layer), s_date_grect_a); + layer_set_frame(bitmap_layer_get_layer(s_bt_layer), s_bt_grect_a); + + // show the Window on the watch, with animated=true + window_stack_push(s_main_window, true); // register with TickTimerService tick_timer_service_subscribe(MINUTE_UNIT, minute_handler); @@ -342,6 +390,14 @@ static void init() { // free memory on app exit static void deinit() { + // destroy layers + text_layer_destroy(s_time_layer); + text_layer_destroy(s_date_layer); + bitmap_layer_destroy(s_bt_layer); + + // DEBUG memory usage layer + text_layer_destroy(s_memory_layer); + // unload font fonts_unload_custom_font(s_custom_font);