Keep date and BT indicators centered at all times

This commit is contained in:
2026-03-22 11:13:33 -04:00
parent 1dc5c626ec
commit c70dd3ffcc

View File

@@ -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_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_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 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 // declare animation templates
static uint8_t s_animation_duration = 255;
static Animation *s_head_raise_template; static Animation *s_head_raise_template;
static Animation *s_head_out_template; static Animation *s_head_out_template;
static Animation *s_butt_raise_template; static Animation *s_butt_raise_template;
static Animation *s_butt_out_template; static Animation *s_butt_out_template;
// declare lookup tables // 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 uint8_t s_head_count = 25;
static const uint32_t s_random_heads[] = { static const uint32_t s_random_heads[] = {
RESOURCE_ID_H001, RESOURCE_ID_H002, RESOURCE_ID_H003, RESOURCE_ID_H004, RESOURCE_ID_H001, RESOURCE_ID_H002, RESOURCE_ID_H003, RESOURCE_ID_H004,
@@ -75,6 +88,12 @@ static const GColor8 s_dark_bg_colors[] = {
// Brown // Brown
GColorRoseVale}; 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() { static void update_minute_1() {
// get a tm structure // get a tm structure
time_t temp = time(NULL); time_t temp = time(NULL);
@@ -88,19 +107,53 @@ static void update_minute_1() {
static char s_date_buffer[16]; static char s_date_buffer[16];
strftime(s_date_buffer, sizeof(s_date_buffer), "%m.%d", tick_time); 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_time_layer, s_time_buffer);
text_layer_set_text(s_date_layer, s_date_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 // DEBUG memory usage layer
static char s_memory_buffer[32]; 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()); 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); 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) { static void schedule_guy_animation(Animation *raise_tmpl, Animation *out_tmpl, AnimationStoppedHandler out_handler) {
Animation *raise = animation_clone(raise_tmpl); Animation *raise = animation_clone(raise_tmpl);
Animation *out = animation_clone(out_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_alignment(s_guy_butt_layer, GAlignTopLeft);
bitmap_layer_set_bitmap(s_guy_butt_layer, s_butt_current); 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 // 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)); 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); layer_set_update_proc(s_time_bar_layer, time_bar_update_proc);
text_layer_set_text_alignment(s_date_layer, GTextAlignmentCenter); 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 // 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_head_layer));
layer_add_child(window_layer, bitmap_layer_get_layer(s_guy_butt_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); 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); s_butt_raise_template = property_animation_get_animation(butt_raise_prop);
animation_set_curve(s_butt_raise_template, AnimationCurveEaseOut); 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 // 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); 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); s_butt_out_template = property_animation_get_animation(butt_out_prop);
animation_set_curve(s_butt_out_template, AnimationCurveEaseOut); 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 // 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); 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); s_head_raise_template = property_animation_get_animation(head_raise_prop);
animation_set_curve(s_head_raise_template, AnimationCurveEaseOut); 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 // 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); 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); s_head_out_template = property_animation_get_animation(head_out_prop);
animation_set_curve(s_head_out_template, AnimationCurveEaseOut); 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 // free memory on Window close
@@ -262,14 +290,8 @@ static void main_window_unload(Window *window) {
// destroy layers // destroy layers
bitmap_layer_destroy(s_guy_head_layer); bitmap_layer_destroy(s_guy_head_layer);
bitmap_layer_destroy(s_guy_butt_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); layer_destroy(s_time_bar_layer);
// DEBUG memory usage layer
text_layer_destroy(s_memory_layer);
// Destroy animation templates // Destroy animation templates
animation_destroy(s_butt_raise_template); animation_destroy(s_butt_raise_template);
animation_destroy(s_butt_out_template); animation_destroy(s_butt_out_template);
@@ -327,11 +349,37 @@ static void init() {
// load bluetooth indicator // load bluetooth indicator
s_bt_icon = gbitmap_create_with_resource(RESOURCE_ID_BT); s_bt_icon = gbitmap_create_with_resource(RESOURCE_ID_BT);
// show the Window on the watch, with animated=true // pre-create layers used in update_minute_1
window_stack_push(s_main_window, true); //// 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 // make sure the time is displayed from the start
// and that the date/bt indicators are centered
update_minute_1(); 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 // register with TickTimerService
tick_timer_service_subscribe(MINUTE_UNIT, minute_handler); tick_timer_service_subscribe(MINUTE_UNIT, minute_handler);
@@ -342,6 +390,14 @@ static void init() {
// free memory on app exit // free memory on app exit
static void deinit() { 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 // unload font
fonts_unload_custom_font(s_custom_font); fonts_unload_custom_font(s_custom_font);