feat(layers): information layer

This commit is contained in:
mikhail "synzr" 2025-10-18 21:23:17 +05:00
parent 1e600be872
commit 48f336a2eb
12 changed files with 205 additions and 3 deletions

View file

@ -23,6 +23,9 @@
"messageKeys": [ "messageKeys": [
"dummy" "dummy"
], ],
"capabilities": [
"health"
],
"resources": { "resources": {
"media": [{ "media": [{
"type": "bitmap", "type": "bitmap",
@ -36,6 +39,30 @@
"file": "character_odd.png", "file": "character_odd.png",
"memoryFormat": "Smallest", "memoryFormat": "Smallest",
"spaceOptimization": "memory" "spaceOptimization": "memory"
}, {
"type": "bitmap",
"name": "ICON_BATTERY_EMPTY",
"file": "icon_battery_empty.png",
"memoryFormat": "Smallest",
"spaceOptimization": "memory"
}, {
"type": "bitmap",
"name": "ICON_BATTERY_HALF",
"file": "icon_battery_half.png",
"memoryFormat": "Smallest",
"spaceOptimization": "memory"
}, {
"type": "bitmap",
"name": "ICON_BATTERY_FULL",
"file": "icon_battery_full.png",
"memoryFormat": "Smallest",
"spaceOptimization": "memory"
}, {
"type": "bitmap",
"name": "ICON_PAWS",
"file": "icon_paws.png",
"memoryFormat": "Smallest",
"spaceOptimization": "memory"
}, { }, {
"type": "font", "type": "font",
"name": "FONT_TORO_36", "name": "FONT_TORO_36",

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

BIN
resources/icon_paws.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

View file

@ -60,6 +60,10 @@ void clock_layer_init(Layer *layer, int y) {
layer_add_child(layer, s_clock_layer); layer_add_child(layer, s_clock_layer);
} }
GRect clock_layer_get_bounds() {
return layer_get_bounds(s_clock_layer);
}
void clock_layer_tick(void) { void clock_layer_tick(void) {
layer_mark_dirty(s_clock_layer); layer_mark_dirty(s_clock_layer);
} }

View file

@ -4,7 +4,8 @@
#include <pebble.h> #include <pebble.h>
void clock_layer_init(Layer *layer, int y); void clock_layer_init(Layer *layer, int y);
GRect clock_layer_get_bounds();
void clock_layer_tick(void); void clock_layer_tick(void);
void clock_layer_deinit(void); void clock_layer_deinit(void);
#endif #endif // CLOCK_LAYER_H_

115
src/c/information_layer.c Normal file
View file

@ -0,0 +1,115 @@
#include "information_layer.h"
#include "resources_service.h"
#define INFO_LAYER_ICON_WIDTH 18
#define INFO_LAYER_PADDING 4
#define INFO_LAYER_HEIGHT 24
typedef struct InformationLayerData {
int level;
int steps;
} InformationLayerData;
static Layer *s_information_layer;
static char s_information_layer_level_text[3];
static char s_information_layer_count_text[6];
static void information_layer_update_proc(Layer *layer, GContext *ctx) {
GRect rect = layer_get_unobstructed_bounds(layer);
GFont font = resources_service_get_custom_font(CustomFontKonekoToro);
InformationLayerData *data = layer_get_data(s_information_layer);
int content_w = INFO_LAYER_ICON_WIDTH + INFO_LAYER_PADDING;
snprintf(s_information_layer_level_text, sizeof(s_information_layer_level_text), "%d",
data->level);
GSize text_size = graphics_text_layout_get_content_size(
s_information_layer_level_text, font, rect, GTextOverflowModeFill, GTextAlignmentLeft);
content_w += text_size.w;
#ifdef PBL_HEALTH
snprintf(s_information_layer_count_text, sizeof(s_information_layer_count_text), "%d",
data->steps);
text_size = graphics_text_layout_get_content_size(s_information_layer_count_text, font, rect,
GTextOverflowModeFill, GTextAlignmentLeft);
content_w += INFO_LAYER_ICON_WIDTH + INFO_LAYER_PADDING + text_size.w;
#endif
int content_x = (rect.size.w - content_w) / 2;
/* Draw the battery icon based on level percentage. */
graphics_context_set_compositing_mode(ctx, GCompOpSet);
GBitmap *battery_icon;
if (data->level < 20) {
battery_icon = resources_service_get_custom_icon(CustomIconBatteryEmpty);
} else if (data->level < 80) {
battery_icon = resources_service_get_custom_icon(CustomIconBatteryHalf);
} else {
battery_icon = resources_service_get_custom_icon(CustomIconBatteryFull);
}
GRect battery_rect_inside = GRect(0, 0, INFO_LAYER_ICON_WIDTH, rect.size.h);
GRect battery_rect = gbitmap_get_bounds(battery_icon);
grect_align(&battery_rect, &battery_rect_inside, GAlignCenter, true);
battery_rect.origin.x += content_x;
graphics_draw_bitmap_in_rect(ctx, battery_icon, battery_rect);
/* Draw the battery level in text. */
GRect text_rect =
GRect(content_x + INFO_LAYER_ICON_WIDTH + INFO_LAYER_PADDING, -4, text_size.w, text_size.h);
graphics_context_set_text_color(ctx, GColorBlack);
graphics_draw_text(ctx, s_information_layer_level_text, font, text_rect, GTextOverflowModeFill,
GTextAlignmentLeft, NULL);
#ifdef PBL_HEALTH
/* Draw the paws icon. */
GBitmap *paws_icon = resources_service_get_custom_icon(CustomIconPaws);
GRect paws_rect = gbitmap_get_bounds(paws_icon);
paws_rect.origin.x = content_x + text_rect.size.w;
graphics_draw_bitmap_in_rect(ctx, paws_icon, paws_rect);
/* Draw the step count in text. */
text_rect = GRect(paws_rect.origin.x + INFO_LAYER_ICON_WIDTH + INFO_LAYER_PADDING, -4,
text_size.w, text_size.h);
graphics_context_set_text_color(ctx, GColorBlack);
graphics_draw_text(ctx, s_information_layer_count_text, font, text_rect, GTextOverflowModeFill,
GTextAlignmentLeft, NULL);
#endif // PBL_HEALTH
}
static void battery_state_service_callback(BatteryChargeState charge) {
InformationLayerData *data = layer_get_data(s_information_layer);
data->level = charge.charge_percent;
layer_mark_dirty(s_information_layer);
}
void information_layer_init(Layer *layer, int y) {
GRect rect = layer_get_unobstructed_bounds(layer);
s_information_layer = layer_create_with_data(GRect(0, y, rect.size.w, INFO_LAYER_HEIGHT),
sizeof(InformationLayerData));
InformationLayerData *data = layer_get_data(s_information_layer);
data->level = battery_state_service_peek().charge_percent;
data->steps = PBL_IF_HEALTH_ELSE(health_service_sum_today(HealthMetricStepCount), 0);
layer_set_update_proc(s_information_layer, information_layer_update_proc);
layer_add_child(layer, s_information_layer);
battery_state_service_subscribe(battery_state_service_callback);
}
void information_layer_tick(void) {
#ifdef PBL_HEALTH
InformationLayerData *data = layer_get_data(s_information_layer);
data->steps = health_service_sum_today(HealthMetricStepCount);
layer_mark_dirty(s_information_layer);
#endif // PBL_HEALTH
}
void information_layer_deinit(void) {
layer_destroy(s_information_layer);
}

10
src/c/information_layer.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef INFORMATION_LAYER_H_
#define INFORMATION_LAYER_H_
#include <pebble.h>
void information_layer_init(Layer *layer, int y);
void information_layer_tick(void);
void information_layer_deinit(void);
#endif // INFORMATION_LAYER_H_

View file

@ -2,12 +2,14 @@
#include "date_layer.h" #include "date_layer.h"
#include "character_layer.h" #include "character_layer.h"
#include "clock_layer.h" #include "clock_layer.h"
#include "information_layer.h"
static Window *s_main_window; static Window *s_main_window;
static void main_window_tick(struct tm *time, TimeUnits units) { static void main_window_tick(struct tm *time, TimeUnits units) {
date_layer_tick(); date_layer_tick();
clock_layer_tick(); clock_layer_tick();
information_layer_tick();
character_layer_tick(); character_layer_tick();
} }
@ -15,7 +17,11 @@ static void main_window_load(Window *window) {
Layer *layer = window_get_root_layer(window); Layer *layer = window_get_root_layer(window);
date_layer_init(layer, 0); date_layer_init(layer, 0);
clock_layer_init(layer, 40); clock_layer_init(layer, 30);
GRect clock_layer_rect = clock_layer_get_bounds();
information_layer_init(layer, 30 + clock_layer_rect.size.h + 4);
character_layer_init(layer); character_layer_init(layer);
tick_timer_service_subscribe(SECOND_UNIT, main_window_tick); tick_timer_service_subscribe(SECOND_UNIT, main_window_tick);
@ -26,6 +32,7 @@ static void main_window_unload(Window *window) {
clock_layer_deinit(); clock_layer_deinit();
date_layer_deinit(); date_layer_deinit();
information_layer_deinit();
character_layer_deinit(); character_layer_deinit();
} }

View file

@ -8,25 +8,41 @@ typedef struct ResourcesService {
GFont font_toro; GFont font_toro;
GBitmap *character_even; GBitmap *character_even;
GBitmap *character_odd; GBitmap *character_odd;
GBitmap *icon_battery_empty;
GBitmap *icon_battery_half;
GBitmap *icon_battery_full;
GBitmap *icon_paws;
} ResourcesService; } ResourcesService;
static ResourcesService *s_resources_service; static ResourcesService *s_resources_service;
void resources_service_init(void) { void resources_service_init(void) {
s_resources_service = malloc(sizeof(ResourcesService)); s_resources_service = malloc(sizeof(ResourcesService));
s_resources_service->font_koneko_toro = s_resources_service->font_koneko_toro =
fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_KONEKO_TORO)); fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_KONEKO_TORO));
s_resources_service->font_toro = s_resources_service->font_toro =
fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_TORO)); fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_TORO));
s_resources_service->character_even = gbitmap_create_with_resource(RESOURCE_ID_CHARACTER_EVEN); s_resources_service->character_even = gbitmap_create_with_resource(RESOURCE_ID_CHARACTER_EVEN);
s_resources_service->character_odd = gbitmap_create_with_resource(RESOURCE_ID_CHARACTER_ODD); s_resources_service->character_odd = gbitmap_create_with_resource(RESOURCE_ID_CHARACTER_ODD);
s_resources_service->icon_battery_empty =
gbitmap_create_with_resource(RESOURCE_ID_ICON_BATTERY_EMPTY);
s_resources_service->icon_battery_full =
gbitmap_create_with_resource(RESOURCE_ID_ICON_BATTERY_FULL);
s_resources_service->icon_battery_half =
gbitmap_create_with_resource(RESOURCE_ID_ICON_BATTERY_HALF);
s_resources_service->icon_paws = gbitmap_create_with_resource(RESOURCE_ID_ICON_PAWS);
} }
void resources_service_deinit(void) { void resources_service_deinit(void) {
fonts_unload_custom_font(s_resources_service->font_koneko_toro); fonts_unload_custom_font(s_resources_service->font_koneko_toro);
fonts_unload_custom_font(s_resources_service->font_toro); fonts_unload_custom_font(s_resources_service->font_toro);
gbitmap_destroy(s_resources_service->character_even); gbitmap_destroy(s_resources_service->character_even);
gbitmap_destroy(s_resources_service->character_odd); gbitmap_destroy(s_resources_service->character_odd);
free(s_resources_service); free(s_resources_service);
} }
@ -37,6 +53,20 @@ GBitmap *resources_service_get_character(int ticks) {
return s_resources_service->character_odd; return s_resources_service->character_odd;
} }
GBitmap *resources_service_get_custom_icon(CustomIcon icon) {
switch (icon) {
case CustomIconBatteryEmpty:
return s_resources_service->icon_battery_empty;
case CustomIconBatteryFull:
return s_resources_service->icon_battery_full;
case CustomIconBatteryHalf:
return s_resources_service->icon_battery_half;
case CustomIconPaws:
return s_resources_service->icon_paws;
}
return NULL;
}
GFont resources_service_get_custom_font(CustomFont font) { GFont resources_service_get_custom_font(CustomFont font) {
switch (font) { switch (font) {
case CustomFontKonekoToro: case CustomFontKonekoToro:
@ -44,5 +74,5 @@ GFont resources_service_get_custom_font(CustomFont font) {
case CustomFontToro: case CustomFontToro:
return s_resources_service->font_toro; return s_resources_service->font_toro;
} }
return NULL; return fonts_get_system_font(FONT_KEY_FONT_FALLBACK);
} }

View file

@ -8,10 +8,18 @@ typedef enum CustomFont {
CustomFontToro CustomFontToro
} CustomFont; } CustomFont;
typedef enum CustomIcon {
CustomIconBatteryEmpty,
CustomIconBatteryFull,
CustomIconBatteryHalf,
CustomIconPaws
} CustomIcon;
void resources_service_init(void); void resources_service_init(void);
void resources_service_deinit(void); void resources_service_deinit(void);
GBitmap *resources_service_get_character(int ticks); GBitmap *resources_service_get_character(int ticks);
GBitmap *resources_service_get_custom_icon(CustomIcon icon);
GFont resources_service_get_custom_font(CustomFont font); GFont resources_service_get_custom_font(CustomFont font);
#endif #endif