Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,28 @@ text to paste into the **Release notes** field when uploading the `.pbw` to the
the build, so the portal does not auto-fill it). The store **Description** lives
in [`PUBLISHING.md`](PUBLISHING.md).

## 0.0.2

- Settings are now WYSIWYG: saving the config page applies **every** value it
shows, not only the ones you touched. Previously the page sent just the keys
that changed against a phone-side baseline; when that baseline had drifted from
the watch (reinstall, a different phone, or defaults that never matched), a
setting shown in the page but left untouched was silently never applied — so
e.g. a complication could read "Location" in the settings yet stay "Date" on
the watch. Every non-translation setting is now always sent. Translation
strings stay delta-only (they are bulk and must fit the watch's message inbox);
the language picker resends them when it changes them.
- Config page defaults now match the watch's firmware defaults (the
above-calendar Week and AM/PM slots, low-battery threshold), so a fresh install
shows the watch's real out-of-box state before the first save.
- Coloured weather icons on colour watches (basalt, emery): the condition glyph
is tinted by meaning — sun amber, rain/snow blue, lightning amber, moon pale
yellow — while the temperature keeps the theme colour. Black-and-white watches
(diorite, flint) and the Mono theme are unchanged.
- New **Weather → Icon style** setting: *Default* (colour on colour watches, B&W
on the rest), *Colour* (force the tinted icons, overriding the Mono theme), or
*Black & White* (force the classic monochrome glyph on colour watches too).

## 0.0.1 — first public release

- Configurable complication rows (status bar, above the time, above the
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "TimelyNG",
"author": "Emanuele 'Lele' Calo",
"version": "0.0.1",
"version": "0.0.2",
"description": "A calendar watchface for Pebble: date, time and weather up top, a 3-week calendar below, and fully configurable complication rows. Appstore copy in PUBLISHING.md; release notes in CHANGELOG.md.",
"private": true,
"keywords": [
Expand Down Expand Up @@ -155,6 +155,7 @@
"slot_ctr_l": 116,
"slot_ctr_r": 117,
"batt_style": 118,
"weather_icons": 119,
"trans_abbr_sunday": 500,
"trans_abbr_monday": 501,
"trans_abbr_tuesday": 502,
Expand Down
8 changes: 8 additions & 0 deletions src/Timely.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ static bool showing_statusbar = true;
#define AK_SLOT_CTR_L 116
#define AK_SLOT_CTR_R 117
#define AK_BATT_STYLE 118
#define AK_WEATHER_ICONS 119

#define AK_TRANS_ABBR_SUNDAY 500
#define AK_TRANS_ABBR_MONDAY 501
Expand Down Expand Up @@ -1980,6 +1981,13 @@ void in_configuration_handler(DictionaryIterator *received, void *context) {
adv_settings_get()->weather_update = appkey->value->uint8;
}

// AK_WEATHER_ICONS == weather icon style (0 default / 1 colour / 2 B&W)
appkey = dict_find(received, AK_WEATHER_ICONS);
if (appkey != NULL) {
adv_settings_get()->weather_icons = appkey->value->uint8;
weather_mark_dirty();
}

// AK_THEME == color theme id; AK_THEME_MODE == 0 light / 1 dark / 2 auto
appkey = dict_find(received, AK_THEME);
if (appkey != NULL && appkey->value->uint8 < THEME_COUNT) {
Expand Down
7 changes: 4 additions & 3 deletions src/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,9 +312,10 @@ V = waning crescent 0.25 +
Pebble.addEventListener("webviewclosed", function (e) {
if (!e || !e.response) { return; } // user cancelled

// The page sends only the keys that changed. Merge them into the stored
// settings (don't overwrite) so the config page keeps the full picture and
// doesn't show untouched settings as reset on the next open.
// The page sends every non-translation setting plus any changed translation
// string. Merge them into the stored settings so the config page keeps the
// full picture (incl. translations) and doesn't show untouched settings as
// reset on the next open.
var dict;
try { dict = JSON.parse(decodeURIComponent(e.response)); } catch (err) { return; }
var stored = {};
Expand Down
9 changes: 6 additions & 3 deletions src/js/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ module.exports = [
{ key: 'slot_stat_r', label: 'Status bar — right', type: 'select', def: 15, options: SLOTS },
{ key: 'slot_ctr_l', label: 'Above time — left', type: 'select', def: 18, options: SLOTS },
{ key: 'slot_ctr_r', label: 'Above time — right', type: 'select', def: 0, options: SLOTS },
{ key: 'style_week', label: 'Above calendar — left', type: 'select', def: 0, options: SLOTS },
{ key: 'style_am_pm', label: 'Above calendar — right', type: 'select', def: 0, options: SLOTS },
{ key: 'style_week', label: 'Above calendar — left', type: 'select', def: 3, options: SLOTS },
{ key: 'style_am_pm', label: 'Above calendar — right', type: 'select', def: 5, options: SLOTS },
{ key: 'batt_style', label: 'Battery style', type: 'select', def: 2,
options: [['Bar (fills) with %', 0], ['Text %', 1], ['Icon + %', 2], ['Bar + icon', 3]],
note: 'How the Watch/Phone battery complications are drawn.' },
Expand Down Expand Up @@ -134,7 +134,10 @@ module.exports = [
{ key: 'weather_fmt', label: 'Units', type: 'select', def: 0,
options: [['Celsius', 0], ['Fahrenheit', 1]] },
{ key: 'weather_update', label: 'Update every', type: 'select', def: 15,
options: [['Off', 0], ['15 min', 15], ['30 min', 30], ['60 min', 60]] }
options: [['Off', 0], ['15 min', 15], ['30 min', 30], ['60 min', 60]] },
{ key: 'weather_icons', label: 'Icon style', type: 'select', def: 0,
options: [['Default', 0], ['Colour', 1], ['Black & White', 2]],
note: 'Default: colour on colour watches, B&W on the rest. Colour and Black & White force it.' }
]
},
{
Expand Down
18 changes: 13 additions & 5 deletions src/js/configpage.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,11 @@ var CSS = [
function buildConfigPage(spec, current) {
current = current || {};
var body = '';
// Baseline = the value each control starts at; on submit we send only the keys
// that actually changed, keeping the pebblejs://close payload small (a full
// dump of every setting exceeds the close-URL length and gets dropped).
// Baseline = the value each control starts at. It is only used to delta the
// bulky translation strings (trans_*): every other setting is always sent so
// the watch ends up matching exactly what the page shows (WYSIWYG). The phone
// copy used to seed this baseline is not authoritative — it can drift from the
// watch — so a "shown but unchanged" value must never be silently dropped.
var baseline = {};
for (var s = 0; s < spec.length; s++) {
var sec = spec[s];
Expand Down Expand Up @@ -237,8 +239,14 @@ function buildConfigPage(spec, current) {
'els=document.querySelectorAll("[data-key]");for(var i=0;i<els.length;i++){' +
'o[els[i].getAttribute("data-key")]=val(els[i]);}' +
'if(augment(o)===false)return;' +
// Send only changed keys: the watch keeps its current value for anything omitted.
'var send={};for(var k in o){if(String(o[k])!==String(BASELINE[k]))send[k]=o[k];}' +
// WYSIWYG: always send every non-translation setting so the watch matches the
// page exactly (a drifted phone-side baseline must never leave a shown value
// unapplied). Translation strings (trans_*) are bulk; keep them delta-only so
// the payload stays within the watch's 1280-byte inbox — the language picker
// already resends them whenever it changes them.
'var send={};for(var k in o){' +
'if(k.indexOf("trans_")===0){if(String(o[k])!==String(BASELINE[k]))send[k]=o[k];}' +
'else{send[k]=o[k];}}' +
'document.location=RET+encodeURIComponent(JSON.stringify(send));};';
return '<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">' +
'<meta name="viewport" content="width=device-width,initial-scale=1">' +
Expand Down
5 changes: 3 additions & 2 deletions src/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ static persist_adv_settings s_adv = {
.invertTopSlot = 0,
.invertBotSlot = 0,
.showStatus = 1,
.showStatusBat = 100,
.showStatusBat = 20, // matches the config page default; threshold for "when battery low"
.showDate = 1,
.DND_start = 0,
.DND_stop = 0,
Expand All @@ -54,7 +54,8 @@ static persist_adv_settings s_adv = {
.clock_font = 1,
.token_type = { 0, 0 },
.token_code = { "", "" },
.slots = { 0, 1, 2, 3, 0, 1, 0, 1, 0, 1 }
.slots = { 0, 1, 2, 3, 0, 1, 0, 1, 0, 1 },
.weather_icons = 0 // Default: colour on colour watches, B&W on the rest
};

persist *settings_get(void) { return &s_settings; }
Expand Down
3 changes: 3 additions & 0 deletions src/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ typedef struct persist_adv_settings { // 243 bytes
uint8_t token_type[2]; // 2 bytes
char token_code[2][65]; //130 bytes
uint8_t slots[10]; // 10 bytes
uint8_t weather_icons; // 1 byte: 0 default (colour where supported), 1 force colour, 2 force B&W
} __attribute__((__packed__)) persist_adv_settings;
// New fields go at the END: persist_read_data tolerates a shorter stored blob,
// leaving appended fields at their settings.c default for pre-existing installs.

persist *settings_get(void);
persist_adv_settings *adv_settings_get(void);
42 changes: 42 additions & 0 deletions src/theme.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,45 @@ void setTodayColors(GContext* ctx) {
graphics_context_set_fill_color(ctx, p.accent);
graphics_context_set_text_color(ctx, p.accent_fg);
}

GColor weather_glyph_color(char glyph) {
Palette p = theme_palette();
#ifdef PBL_COLOR
uint8_t style = adv_settings_get()->weather_icons;
if (style == WEATHER_ICONS_BW) { return p.fg; } // forced monochrome
// Default follows the Mono theme's single-colour look; Colour overrides it.
if (style == WEATHER_ICONS_DEFAULT && settings_get()->theme == THEME_MONO) { return p.fg; }
// Saturated tints chosen to read on both light and dark backgrounds. Clouds,
// fog, tornado and anything unmapped stay the theme fg (which reads anywhere).
switch (glyph) {
case 'I': // sun
return GColorChromeYellow;
case 'J': case 'K': // sunset / sunrise
return GColorOrange;
case 'N': case 'O': case 'P': case 'Q': case 'R': // moon phases
case 'S': case 'T': case 'U': case 'V':
return GColorPastelYellow;
case '$': case '%': case '&': // rain
case '\'': case '(': case ')': // showers
case '*': case '+': case ',': // downpour
case '-': case '.': case '/': // drizzle
return GColorVividCerulean;
case '0': case '1': case '2': // sleet
case '3': case '4': case '5': // hail
return GColorBlue;
case '6': case '7': case '8': // flurries
case '9': case ':': case ';': // snow
case 'W': // snowflake
return GColorVividCerulean;
case 'B': case 'C': case 'D': case 'E': // wind
return GColorJaegerGreen;
case 'F': case 'G': case 'H': // lightning
return GColorChromeYellow;
default: // clouds, fog, haze, …
return p.fg;
}
#else
(void)glyph;
return p.fg;
#endif
}
8 changes: 8 additions & 0 deletions src/theme.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ typedef struct {

enum { THEME_MONO = 0, THEME_FUNCTIONAL = 1, THEME_MINIMAL = 2, THEME_VIBRANT = 3, THEME_COUNT };
enum { THEME_MODE_LIGHT = 0, THEME_MODE_DARK = 1, THEME_MODE_AUTO = 2 };
// Weather icon style (adv_settings.weather_icons): Default follows the platform
// and the Mono theme; Colour/B&W force it regardless.
enum { WEATHER_ICONS_DEFAULT = 0, WEATHER_ICONS_COLOUR = 1, WEATHER_ICONS_BW = 2 };

// The active palette, resolved from settings (theme + mode; AUTO uses night).
Palette theme_palette(void);
Expand All @@ -22,3 +25,8 @@ Palette theme_palette(void);
void setColors(GContext* ctx); // base: bg + fg
void setInvColors(GContext* ctx); // swapped patch: fg-on-bg
void setTodayColors(GContext* ctx);// accent

// Meaningful tint for a Climacons weather glyph (sun yellow, rain blue, …),
// honouring the weather_icons setting. Returns the palette fg (monochrome) on
// 1-bit watches, when B&W is forced, or under Default + the Mono theme.
GColor weather_glyph_color(char glyph);
4 changes: 4 additions & 0 deletions src/weather.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ static void weather_render(Layer *me, GContext *ctx) {
int block = gap + temp_h; // total visual height of icon+temp
int top = (band_h - block) / 2;
if (top < 0) { top = 0; }
// Tint the condition glyph (color platforms, non-Mono theme); the temperature
// stays the theme color, so restore it afterwards.
graphics_context_set_text_color(ctx, weather_glyph_color(cond_current[0]));
graphics_draw_text(ctx, cond_current, climacons, GRect(2, top, icon_w, gap + 8), GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
graphics_context_set_text_color(ctx, theme_palette().fg);
graphics_draw_text(ctx, temp_current, temp_font, GRect(2, top + gap, icon_w + 2, temp_h + 8), GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
if (debug_get()->general) { app_log(APP_LOG_LEVEL_DEBUG, __FILE__, __LINE__, "Weather redrawing: %d, %s", weather_state()->current, weather_state()->condition); }
}
Expand Down
Loading