forked from espruino/BangleApps
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request espruino#1821 from rigrig/clock-module
ClockFace module
- Loading branch information
Showing
5 changed files
with
261 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/* | ||
Most of the boilerplate needed to run a clock. | ||
See ClockFace.md for documentation | ||
*/ | ||
function ClockFace(options) { | ||
if ("function"=== typeof options) options = {draw: options}; // simple usage | ||
// some validation, in the hopes of at least catching typos/basic mistakes | ||
Object.keys(options).forEach(k => { | ||
if (![ | ||
"precision", | ||
"init", "draw", "update", | ||
"pause", "resume", | ||
"up", "down", "upDown" | ||
].includes(k)) throw `Invalid ClockFace option: ${k}`; | ||
}); | ||
if (!options.draw && !options.update) throw "ClockFace needs at least one of draw() or update() functions"; | ||
this.draw = options.draw || (t=> { | ||
options.update.apply(this, [t, {d: true, h: true, m: true, s: true}]); | ||
}); | ||
this.update = options.update || (t => { | ||
g.clear(); | ||
options.draw.apply(this, [t, {d: true, h: true, m: true, s: true}]); | ||
}); | ||
if (options.precision===1000||options.precision===60000) throw "ClockFace precision is in seconds, not ms"; | ||
this.precision = (options.precision || 60); | ||
if (options.init) this.init = options.init; | ||
if (options.pause) this._pause = options.pause; | ||
if (options.resume) this._resume = options.resume; | ||
if ((options.up || options.down) && options.upDown) throw "ClockFace up/down and upDown cannot be used together"; | ||
if (options.up || options.down) this._upDown = (dir) => { | ||
if (dir<0 && options.up) options.up.apply(this); | ||
if (dir>0 && options.down) options.down.apply(this); | ||
}; | ||
if (options.upDown) this._upDown = options.upDown; | ||
} | ||
|
||
ClockFace.prototype.tick = function() { | ||
const time = new Date(); | ||
const now = { | ||
d: `${time.getFullYear()}-${time.getMonth()}-${time.getDate()}`, | ||
h: time.getHours(), | ||
m: time.getMinutes(), | ||
s: time.getSeconds(), | ||
}; | ||
if (!this._last) { | ||
g.clear(true); | ||
Bangle.drawWidgets(); | ||
g.reset(); | ||
this.draw.apply(this, [time, {d: true, h: true, m: true, s: true}]); | ||
} else { | ||
let c = {d: false, h: false, m: false, s: false}; // changed | ||
if (now.d!==this._last.d) c.d = c.h = c.m = c.s = true; | ||
else if (now.h!==this._last.h) c.h = c.m = c.s = true; | ||
else if (now.m!==this._last.m) c.m = c.s = true; | ||
else if (now.s!==this._last.s) c.s = true; | ||
g.reset(); | ||
this.update.apply(this, [time, c]); | ||
} | ||
this._last = now; | ||
if (this.paused) return; // called redraw() while still paused | ||
// figure out timeout: if e.g. precision=60s, update at the start of a new minute | ||
const interval = this.precision*1000; | ||
this._timeout = setTimeout(() => this.tick(), interval-(Date.now()%interval)); | ||
}; | ||
|
||
ClockFace.prototype.start = function() { | ||
Bangle.loadWidgets(); | ||
if (this.init) this.init.apply(this); | ||
if (this._upDown) Bangle.setUI("clockupdown", d=>this._upDown.apply(this,[d])); | ||
else Bangle.setUI("clock"); | ||
delete this._last; | ||
this.tick(); | ||
|
||
Bangle.on("lcdPower", on => { | ||
if (on) this.resume(); | ||
else this.pause(); | ||
}); | ||
}; | ||
|
||
ClockFace.prototype.pause = function() { | ||
if (!this._timeout) return; // already paused | ||
clearTimeout(this._timeout); | ||
delete this._timeout; | ||
this.paused = true; // apps might want to check this | ||
if (this._pause) this._pause.apply(this); | ||
}; | ||
ClockFace.prototype.resume = function() { | ||
if (this._timeout) return; // not paused | ||
delete this._last; | ||
delete this.paused; | ||
if (this._resume) this._resume.apply(this); | ||
this.tick(true); | ||
}; | ||
|
||
/** | ||
* Force a complete redraw | ||
*/ | ||
ClockFace.prototype.redraw = function() { | ||
delete this._last; | ||
this.tick(); | ||
}; | ||
|
||
exports = ClockFace; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
ClockFace | ||
========= | ||
|
||
This module handles most of the tasks needed to set up a clock, so you can | ||
concentrate on drawing the time. | ||
|
||
Example | ||
------- | ||
Tthe [tutorial clock](https://www.espruino.com/Bangle.js+Clock) converted to use | ||
this module: | ||
|
||
```js | ||
|
||
// Load fonts | ||
require("Font7x11Numeric7Seg").add(Graphics); | ||
// position on screen | ||
const X = 160, Y = 140; | ||
|
||
var ClockFace = require("ClockFace"); | ||
var clock = new ClockFace({ | ||
precision: 1, // update every second | ||
draw: function(d) { | ||
// work out how to display the current time | ||
var h = d.getHours(), m = d.getMinutes(); | ||
var time = (" "+h).substr(-2)+":"+("0"+m).substr(-2); | ||
// draw the current time (4x size 7 segment) | ||
g.setFont("7x11Numeric7Seg", 4); | ||
g.setFontAlign(1, 1); // align right bottom | ||
g.drawString(time, X, Y, true /*clear background*/); | ||
// draw the seconds (2x size 7 segment) | ||
g.setFont("7x11Numeric7Seg", 2); | ||
g.drawString(("0"+d.getSeconds()).substr(-2), X+30, Y, true /*clear background*/); | ||
// draw the date, in a normal font | ||
g.setFont("6x8"); | ||
g.setFontAlign(0, 1); // align center bottom | ||
// pad the date - this clears the background if the date were to change length | ||
var dateStr = " "+require("locale").date(d)+" "; | ||
g.drawString(dateStr, g.getWidth()/2, Y+15, true /*clear background*/); | ||
} | ||
}); | ||
clock.start(); | ||
|
||
``` | ||
|
||
|
||
|
||
Complete Usage | ||
-------------- | ||
|
||
```js | ||
|
||
var ClockFace = require("ClockFace"); | ||
var clock = new ClockFace({ | ||
precision: 1, // optional, defaults to 60: how often to call update(), in seconds | ||
init: function() { // optional | ||
// called only once before starting the clock, but after setting up the | ||
// screen/widgets, so you can use Bangle.appRect | ||
}, | ||
draw: function(time, changed) { // at least draw or update is required | ||
// (re)draw entire clockface, time is a Date object | ||
// `changed` is the same format as for update() below, but always all true | ||
}, | ||
// The difference between draw() and update() is that the screen is cleared | ||
// before draw() is called, so it needs to always redraw the entire clock | ||
update: function(time, changed) { // at least draw or update is required | ||
// redraw date/time, time is a Date object | ||
// if you want, you can only redraw the changed parts: | ||
if (changed.d) // redraw date (changed.h/m/s will also all be true) | ||
if (changed.h) // redraw hours | ||
if (changed.m) // redraw minutes | ||
if (changed.s) // redraw seconds | ||
}, | ||
pause: function() { // optional, called when the screen turns off | ||
// for example: turn off GPS/compass if the watch used it | ||
}, | ||
resume: function() { // optional, called when the screen turns on | ||
// for example: turn GPS/compass back on | ||
}, | ||
up: function() { // optional, up handler | ||
}, | ||
down: function() { // optional, down handler | ||
}, | ||
upDown: function(dir) { // optional, combined up/down handler | ||
if (dir === -1) // Up | ||
else // (dir === 1): Down | ||
}, | ||
}); | ||
clock.start(); | ||
|
||
``` | ||
|
||
|
||
Simple Usage | ||
------------ | ||
Basic clocks can pass just a function to redraw the entire screen every minute: | ||
|
||
```js | ||
|
||
var ClockFace = require("ClockFace"); | ||
var clock = new ClockFace(function(time) { | ||
// draw the current time at the center of the screen | ||
g.setFont("Vector:50").setFontAlign(0, 0) | ||
.drawString( | ||
require("locale").time(time, true), | ||
Bangle.appRect.w/2, Bangle.appRect.h/2 | ||
); | ||
}); | ||
clock.start(); | ||
|
||
``` |