From 78d9eeac27235ca7ac2dfd23f0d13e45164c9537 Mon Sep 17 00:00:00 2001 From: parg-mtwn <165780092+parg-mtwn@users.noreply.github.com> Date: Sat, 15 Jun 2024 03:35:08 +0200 Subject: [PATCH] add new module: more_pickers --- modules/more_pickers.js | 198 ++++++++++++++++++++++++++++++++++++++++ modules/more_pickers.md | 93 +++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 modules/more_pickers.js create mode 100644 modules/more_pickers.md diff --git a/modules/more_pickers.js b/modules/more_pickers.js new file mode 100644 index 000000000..596b36fdf --- /dev/null +++ b/modules/more_pickers.js @@ -0,0 +1,198 @@ +/* see more_pickers.md for more information */ + +exports.doublePicker = function (options) { + var menuIcon = "\0\f\f\x81\0\xFF\xFF\xFF\0\0\0\0\x0F\xFF\xFF\xF0\0\0\0\0\xFF\xFF\xFF"; + + var R = Bangle.appRect; + g.reset().clearRect(R); + g.setFont("12x20").setFontAlign(0, 0).drawString(menuIcon + " " + options.title, R.x + R.w / 2, R.y + 12); + + var v_1 = options.value_1; + var v_2 = options.value_2; + + function draw() { + g.setColor(g.theme.bg2) + .fillRect(14, 60, 81, 166) + .fillRect(95, 60, 162, 166); + + g.setColor(g.theme.fg2) + .fillPoly([47.5, 68, 62.5, 83, 32.5, 83]) + .fillPoly([47.5, 158, 62.5, 143, 32.5, 143]) + .fillPoly([128.5, 68, 143.5, 83, 113.5, 83]) + .fillPoly([128.5, 158, 143.5, 143, 113.5, 143]); + + var txt_1 = options.format_1 ? options.format_1(v_1) : v_1; + var txt_2 = options.format_2 ? options.format_2(v_2) : v_2; + + g.setFontAlign(0, 0) + .setFontVector(Math.min(30, (R.w - 110) * 100 / g.setFontVector(100).stringWidth(txt_1))) + .drawString(txt_1, 47.5, 113) + .setFontVector(Math.min(30, (R.w - 110) * 100 / g.setFontVector(100).stringWidth(txt_2))) + .drawString(txt_2, 128.5, 113) + .setFontVector(30) + .drawString(options.separator ?? "", 88, 110); + } + function cb(dir, x_part) { + if (dir) { + if (x_part == -1) { + v_1 -= (dir || 1) * (options.step_1 || 1); + if (options.min_1 !== undefined && v_1 < options.min_1) v_1 = options.wrap_1 ? options.max_1 : options.min_1; + if (options.max_1 !== undefined && v_1 > options.max_1) v_1 = options.wrap_1 ? options.min_1 : options.max_1; + } else { + v_2 -= (dir || 1) * (options.step_2 || 1); + if (options.min_2 !== undefined && v_2 < options.min_2) v_2 = options.wrap_2 ? options.max_2 : options.min_2; + if (options.max_2 !== undefined && v_2 > options.max_2) v_2 = options.wrap_2 ? options.min_2 : options.max_2; + } + draw(); + } else { // actually selected + options.value_1 = v_1; + options.value_2 = v_2; + if (options.onchange) options.onchange(options.value_1, options.value_2); + options.back(); // redraw original menu + } + } + + draw(); + var dy = 0; + + Bangle.setUI({ + mode: "custom", + back: options.back, + remove: options.remove, + redraw: draw, + drag: e => { + dy += e.dy; // after a certain amount of dragging up/down fire cb + if (!e.b) dy = 0; + var x_part; + if (e.x <= 88) { + x_part = -1; + } else { + x_part = 1; + } + while (Math.abs(dy) > 32) { + if (dy > 0) { dy -= 32; cb(1, x_part); } + else { dy += 32; cb(-1, x_part); } + Bangle.buzz(20); + } + }, + touch: (_, e) => { + Bangle.buzz(20); + var x_part; + if (e.x <= 88) { + x_part = -1; + } else { + x_part = 1; + } + if (e.y < 82) cb(-1, x_part); // top third + else if (e.y > 142) cb(1, x_part); // bottom third + else cb(); // middle = accept + } + }); +} + +exports.triplePicker = function (options) { + var menuIcon = "\0\f\f\x81\0\xFF\xFF\xFF\0\0\0\0\x0F\xFF\xFF\xF0\0\0\0\0\xFF\xFF\xFF"; + + var R = Bangle.appRect; + g.reset().clearRect(R); + g.setFont("12x20").setFontAlign(0, 0).drawString(menuIcon + " " + options.title, R.x + R.w / 2, R.y + 12); + + var v_1 = options.value_1; + var v_2 = options.value_2; + var v_3 = options.value_3; + + function draw() { + g.setColor(g.theme.bg2) + .fillRect(8, 60, 56, 166) + .fillRect(64, 60, 112, 166) + .fillRect(120, 60, 168, 166); + + g.setColor(g.theme.fg2) + .fillPoly([32, 68, 47, 83, 17, 83]) + .fillPoly([32, 158, 47, 143, 17, 143]) + .fillPoly([88, 68, 103, 83, 73, 83]) + .fillPoly([88, 158, 103, 143, 73, 143]) + .fillPoly([144, 68, 159, 83, 129, 83]) + .fillPoly([144, 158, 159, 143, 129, 143]); + + var txt_1 = options.format_1 ? options.format_1(v_1) : v_1; + var txt_2 = options.format_2 ? options.format_2(v_2) : v_2; + var txt_3 = options.format_3 ? options.format_3(v_3) : v_3; + + g.setFontAlign(0, 0) + .setFontVector(Math.min(30, (R.w - 130) * 100 / g.setFontVector(100).stringWidth(txt_1))) + .drawString(txt_1, 32, 113) + .setFontVector(Math.min(30, (R.w - 130) * 100 / g.setFontVector(100).stringWidth(txt_2))) + .drawString(txt_2, 88, 113) + .setFontVector(Math.min(30, (R.w - 130) * 100 / g.setFontVector(100).stringWidth(txt_3))) + .drawString(txt_3, 144, 113) + .setFontVector(30) + .drawString(options.separator_1 ?? "", 60, 113) + .drawString(options.separator_2 ?? "", 116, 113); + } + function cb(dir, x_part) { + if (dir) { + if (x_part == -1) { + v_1 -= (dir || 1) * (options.step_1 || 1); + if (options.min_1 !== undefined && v_1 < options.min_1) v_1 = options.wrap_1 ? options.max_1 : options.min_1; + if (options.max_1 !== undefined && v_1 > options.max_1) v_1 = options.wrap_1 ? options.min_1 : options.max_1; + } else if (x_part == 0) { + v_2 -= (dir || 1) * (options.step_2 || 1); + if (options.min_2 !== undefined && v_2 < options.min_2) v_2 = options.wrap_2 ? options.max_2 : options.min_3; + if (options.max_2 !== undefined && v_2 > options.max_2) v_2 = options.wrap_2 ? options.min_2 : options.max_3; + } else { + v_3 -= (dir || 1) * (options.step_3 || 1); + if (options.min_3 !== undefined && v_3 < options.min_3) v_3 = options.wrap_3 ? options.max_3 : options.min_3; + if (options.max_3 !== undefined && v_3 > options.max_3) v_3 = options.wrap_3 ? options.min_3 : options.max_3; + } + draw(); + } else { // actually selected + options.value_1 = v_1; + options.value_2 = v_2; + options.value_3 = v_3; + if (options.onchange) options.onchange(options.value_1, options.value_2, options.value_3); + options.back(); // redraw original menu + } + } + + draw(); + var dy = 0; + + Bangle.setUI({ + mode: "custom", + back: options.back, + remove: options.remove, + redraw: draw, + drag: e => { + dy += e.dy; // after a certain amount of dragging up/down fire cb + if (!e.b) dy = 0; + var x_part; + if (e.x <= 58) { + x_part = -1; + } else if (58 < e.x && e.x <= 116) { + x_part = 0; + } else { + x_part = 1; + } + while (Math.abs(dy) > 32) { + if (dy > 0) { dy -= 32; cb(1, x_part); } + else { dy += 32; cb(-1, x_part); } + Bangle.buzz(20); + } + }, + touch: (_, e) => { + Bangle.buzz(20); + var x_part; + if (e.x <= 58) { + x_part = -1; + } else if (58 < e.x && e.x <= 116) { + x_part = 0; + } else { + x_part = 1; + } + if (e.y < 82) cb(-1, x_part); // top third + else if (e.y > 142) cb(1, x_part); // bottom third + else cb(); // middle = accept + } + }); +} \ No newline at end of file diff --git a/modules/more_pickers.md b/modules/more_pickers.md new file mode 100644 index 000000000..1d37af44e --- /dev/null +++ b/modules/more_pickers.md @@ -0,0 +1,93 @@ +# More pickers + +This library provides a double picker and a triple picker, similar to the stock picker. + +# How to use +**Important:** you need to define a `back` handler that will be called to go back to the previous screen when the user confirms the input or clicks on the back button. + +It is possible to define an optionnal custom separator between the values. See examples below. + +## Double picker + +Example: + +```javascript +// example of a formatting function +function pad2(number) { + return (String(number).padStart(2, '0')); +} + +var hours = 10; +var minutes = 32; + +function showMainMenu() { + E.showMenu({ + 'Time': function () { + require("more_pickers").doublePicker({ + back: showMainMenu, + title: "Time", + separator: ":", + + value_1: hours, + min_1: 0, max_1: 23, step_1: 1, wrap_1: true, + + value_2: minutes, + min_2: 0, max_2: 59, step_2: 1, wrap_2: true, + + format_1: function (v_1) { return (pad2(v_1)); }, + format_2: function (v_2) { return (pad2(v_2)); }, + onchange: function (v_1, v_2) { hours = v_1; minutes = v_2; } + }); + } + }); +} + +Bangle.loadWidgets(); +Bangle.drawWidgets(); +showMainMenu(); +``` + + +## Triple picker + +Example: + +```javascript +// example of a formatting function +function pad2(number) { + return (String(number).padStart(2, '0')); +} + +var day = 21; +var month = 5; +var year = 2021; + +function showMainMenu() { + E.showMenu({ + 'Date': function () { + require("more_pickers").triplePicker({ + back: showMainMenu, + title: "Date", + separator_1: "/", + separator_2: "/", + + value_1: day, + min_1: 1, max_1: 31, step_1: 1, wrap_1: true, + + value_2: month, + min_2: 1, max_2: 12, step_2: 1, wrap_2: true, + + value_3: year, + min_3: 2000, max_3: 2050, step_3: 1, wrap_3: false, + + format_1: function (v_1) { return (pad2(v_1)); }, + format_2: function (v_2) { return (pad2(v_2)); }, + onchange: function (v_1, v_2, v_3) { day = v_1; month = v_2; year = v_3; } + }); + } + }); +} + +Bangle.loadWidgets(); +Bangle.drawWidgets(); +showMainMenu(); \ No newline at end of file