rep: more layout/design tweaks
parent
a783ee6435
commit
ef26eae665
315
apps/rep/app.ts
315
apps/rep/app.ts
|
|
@ -18,59 +18,44 @@ const reps: Rep[] = [
|
||||||
// 3 min static recovery
|
// 3 min static recovery
|
||||||
// 1 X 4 mins to finish
|
// 1 X 4 mins to finish
|
||||||
|
|
||||||
{dur:1/60, label:"1st-sec"},
|
{dur:3/60, label:"1st-sec"},
|
||||||
{dur:5/60, label:"5-sec"},
|
{dur:5/60, label:"5-sec"},
|
||||||
|
|
||||||
{dur:4, label:"jog"},
|
// {dur:4, label:"jog"},
|
||||||
{dur:4, label:"recovery"},
|
// {dur:4, label:"recovery"},
|
||||||
|
|
||||||
{dur:2, label:"jog"},
|
// {dur:2, label:"jog"},
|
||||||
{dur:2, label:"recovery"},
|
// {dur:2, label:"recovery"},
|
||||||
{dur:2, label:"jog"},
|
// {dur:2, label:"jog"},
|
||||||
{dur:2, label:"recovery"},
|
// {dur:2, label:"recovery"},
|
||||||
|
|
||||||
{dur:1, label:"jog"},
|
// {dur:1, label:"jog"},
|
||||||
{dur:1, label:"recovery"},
|
// {dur:1, label:"recovery"},
|
||||||
{dur:1, label:"jog"},
|
// {dur:1, label:"jog"},
|
||||||
{dur:1, label:"recovery"},
|
// {dur:1, label:"recovery"},
|
||||||
{dur:1, label:"jog"},
|
// {dur:1, label:"jog"},
|
||||||
{dur:1, label:"recovery"},
|
// {dur:1, label:"recovery"},
|
||||||
{dur:1, label:"jog"},
|
// {dur:1, label:"jog"},
|
||||||
{dur:1, label:"recovery"},
|
// {dur:1, label:"recovery"},
|
||||||
|
|
||||||
{dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"},
|
// {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"},
|
||||||
{dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"},
|
// {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"},
|
||||||
{dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"},
|
// {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"},
|
||||||
{dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"},
|
// {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"},
|
||||||
{dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"},
|
// {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"},
|
||||||
{dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"},
|
// {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"},
|
||||||
{dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"},
|
// {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"},
|
||||||
{dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"},
|
// {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"},
|
||||||
|
|
||||||
{dur:3, label:"static recovery"},
|
// {dur:3, label:"static recovery"},
|
||||||
{dur:4, label:"finish"},
|
// {dur:4, label:"finish"},
|
||||||
];
|
];
|
||||||
|
|
||||||
const fontSzMain = 64;
|
const fontSzMain = 64;
|
||||||
const fontSzRep = 20;
|
const fontSzRep = 20;
|
||||||
|
const fontSzRepDesc = 12;
|
||||||
|
|
||||||
// FIXME: `Layout_` name
|
// FIXME: `Layout_` name, e.g. Layout_.Align.Right
|
||||||
const repLayout = (id: string): (Layout_.Hierarchy & {type:"txt"})[] => [
|
|
||||||
{
|
|
||||||
id: `${id}_name`,
|
|
||||||
type: "txt",
|
|
||||||
font: `Vector:${fontSzRep}`,
|
|
||||||
label: "Name PH",
|
|
||||||
//pad: 4,
|
|
||||||
fillx: 1,
|
|
||||||
}, {
|
|
||||||
id: `${id}_dur`,
|
|
||||||
type: "txt",
|
|
||||||
font: `Vector:${fontSzRep}`,
|
|
||||||
label: "DURATION",
|
|
||||||
halign: Layout_.Align.Right,
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// top: show current rep: name & duration
|
// top: show current rep: name & duration
|
||||||
// middle: show time left on current rep
|
// middle: show time left on current rep
|
||||||
|
|
@ -80,53 +65,159 @@ const layout = new Layout({
|
||||||
type: "v",
|
type: "v",
|
||||||
c: [
|
c: [
|
||||||
{
|
{
|
||||||
id: "cur_time",
|
id: "duration",
|
||||||
type: "txt",
|
lazyBuster: 1,
|
||||||
|
type: "custom",
|
||||||
font: `Vector:${fontSzMain}` as FontNameWithScaleFactor, // modified in draw
|
font: `Vector:${fontSzMain}` as FontNameWithScaleFactor, // modified in draw
|
||||||
label: "MM:SS", // TODO: empty strings
|
|
||||||
fillx: 1,
|
fillx: 1,
|
||||||
filly: 1,
|
filly: 1,
|
||||||
|
render: (l: Layout_.RenderedHierarchy) => {
|
||||||
|
let lbl;
|
||||||
|
|
||||||
|
g.clearRect(l.x, l.y, l.x+l.w, l.y+l.h);
|
||||||
|
|
||||||
|
if(state){
|
||||||
|
const elapsed = getElapsed(state);
|
||||||
|
const i = currentRepIndex(elapsed);
|
||||||
|
|
||||||
|
if(i == null){
|
||||||
|
// FIXME: dodgy end-of-rep handling
|
||||||
|
lbl = msToHM(elapsed);
|
||||||
|
}else{
|
||||||
|
const thisDur = repDuration(reps[i]!);
|
||||||
|
const remaining = thisDur - elapsed;
|
||||||
|
lbl = msToHM(remaining);
|
||||||
|
|
||||||
|
const fract = elapsed / thisDur;
|
||||||
|
g.setColor("#00f")
|
||||||
|
.fillRect(
|
||||||
|
l.x,
|
||||||
|
l.y,
|
||||||
|
l.x + fract * l.w,
|
||||||
|
l.y+l.h
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
lbl = "RDY";
|
||||||
|
}
|
||||||
|
|
||||||
|
g.setColor(l.col || g.theme.fg)
|
||||||
|
.setFont(l.font!)
|
||||||
|
.setFontAlign(0, 0)
|
||||||
|
.drawString(
|
||||||
|
lbl,
|
||||||
|
l.x+(l.w>>1),
|
||||||
|
l.y+(l.h>>1)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "txt",
|
||||||
|
font: `Vector:${fontSzRepDesc}`,
|
||||||
|
label: "Activity / Duration",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: `cur_name`,
|
||||||
|
type: "txt",
|
||||||
|
font: `Vector:${fontSzRep}`,
|
||||||
|
label: "",
|
||||||
|
//pad: 4,
|
||||||
|
fillx: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "txt",
|
||||||
|
font: `Vector:${fontSzRepDesc}`,
|
||||||
|
label: "Next / Duration",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: `next_name`,
|
||||||
|
type: "txt",
|
||||||
|
font: `Vector:${fontSzRep}`,
|
||||||
|
label: "",
|
||||||
|
//pad: 4,
|
||||||
|
fillx: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "h",
|
type: "h",
|
||||||
c: repLayout("cur"),
|
c: [
|
||||||
filly: 1,
|
{
|
||||||
},
|
id: "prev",
|
||||||
{
|
type: "btn",
|
||||||
type: "h",
|
label: "<",
|
||||||
c: repLayout("next"),
|
fillx: 1,
|
||||||
filly: 1,
|
cb: () => onPrev(),
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "h",
|
|
||||||
c: [{
|
|
||||||
id: "play",
|
|
||||||
type: "btn",
|
|
||||||
label: "Play", // TODO: change
|
|
||||||
fillx: 1,
|
|
||||||
cb: () => {
|
|
||||||
console.log("cb()");
|
|
||||||
},
|
},
|
||||||
filly: 1,
|
{
|
||||||
}]
|
id: "play",
|
||||||
|
type: "btn",
|
||||||
|
label: "Play", // TODO: change
|
||||||
|
fillx: 1,
|
||||||
|
cb: () => onToggle(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "next",
|
||||||
|
type: "btn",
|
||||||
|
label: ">",
|
||||||
|
fillx: 1,
|
||||||
|
cb: () => onNext(),
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}, {lazy: true});
|
}, {lazy: true});
|
||||||
|
|
||||||
let index = 0;
|
const onToggle = () => {
|
||||||
let begin: number | undefined = Date.now(); // TODO: start null
|
if(!state){
|
||||||
let drawTimeout: number | undefined;
|
state = {
|
||||||
let paused = false;
|
begin: Date.now(),
|
||||||
let positioned = false;
|
accumulated: 0,
|
||||||
|
};
|
||||||
|
play(true, state);
|
||||||
|
}else{
|
||||||
|
play(paused, state);
|
||||||
|
}
|
||||||
|
|
||||||
const pad2 = (s: number) => ('0' + s.toFixed(0)).slice(-2);
|
drawRep();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPrev = () => {
|
||||||
|
};
|
||||||
|
|
||||||
|
const onNext = () => {
|
||||||
|
};
|
||||||
|
|
||||||
|
const play = (p: boolean, state: State) => {
|
||||||
|
if(p){
|
||||||
|
layout["play"]!.label = "Pause";
|
||||||
|
|
||||||
|
state.begin = Date.now();
|
||||||
|
|
||||||
|
drawInterval = setInterval(drawRep, 1000);
|
||||||
|
}else{
|
||||||
|
layout["play"]!.label = "Play";
|
||||||
|
|
||||||
|
const diff = Date.now() - state.begin;
|
||||||
|
state.accumulated += diff;
|
||||||
|
|
||||||
|
clearInterval(drawInterval!);
|
||||||
|
}
|
||||||
|
|
||||||
|
paused = !p;
|
||||||
|
}
|
||||||
|
|
||||||
|
type State = { begin: number, accumulated: number };
|
||||||
|
|
||||||
|
let state: State | undefined;
|
||||||
|
//let drawTimeout: number | undefined;
|
||||||
|
let paused = false; // TODO: ditch this, used drawInterval
|
||||||
|
let drawInterval: IntervalId | undefined;
|
||||||
|
|
||||||
const currentRepIndex = (elapsedMs: number) => {
|
const currentRepIndex = (elapsedMs: number) => {
|
||||||
let total = 0;
|
let total = 0;
|
||||||
let ent;
|
let ent;
|
||||||
for(let i = 0; ent = reps[i]; i++){
|
for(let i = 0; ent = reps[i]; i++){
|
||||||
total += ent.dur * 60 * 1000;
|
total += repDuration(ent);
|
||||||
if(elapsedMs < total)
|
if(elapsedMs <= total)
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -135,82 +226,64 @@ const currentRepIndex = (elapsedMs: number) => {
|
||||||
const repToLabel = (i: number, id: string) => {
|
const repToLabel = (i: number, id: string) => {
|
||||||
const rep = reps[i];
|
const rep = reps[i];
|
||||||
if(rep){
|
if(rep){
|
||||||
layout[`${id}_name`]!.label = `${i+1}: ${rep.label}`;
|
layout[`${id}_name`]!.label = `${i+1}: ${rep.label} / ${rep.dur.toFixed(0)}m`;
|
||||||
// FIXME: display time, i.e. hh:mm
|
// FIXME: display time, i.e. hh:mm
|
||||||
layout[`${id}_dur`]!.label = `${rep.dur.toFixed(0)}m`;
|
//layout[`${id}_dur`]!.label = ``;
|
||||||
}else{
|
}else{
|
||||||
emptyLabel(id);
|
emptyLabel(id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const emptyLabel = (id: string) => {
|
const emptyLabel = (id: string) => {
|
||||||
layout[`${id}_name`]!.label = "<none>";
|
layout[`${id}_name`]!.label = "<none> / 0m";
|
||||||
layout[`${id}_dur`]!.label = `0m`;
|
};
|
||||||
|
|
||||||
|
const pad2 = (s: number) => ('0' + s.toFixed(0)).slice(-2);
|
||||||
|
|
||||||
|
const repDuration = (rep: Rep) => rep.dur * 60 * 1000;
|
||||||
|
|
||||||
|
const getElapsed = (state: State) =>
|
||||||
|
Date.now() - state.begin + state.accumulated;
|
||||||
|
|
||||||
|
const msToHM = (ms: number) => {
|
||||||
|
const sec = Math.round(ms / 1000);
|
||||||
|
const min = Math.round(sec / 60);
|
||||||
|
return min.toFixed(0) + ":" + pad2(sec % 60);
|
||||||
};
|
};
|
||||||
|
|
||||||
const drawRep = () => {
|
const drawRep = () => {
|
||||||
if(paused || !begin){ // TODO: separate case for paused
|
(layout["duration"] as any).lazyBuster ^= 1;
|
||||||
layout.clear();
|
|
||||||
layout["cur_time"]!.label = "Ready";
|
|
||||||
|
|
||||||
if(!positioned){
|
if(state){
|
||||||
positioned = true;
|
// TODO: layout.clear(layout.next_name); layout.render(layout.next_name)
|
||||||
layout.update(); // position
|
|
||||||
|
const elapsed = getElapsed(state);
|
||||||
|
const i = currentRepIndex(elapsed);
|
||||||
|
if(i !== null){
|
||||||
|
repToLabel(i, "cur");
|
||||||
|
repToLabel(i+1, "next");
|
||||||
|
}else{
|
||||||
|
emptyLabel("cur");
|
||||||
|
emptyLabel("next");
|
||||||
}
|
}
|
||||||
|
|
||||||
layout.render(layout["play"]);
|
|
||||||
layout.render(layout["cur_time"]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: layout.clear(layout.next_name); layout.render(layout.next_name)
|
|
||||||
|
|
||||||
const now = Date.now();
|
|
||||||
|
|
||||||
const elapsed = now - begin;
|
|
||||||
const sec = Math.round(elapsed / 1000);
|
|
||||||
const min = Math.round(sec / 60);
|
|
||||||
const timeStr = min.toFixed(0) + ":" + pad2(sec % 60);
|
|
||||||
layout["cur_time"]!.label = timeStr;
|
|
||||||
|
|
||||||
const i = currentRepIndex(elapsed);
|
|
||||||
if(i !== null){
|
|
||||||
repToLabel(i, "cur");
|
|
||||||
repToLabel(i+1, "next");
|
|
||||||
}else{
|
|
||||||
emptyLabel("cur");
|
|
||||||
emptyLabel("next");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!positioned){
|
|
||||||
positioned = true;
|
|
||||||
layout.update(); // position
|
|
||||||
}
|
}
|
||||||
|
|
||||||
layout.render();
|
layout.render();
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
g.reset()
|
|
||||||
.clearRect(Bangle.appRect)
|
|
||||||
.setFontAlign(0, 0)
|
|
||||||
.setFont("Vector", 55)
|
|
||||||
.drawString(timeStr, x, y)
|
|
||||||
.setFont("Vector", 33)
|
|
||||||
.drawString(current ? current.label + " for " + current.dur : "<done>", x, y+48);
|
|
||||||
|
|
||||||
// TODO: figure out next rep change time? or every 5s, then countdown from 10-->0
|
// TODO: figure out next rep change time? or every 5s, then countdown from 10-->0
|
||||||
if (drawTimeout) clearTimeout(drawTimeout);
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
drawTimeout = setTimeout(function() {
|
drawTimeout = setTimeout(function() {
|
||||||
drawTimeout = undefined;
|
drawTimeout = undefined;
|
||||||
drawRep();
|
drawRep();
|
||||||
}, 1000 - (Date.now() % 1000));
|
}, 1000 - (Date.now() % 1000));
|
||||||
|
|
||||||
*/
|
*/
|
||||||
};
|
};
|
||||||
|
|
||||||
g.clear();
|
g.clear();
|
||||||
var drawInterval = setInterval(drawRep, 1000);
|
|
||||||
drawRep();
|
drawRep();
|
||||||
|
|
||||||
|
// TODO: swipe handlers
|
||||||
|
//Bangle.setUI
|
||||||
|
|
||||||
// TODO: widgets
|
// TODO: widgets
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue