2024-09-15 10:20:17 +00:00
|
|
|
(function(back, faceEdit, helpShown) {
|
|
|
|
// Shared library for face drawing and settings loading.
|
|
|
|
let lib = require('tinyheads.lib.js');
|
|
|
|
|
|
|
|
let paletteCanvas;
|
|
|
|
let featureColour = 'faceColour';
|
|
|
|
let colourSelectTimeout;
|
|
|
|
|
2024-11-04 22:10:58 +00:00
|
|
|
const scale = lib.settingsScale;
|
2024-09-15 10:20:17 +00:00
|
|
|
|
|
|
|
// 27 colours
|
|
|
|
let colours = [
|
|
|
|
'#000', '#008', '#00f',
|
|
|
|
'#800', '#808', '#80f',
|
|
|
|
'#080', '#088', '#08f',
|
|
|
|
'#880', '#888', '#88f',
|
|
|
|
'#0f0', '#0f8', '#0ff',
|
|
|
|
'#8f0', '#8f8', '#8ff',
|
|
|
|
'#f00', '#f08', '#f0f',
|
|
|
|
'#001', '#001', '#001',
|
|
|
|
'#f80', '#f88', '#f8f',
|
|
|
|
'#001', '#001', '#001',
|
|
|
|
'#ff0', '#ff8', '#fff',
|
|
|
|
'#001', '#001', '#001'
|
|
|
|
];
|
|
|
|
|
|
|
|
let colorW = 6;
|
|
|
|
let colorScale = Math.floor(176/colorW);
|
|
|
|
|
|
|
|
function writeSettings() {
|
|
|
|
require('Storage').writeJSON(lib.settingsFile, lib.settings);
|
|
|
|
}
|
|
|
|
|
|
|
|
function setSetting(key,value) {
|
|
|
|
lib.settings[key] = value;
|
|
|
|
writeSettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper method which uses int-based menu item for set of string values and their labels
|
|
|
|
function stringItems(key, startvalue, values, labels) {
|
|
|
|
return {
|
|
|
|
value: (startvalue === undefined ? 0 : values.indexOf(startvalue)),
|
|
|
|
format: v => labels[v],
|
|
|
|
min: 0,
|
|
|
|
max: values.length - 1,
|
|
|
|
wrap: true,
|
|
|
|
step: 1,
|
|
|
|
onchange: v => {
|
|
|
|
setSetting(key,values[v]);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper method which breaks string set settings down to local settings object
|
|
|
|
function stringInSettings(name, values, labels) {
|
|
|
|
return stringItems(name,lib.settings[name], values, labels);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Colour selection mode
|
|
|
|
function colourSelect(index) {
|
|
|
|
Bangle.setUI({
|
|
|
|
mode: "custom",
|
|
|
|
touch: colourTouchHandler,
|
|
|
|
btn: () => { // On button press write colour settings and return to feature selection
|
|
|
|
colourSelectTimeout = null;
|
|
|
|
writeSettings();
|
|
|
|
featureSelect();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Create a canvas for the palette and set each pixel
|
|
|
|
if (paletteCanvas === undefined) {
|
|
|
|
paletteCanvas = Graphics.createArrayBuffer(colorW, colorW, 8, {msb:true});
|
|
|
|
paletteCanvas.setBgColor(0, 0, 0);
|
|
|
|
paletteCanvas.clear();
|
|
|
|
|
|
|
|
|
|
|
|
for (let i=0; i<(colorW*colorW); i++) {
|
|
|
|
let x = (i % colorW);
|
|
|
|
let y = Math.floor(i / colorW);
|
|
|
|
|
|
|
|
paletteCanvas.setPixel(x, y, colours[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scale the canvas to full screen size
|
|
|
|
g.setBgColor(0, 0, 0);
|
|
|
|
g.clear();
|
|
|
|
g.drawImage(paletteCanvas, 0, 0, {scale: colorScale});
|
|
|
|
g.setColor(1, 1, 1);
|
|
|
|
g.setFontAlign(0, 0);
|
|
|
|
g.setFont("6x8:2");
|
2024-10-14 21:54:41 +00:00
|
|
|
let c = featureColour.split('Colour')[0];
|
2024-09-15 10:20:17 +00:00
|
|
|
g.drawString(c[0].toUpperCase() + c.slice(1), 132, 132);
|
|
|
|
|
|
|
|
if (index !== undefined) { // If a colour has been selected draw it in a larger box
|
2024-10-14 21:54:41 +00:00
|
|
|
let x = (index % colorW) * colorScale;
|
|
|
|
let y = Math.floor((index / colorW)) * colorScale;
|
2024-09-15 10:20:17 +00:00
|
|
|
g.setColor(0, 0, 0);
|
|
|
|
g.fillRect(x-(colorScale/2), y-(colorScale/2), x+(colorScale/2)+colorScale, y+(colorScale/2)+colorScale);
|
|
|
|
g.setColor(colours[index]);
|
|
|
|
g.fillRect(x-(colorScale/2)-2, y-(colorScale/2)-2, x+(colorScale/2)+colorScale-2, y+(colorScale/2)+colorScale-2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Feature selection mode
|
|
|
|
function featureSelect() {
|
|
|
|
E.showMenu(); // Remove previous menu
|
|
|
|
Bangle.setUI({
|
|
|
|
mode: "custom",
|
|
|
|
touch: featureTouchHandler,
|
|
|
|
btn: () => { // On button press write settings and return to the main menu
|
|
|
|
writeSettings();
|
|
|
|
require("widget_utils").show();
|
|
|
|
E.showMenu(mainMenu);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
lib.drawFace(scale);
|
|
|
|
|
|
|
|
// Arrows
|
|
|
|
for (let i=0; i<4; i++) {
|
|
|
|
g.setColor(0, 0, 0);
|
|
|
|
g.fillPolyAA([0, 22+(i*44), 34, 5+(i*44), 34, 39+(i*44)]);
|
|
|
|
g.fillPolyAA([175, 22+(i*44), 141, 5+(i*44), 141, 39+(i*44)]);
|
|
|
|
g.setColor(1, 1, 0);
|
|
|
|
g.fillPolyAA([5, 22+(i*44), 31, 10+(i*44), 31, 34+(i*44)]);
|
|
|
|
g.fillPolyAA([170, 22+(i*44), 144, 10+(i*44), 144, 34+(i*44)]);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cycle between features
|
|
|
|
function modifyFeature(feature, inc, max) {
|
|
|
|
lib.settings[feature] += inc;
|
|
|
|
if (lib.settings[feature] < 0) {
|
|
|
|
lib.settings[feature] = max - 1;
|
|
|
|
} else if (lib.settings[feature] >= max) {
|
|
|
|
lib.settings[feature] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let featureTouchHandler = (button, xy) => {
|
|
|
|
let inc = 0;
|
|
|
|
// Left size decrements feature, right side increments
|
|
|
|
if (xy.x < 45) {
|
|
|
|
inc = -1;
|
|
|
|
}
|
|
|
|
if (xy.x > 130) {
|
|
|
|
inc = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Center selects feature for colour changing
|
|
|
|
if (xy.x>44 && xy.x<132) {
|
|
|
|
let yOffset = (g.getHeight() - (lib.faceH * scale)) / 2;
|
|
|
|
let featureHeight = (lib.faceH * scale) / 4; // All features are considered to be of equal heights when selecting
|
|
|
|
if (xy.y > yOffset && xy.y < yOffset + (lib.faceH * scale)) {
|
|
|
|
if (xy.type == 0) { // Short press, select feature
|
|
|
|
if (xy.y < yOffset + featureHeight) {
|
|
|
|
featureColour = 'hairColour';
|
|
|
|
} else if (xy.y < yOffset + featureHeight*2) {
|
|
|
|
featureColour = 'eyesColour';
|
|
|
|
} else if (xy.y < yOffset + featureHeight*3) {
|
|
|
|
featureColour = 'noseColour';
|
|
|
|
} else {
|
|
|
|
featureColour = 'mouthColour';
|
|
|
|
}
|
|
|
|
} else { // Long press, select skin
|
|
|
|
featureColour = 'faceColour';
|
|
|
|
}
|
|
|
|
// Show colour palette
|
|
|
|
colourSelect();
|
|
|
|
}
|
|
|
|
} else { // Which arrow was pressed
|
|
|
|
if (xy.y < 44) {
|
|
|
|
modifyFeature('hairNum', inc, lib.maxHair);
|
|
|
|
} else if (xy.y < 88) {
|
|
|
|
modifyFeature('eyesNum', inc, lib.maxEyes);
|
|
|
|
} else if (xy.y < 132) {
|
|
|
|
modifyFeature('noseNum', inc, lib.maxNose);
|
|
|
|
} else {
|
|
|
|
modifyFeature('mouthNum', inc, lib.maxMouth);
|
|
|
|
}
|
|
|
|
if (inc !== 0) { // Redraw if feature has been altered
|
|
|
|
featureSelect();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let colourTouchHandler = (button, xy) => {
|
|
|
|
let index = Math.floor(xy.x / colorScale) + (Math.floor(xy.y / colorScale) * colorW);
|
|
|
|
if (colours[index] !== '#001') {
|
|
|
|
lib.settings[featureColour] = colours[index];
|
|
|
|
// Redraw the palette with the chosen colour enlarged for a few ms
|
|
|
|
colourSelect(index);
|
|
|
|
if (colourSelectTimeout) clearTimeout(colourSelectTimeout);
|
|
|
|
colourSelectTimeout = setTimeout(function() {
|
|
|
|
colourSelectTimeout = undefined;
|
|
|
|
// If colour choice not quickly changed, save settings and return to feature selection
|
|
|
|
writeSettings();
|
|
|
|
featureSelect();
|
|
|
|
}, 700);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
function editFace() {
|
|
|
|
require("widget_utils").hide();
|
|
|
|
if (! helpShown) { // Don't show the help text every time
|
|
|
|
E.showPrompt('Editing a face -\nUse arrows to cycle through facial features.', {buttons : {"Ok":true}}).then(function(v) {
|
|
|
|
E.showPrompt('Tap feature to change colour, long press the face to change skin colour.', {buttons : {"Ok":true}}).then(function(v) {
|
|
|
|
helpShown = true;
|
|
|
|
featureSelect();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
featureSelect();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mainMenu = {
|
|
|
|
'' : {
|
|
|
|
'title' : 'Tinyheads',
|
|
|
|
back: () => {
|
|
|
|
back();
|
|
|
|
},
|
|
|
|
},
|
|
|
|
'Face': () => {
|
|
|
|
editFace();
|
|
|
|
},
|
|
|
|
'Analog Clock': stringInSettings('analogClock', ['off', 'on', 'unlock'], ['Off', 'On', 'Unlocked']),
|
|
|
|
'Analog Colour': stringInSettings('analogColour', ['#000', '#fff', '#f00', '#0f0', '#00f', '#ff0', '#0ff', '#f0f'], ['Black', 'White', 'Red', 'Green', 'Blue', 'Yellow', 'Cyan', 'Magenta']),
|
|
|
|
'Digital Clock': stringInSettings('digitalClock', ['off', 'on', 'unlock'], ['Off', 'On', 'Unlocked']),
|
|
|
|
'Digital Position': stringInSettings('digitalPosition', ['bottom', 'top'], ['Bottom', 'Top']),
|
|
|
|
'Show Widgets': stringInSettings('showWidgets', ['off', 'on', 'unlock'], ['Off', 'On', 'Unlocked']),
|
|
|
|
'BT Status Eyes': {
|
|
|
|
value: !!lib.settings.btStatusEyes,
|
|
|
|
onchange: v => {
|
|
|
|
setSetting('btStatusEyes', v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (faceEdit) { // faceEdit passed from main clock so we're taken directly to the feature selection
|
|
|
|
E.showMenu();
|
|
|
|
editFace();
|
|
|
|
} else { // Otherwise if entered from settings display main menu
|
|
|
|
E.showMenu(mainMenu);
|
|
|
|
}
|
|
|
|
})
|