BangleApps/apps/scribble/app.js

469 lines
10 KiB
JavaScript
Raw Normal View History

2021-12-07 17:48:39 +00:00
const black = "#000000";
const white = "#ffffff";
const gray1 = "#444444";
const gray2 = "#888888";
const gray3 = "#bbbbbb";
const red = "#FF0000";
const green = "#00FF00";
const blue = "#0000FF";
const transp = -1;
const abc = "abcdefghijklmnopqrstuvwxyz1234567890";
// const abc_up = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
const uppercase = 1;
const last_layer = false; // set to true at the last layer of the tree
let chunk_size = 6;
const font_height = 2;
const global_font = "Dennis8";
require("FontDennis8").add(Graphics);
2021-12-07 18:49:11 +00:00
const editable_buf = "Scribble";
2021-12-07 17:48:39 +00:00
2021-12-07 18:49:11 +00:00
const left = 3;
2021-12-07 17:48:39 +00:00
const _screen_mid = g.getWidth() / 2;
const right = 176 - 4;
const box_size = {
w: _screen_mid - 6,
2021-12-07 18:49:11 +00:00
h: 46,
2021-12-07 17:48:39 +00:00
};
const spacing = 4;
const border = 4;
const top_start = 25;
const pos_y = [
top_start,
top_start + (box_size.h + spacing),
top_start + (box_size.h + spacing) * 2,
];
// list of points to render
const points = {
"3x2": [{ x: left, y: pos_y[0] },
{ x: left, y: pos_y[1] },
{ x: left, y: pos_y[2] },
{ x: _screen_mid + 2, y: pos_y[0] },
{ x: _screen_mid + 2, y: pos_y[1] },
{ x: _screen_mid + 2, y: pos_y[2] },
]
};
g.theme = {
fg: white,
bg: black,
fg2: white,
bg2: black,
fgH: black,
bgH: red,
dark: false,
};
const maxX = g.getWidth();
const maxY = g.getHeight();
const fontSize = g.getWidth() > 200 ? 2 : 1;
const rowN = 7;
const colN = 7;
const headerH = maxY / 7;
const rowH = (maxY - headerH) / rowN;
const colW = maxX / colN;
function getRndInteger(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
class Window {
constructor(label, bgCol) {
this.label = "win_"
this.label += (typeof label !== "undefined") ? label : "Unset";
console.log(`Constructing Window ${this.label}, args: ${arguments}`)
this.bgCol = bgCol;
this.layers = [];
}
push(layer) {
layer.label=`${this.layers.length}_${layer.label}`;
this.layers.push(layer);
}
pop() {
this.layers.pop();
}
top_layer() {
return this.layers[this.layers.length - 1];
}
render() {
if (this.bgCol !== transp) {
console.log(`${this.label}: filling bg in ${this.bgCol}`);
g.setColor(this.bgCol);
g.fillRect(0, 0, g.getWidth(), g.getHeight());
}
let i = 0;
this.layers.forEach((lyr) => {
// console.log(`Rendering Layer ${i} ${lyr.label}`)
i++;
lyr.render();
});
}
}
class Layer {
constructor(label) {
this.label = "lyr_"
this.label += (typeof label !== "undefined") ? label : "Unset";
console.log(`Constructing Layer ${this.label}, args: ${arguments}`)
this.items = [];
// console.log(`bg is ${bg} type ${typeof bg}`)
}
push(button) {
this.items.push(button);
}
setLabel(label) {
this.label = label;
}
parseTaps(xy) {
this.items.forEach(item => {
// // print(item)
if (item.was_tapped(xy)) {
// pass parent layer to the tapped button
item.callback(this);
}
});
}
render() {
this.items.forEach((item) => {
item.render();
});
}
}
class BTN_layer extends Layer {
constructor(label, layout) {
Layer.call(this, label)
this.alphabet = (uppercase) ? abc.toUpperCase() : abc;
console.log(`Constructing BTN_Layer ${this.label}, layout ${this.layout}`)
if (layout in points) {
this.create_layout(layout);
}
else {
throw `Invalid layout passed ->[${layout}]`;
}
// // print(this);
}
render() {
Layer.prototype.render.call(this);
}
create_layout(layout) {
console.log(`Creating layout ${layout}`);
let start_p = 0;
this.items = this.push_buttons(points[layout], this.alphabet, start_p, chunk_size)
}
push_buttons(points, in_string, start_p) {
items = [];
spacer = "" // char interposed b/w the two halves of text per button
for (let i = 0; i < points.length; i++) {
substr = `${in_string.substring(
start_p,
start_p + chunk_size / 2
)}${spacer}${in_string.substring(start_p + chunk_size / 2, start_p + chunk_size)}`;
btn_label =
uppercase === 1
? substr.toUpperCase()
: substr;
start_p += chunk_size;
items.push(
new Button(
i, // ID of button
points[i].x, // left
points[i].y, // top
btn_label, // text to render in the button
box_size.w, // width
box_size.h, // height
g.theme.bg, // box bg
white, // box fill
black // text col
)
);
}
return items;
}
update_labels(in_string, start_p, chk_size) {
// print(`Updating labels | in_string ${in_string} start_p ${start_p} chk_size ${chk_size}`);
in_string.replace('\n', ''); // remove newlines just in case
spacer = "" // char interposed b/w the two halves of text per button
for (let i = 0; i < this.items.length; i++) {
item = this.items[i];
substr = (chk_size < 3)
? in_string.substring(start_p + chk_size * i, start_p + (chk_size * (i + 1)))
: `${in_string.substring(
start_p + chk_size * i,
start_p + chk_size * i + chk_size / 2
)}${spacer}${in_string.substring(start_p + chk_size * i + chk_size / 2, start_p + chk_size * i + chk_size)}`;
// // print(`(chk_size > 3): ${(chk_size > 3)}`)
// print(`Label ${i} -> ${substr}`);
item.setLabel(substr);
}
}
zoom_in(id) {
let start_p = id * chunk_size;
// print(`Zooming in | start_p ${start_p}`)
if (chunk_size % this.items.length !== 0) {
throw `Chunk size [${chunk_size}] does not fit #btns [${this.items.length}]`
}
subchunk_size = chunk_size / this.items.length;
substr = this.alphabet.substring(start_p, start_p + chunk_size);
// print(`substr ${substr}`);
// print(`subchunk_size ${subchunk_size}`);
this.update_labels(substr, 0, subchunk_size);
}
}
class Button {
constructor(id, x, y, text, w, h, col, bgCol, txtCol, font) {
this.id = id;
this.label = `btn_${this.id}`;
this.text = text;
this.x1 = x;
this.y1 = y;
this.w = w;
this.h = h;
this.col = typeof col !== "undefined" ? col : black;
this.bgCol = typeof bgCol !== "undefined" ? bgCol : gray2;
this.txtCol = typeof txtCol !== "undefined" ? txtCol : black;
// this.font = font;
this.x2 = this.x1 + this.w;
this.y2 = this.y1 + this.h;
this.center = {
x: (this.x1 + this.x2) / 2,
y: (this.y1 + this.y2) / 2,
};
console.log(`Constructed button `)
// // print(this);
}
render() {
// console.log(
// `Button ${this.text} -> P1: (${this.x1}, ${this.y1}) | P2: (${this.x2}, ${this.y2})`
// );
g.setColor(this.bgCol);
g.fillRect(this.x1, this.y1, this.x2, this.y2);
g.setColor(this.col);
g.drawRect(this.x1, this.y1, this.x2, this.y2);
g.setColor(this.txtCol);
g.setFontAlign(0, 0).setFont(global_font, font_height);
g.drawString(this.text, this.center.x, this.center.y);
}
// short tap callback func
callback(parent_layer) {
// print(`Tapped button ${this.id}`);
// this.highlight(); // TODO set up highlighting
if (last_layer) {
l_text.items[0].text += this.text;
// print(`Updated buffer to ${l_text.items[0].text}`)
parent_layer.update_labels(parent_layer.alphabet, 0, chunk_size);
last_layer = false;
}
else {
parent_layer.zoom_in(this.id);
last_layer = true;
}
}
was_tapped(xy) {
var x = xy.x;
var y = xy.y;
if ((x > this.x1 && x < this.x2) && (y > this.y1 && y < this.y2)) {
return true;
}
else {
return false;
}
}
setLabel(lbl) {
// // print(`Button ${this.id}, updating label ${this.text} with ${lbl}`);
this.text = lbl;
}
getLabel(lbl) {
return this.label;
}
highlight() {
g.setColor(g.theme.bgH);
g.fillRect(this.x1, this.y1, this.x2, this.y2);
g.setColor(g.theme.fgH);
g.drawRect(this.x1, this.y1, this.x2, this.y2);
g.setColor(this.fg);
g.setFontAlign(0, 0).setFont(global_font, font_height);
g.drawString(this.text, this.center.x, this.center.y);
}
}
class TextBox {
constructor(x, y, text, col) {
// x and y are the center points
this.x = x;
this.y = y;
this.text = (typeof text !== undefined) ? text : "Default";
this.col = (typeof col !== undefined) ? col : red;
// console.log(`Constr TextBox ${this.text} -> Center: (${this.x}, ${this.y}) | Col ${this.col}`);
}
render() {
// console.log(`Rendering TextBox`)
var align_center = (0, 1);
var align_right = (0, 0);
alignment = (g.stringWidth(this.text) < g.getWidth()) ? align_center : align_right;
// coords = (g.stringWidth(this.text) < g.getWidth()- 20) ? {x:this.x, y:this.y} : {x:g.getWidth()-border, y:this.y}
coords = { x: this.x, y: this.y };
g.setColor(this.col);
g.setFontAlign(0, 0).setFont(global_font, font_height);
g.drawString(this.text, coords.x, coords.y);
}
}
/* Screen refresh *************************************/
function draw(obj) {
console.log("draw()");
obj.render();
}
let tickTimer;
function clearTickTimer() {
if (tickTimer) {
clearTimeout(tickTimer);
tickTimer = undefined;
}
}
function queueNextTick() {
clearTickTimer();
tickTimer = setTimeout(tick, 5000);
}
function tick() {
console.log("tick");
draw(window);
// queueNextTick();
}
/* Init **********************************************/
var window = new Window("abc", red);
var l_btns = new BTN_layer("btns", "3x2");
var l_text = new Layer("text"); // black
var box = new TextBox(
_screen_mid,
12,
editable_buf,
white
);
l_text.push(box);
window.push(l_text);
window.push(l_btns);
// Set up callbacks for touches
Bangle.on('touch', function (button, xy) {
window.top_layer().parseTaps(xy);
window.render();
});
Bangle.on('swipe', function (direction) {
console.log(`Swipe dir ${direction}`);
if (direction === -1) { // left
l_text.items[0].text = l_text.items[0].text.slice(0, -1);
} else if (direction == 1) { // right
l_text.items[0].text += ' ';
}
window.render();
});
// Clear the screen once, at startup
g.clear();
// Start ticking
tick();