From 34c35532ed17a7d30251ddf6c54d58a2a5546a28 Mon Sep 17 00:00:00 2001 From: Ian Ward Date: Thu, 31 Oct 2024 15:21:17 +0000 Subject: [PATCH] Accented characters and extended mode --- apps/kbedgewrite/ChangeLog | 1 + apps/kbedgewrite/README.md | 11 +- apps/kbedgewrite/characterset.json | 78 +++++++++++- apps/kbedgewrite/lib.js | 196 ++++++++++++++++++++++++----- apps/kbedgewrite/metadata.json | 9 +- apps/kbedgewrite/screenshot.png | Bin 0 -> 2878 bytes apps/kbedgewrite/settings.js | 40 ++++++ 7 files changed, 300 insertions(+), 35 deletions(-) create mode 100644 apps/kbedgewrite/screenshot.png create mode 100644 apps/kbedgewrite/settings.js diff --git a/apps/kbedgewrite/ChangeLog b/apps/kbedgewrite/ChangeLog index 5560f00bc..e6dd4b19e 100644 --- a/apps/kbedgewrite/ChangeLog +++ b/apps/kbedgewrite/ChangeLog @@ -1 +1,2 @@ 0.01: New App! +0.02: Accents and extended mode characters diff --git a/apps/kbedgewrite/README.md b/apps/kbedgewrite/README.md index 8d0ca5dc5..2f668d4cf 100644 --- a/apps/kbedgewrite/README.md +++ b/apps/kbedgewrite/README.md @@ -12,7 +12,14 @@ To display the in app character chart, long press the screen; you can scroll thr For a full character chart see [EwChart.pdf](EwChart.pdf) -**Supported:** Letters (including capitals), numbers, backspace, word backspace, space, punctuation, new line, and some cursor controls (left, right, word left/right, home, end). +**Supported:** Letters (including capitals), numbers, backspace, word backspace, space, punctuation, new line, accents, extended mode (if characters are supported by the vector font), and some cursor controls (left, right, word left/right, home, end). -**Unsupported:** Extended mode, accents, and word-level stroking. +**Unsupported:** Word-level stroking. +## Settings + +Font size can be selected in Settings app > "Apps" > "EdgeWrite Keyboard" + +## Author + +Woogal [github](https://github.com/retcurve) diff --git a/apps/kbedgewrite/characterset.json b/apps/kbedgewrite/characterset.json index 74276b683..9aa70ae71 100644 --- a/apps/kbedgewrite/characterset.json +++ b/apps/kbedgewrite/characterset.json @@ -193,17 +193,93 @@ "2425": "`", "31": " \n", "24": " ", + "46": "\xb7", + "432146": "\xb0", + "412346": "\xb0", + "123246": "\xae", + "2123246": "\xae", + "123146": "\xae", + "2123146": "\xae", + "2346": "\xac", + "32146": "\xa9", + "41236": "\xa2", + "24316": "\xd7", + "31246": "\xd7", + "316": "\xf7", + "136": "\xf7", + "232146": "\x80", + "23246": "\x80", + "132146": "\x80", + "412316": "\x80", + "323146": "\x80", + "324146": "\x80", + "24346": "\xa5", + "243416": "\xa5", + "2143416": "\xa5", + "34146": "\xf0", + "342146": "\xf0", + "343146": "\xf0", + "4214346": "\xf0", + "414346": "\xf0", + "434146": "\xf0", + "123416": "\xf0", + "2123416": "\xf0", + "1346": "\xe6", + "1246": "\xe6", + "13416": "\xe6", + "12416": "\xe6", + "3214346": "\xe6", + "21416": "\xdf", + "213416": "\xdf", + "212416": "\xdf", + "141216": "\xdf", + "1341216": "\xdf", + "121416": "\xdf", + "1232416": "\xdf", + "1231416": "\xdf", + "21232416": "\xdf", + "21231416": "\xdf", + "2321416": "\xdf", + "2146": "\xa3", + "21436": "\xb5", + "214346": "\xb5", + "121436": "\xb5", + "1214346": "\xb5", + "3214316": "\xf8", + "3412316": "\xf8", + "4126": "\xbf", + "216": "\xa1", + "346": "\xa6", + "21236": "\xb1", + "212326": "\xb1", + "31426": "\xa4", + "24136": "\xa4", + "3146": "\xab", + "2416": "\xbb", "32": "#bs", "41": "#wbs", "12": "#pu-on", "43": "#pu-on", "325": "#pu-off", "415": "#pu-off", + "42": "#ex-on", + "326": "#ex-off", + "416": "#ex-off", "323": "#cur-left", "232": "#cur-right", "414": "#cur-word-left", "141": "#cur-word-right", "4141": "#cur-home", - "1414": "#cur-end" + "1414": "#cur-end", + "242": "#grave", + "313": "#acute", + "431": "#circumflex", + "421": "#circumflex", + "3421": "#tilde", + "43412": "#umlaut", + "43214": "#ring", + "41234": "#ring", + "142": "#cedilla", + "143": "#cedilla" } diff --git a/apps/kbedgewrite/lib.js b/apps/kbedgewrite/lib.js index a69eabc2c..d71ad5c73 100644 --- a/apps/kbedgewrite/lib.js +++ b/apps/kbedgewrite/lib.js @@ -7,10 +7,20 @@ exports.input = function(options) { let chartX = 0; let chartY = 0; + let settings = Object.assign({ + fontSize: 32, + }, require('Storage').readJSON("kbedgewrite.json", true)); + let shouldShowWidgetBar = Bangle.appRect.y > 0; options = options||{}; let text = options.text; + // Substring doesn't play well with UTF8 + if (E.isUTF8(text)) { + text = E.decodeUTF8(text); + } + let wrappedText = ''; + if ('string' != typeof text) text=''; // Colours for number of corner occurrences @@ -18,40 +28,140 @@ exports.input = function(options) { const cornerSize = g.getWidth() / 3; let punctuationMode = false; + let extendedMode = false; let path = ''; let cursorPos = text.length; let chartShown = false; let characterSet = Object.assign({}, require('Storage').readJSON('kbedgewrite.charset.json', true) || {}); - function draw() { - g.clearRect(Bangle.appRect).setClipRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y2); + const accentedCharacters = { + '#grave': { + 'a': String.fromCharCode(0xE0), + 'A': String.fromCharCode(0xC0), + 'e': String.fromCharCode(0xE8), + 'E': String.fromCharCode(0xC8), + 'i': String.fromCharCode(0xEC), + 'I': String.fromCharCode(0xCC), + 'o': String.fromCharCode(0xF2), + 'O': String.fromCharCode(0xD2), + 'u': String.fromCharCode(0xF9), + 'U': String.fromCharCode(0xD9) + }, + '#acute': { + 'a': String.fromCharCode(0xE1), + 'A': String.fromCharCode(0xC1), + 'e': String.fromCharCode(0xE9), + 'E': String.fromCharCode(0xC9), + 'i': String.fromCharCode(0xED), + 'I': String.fromCharCode(0xCD), + 'o': String.fromCharCode(0xF3), + 'O': String.fromCharCode(0xD3), + 'u': String.fromCharCode(0xFA), + 'U': String.fromCharCode(0xDA), + 'y': String.fromCharCode(0xFD), + 'Y': String.fromCharCode(0xDD) + }, + '#circumflex': { + 'a': String.fromCharCode(0xE2), + 'A': String.fromCharCode(0xC2), + 'e': String.fromCharCode(0xEA), + 'E': String.fromCharCode(0xCA), + 'i': String.fromCharCode(0xEE), + 'I': String.fromCharCode(0xCE), + 'o': String.fromCharCode(0xF4), + 'O': String.fromCharCode(0xD4), + 'u': String.fromCharCode(0xFB), + 'U': String.fromCharCode(0xDB) + }, + '#umlaut': { + 'a': String.fromCharCode(0xE4), + 'A': String.fromCharCode(0xC4), + 'e': String.fromCharCode(0xEB), + 'E': String.fromCharCode(0xCB), + 'i': String.fromCharCode(0xEF), + 'I': String.fromCharCode(0xCF), + 'o': String.fromCharCode(0xF6), + 'O': String.fromCharCode(0xD6), + 'u': String.fromCharCode(0xFC), + 'U': String.fromCharCode(0xDC), + 'y': String.fromCharCode(0xFF) + }, + '#tilde': { + 'a': String.fromCharCode(0xE3), + 'A': String.fromCharCode(0xC3), + 'n': String.fromCharCode(0xF1), + 'N': String.fromCharCode(0xD1), + 'o': String.fromCharCode(0xF5), + 'O': String.fromCharCode(0xD5) + }, + '#ring': { + 'a': String.fromCharCode(0xE5), + 'A': String.fromCharCode(0xC5) + }, + '#cedilla': { + 'c': String.fromCharCode(0xE7), + 'C': String.fromCharCode(0xC7) + }, - // Draw the text string - let l = g.setFont('6x8:4').wrapString(text.substring(0, cursorPos) + '_' + text.substring(cursorPos), g.getWidth()); - if (!l) l = []; - if (l.length>5) { + }; + + function wrapText() { + let stringToWrap = text.substring(0, cursorPos) + '_' + text.substring(cursorPos); + let l = []; + let startPos = 0; + + g.setFont("Vector", settings.fontSize); // set the font so we can calculate a string width + + // Wrap the string into array of lines that will fit the screen width + for (let i = 0; i < stringToWrap.length; i++) { + // wrap if string is too long or we hit a line break + if (stringToWrap.charCodeAt(i) == 10 || g.stringWidth(stringToWrap.substring(startPos, i+1)) > 176) { + l.push(stringToWrap.substring(startPos, i)); + // skip the line break + if (stringToWrap.charCodeAt(i) == 10) { + i++; + } + startPos = i; + } + } + // Add the final line + l.push(stringToWrap.substring(startPos)); + + // Number of lines that can fit on the screen + let numLines = Math.floor(g.getHeight() / g.getFontHeight()); + + // If too many lines, reposition so the cursor can be seen + if (l.length > numLines) { let textPos = 0; let lineNum; for (lineNum = 0; lineNum < l.length; lineNum++) { - textPos = textPos + l[lineNum].length - 1; + textPos = textPos + l[lineNum].length; if (textPos >= cursorPos) break; } - l=l.slice(lineNum - l.length - 5); + l=l.slice(lineNum - l.length - numLines + 1); } - g.setColor(g.theme.fg); - g.setFontAlign(-1, -1, 0); - g.drawString(l.join('\n'), Bangle.appRect.x, Bangle.appRect.y); - // Draw punctuation flag - if (punctuationMode > 0) { + wrappedText = l.join('\n'); + } + + function draw() { + g.clearRect(Bangle.appRect).setClipRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y2); + + g.setColor(g.theme.fg); + g.setFont("Vector", settings.fontSize); + g.setFontAlign(-1, -1, 0); + g.drawString(wrappedText, Bangle.appRect.x, Bangle.appRect.y); + + // Draw punctuation or extended flags + if (punctuationMode || extendedMode) { let x = (g.getWidth() / 2) - 12; let y = g.getHeight() - 32; - g.setColor('#F00'); + g.setColor(punctuationMode ? '#F00' : '#0F0'); g.fillRect(x,y,x+24,y+32); g.setColor('#FFF'); g.setFont('6x8:4'); - g.drawString('P', x+4, y+4, false); + g.drawString(punctuationMode ? 'P' : 'E', x+4, y+4, false); } // Draw corners @@ -69,30 +179,27 @@ exports.input = function(options) { } function processPath() { - let capital = false; - // Punctuation paths end in 5 if (punctuationMode) { path = path + '5'; } - - // Capital letters end in 2, remove that and set a capital flag - // but only if the path isn't 232 (cursor right) - if (path != '232' && path.length > 2 && path.slice(-1) == '2') { - path = path.slice(0,-1); - capital = true; + // Extended paths end in 6 + if (extendedMode) { + path = path + '6'; } // Find character from path let char = characterSet[path]; - // Handle capitals - if (capital && char != 'undefined') { - if (char.charCodeAt(0)>96 && char.charCodeAt(0)<123) { - char = char.toUpperCase(); - } else { - // Anything that can't be capitalised is an invalid path - char = undefined; + // Unknown character, but ends in a 2 so may be a capital letter + if (char == 'undefined' && path.slice(-1) == '2') { + // Remove the 2 and look for a letter + char = characterSet[path.slice(0,-1)]; + // Handle capitals + if (char != 'undefined') { + if (char.charCodeAt(0)>96 && char.charCodeAt(0)<123) { + char = char.toUpperCase(); + } } } @@ -129,6 +236,17 @@ exports.input = function(options) { punctuationMode = false; break; } + // Enable extended mode + case '#ex-on': { + extendedMode = true; + break; + } + // Disable extended mode + case '#ex-off': { + extendedMode = false; + break; + } + // Cursor controls case '#cur-left': { if (cursorPos > 0) { cursorPos--; @@ -168,6 +286,22 @@ exports.input = function(options) { cursorPos = text.length; break; } + // Accents + case '#grave': + case '#acute': + case '#circumflex': + case '#umlaut': + case '#tilde': + case '#ring': + case '#cedilla': + // If the previous character can be accented, replace it with the accented version + if (cursorPos > 0) { + char = accentedCharacters[char][text.substring(cursorPos-1, cursorPos)]; + if (char != 'undefined') { + text = text.substring(0, cursorPos-1) + char + text.substring(cursorPos); + } + } + break; // Append character default: { text = text.substring(0, cursorPos) + char + text.substring(cursorPos); @@ -184,6 +318,7 @@ exports.input = function(options) { if (!chartShown) { if (e.b == 0) { // Finger lifted, process completed path processPath(); + wrapText(); draw(); } else { let corner = 0; @@ -220,6 +355,7 @@ exports.input = function(options) { // Draw initial string require("widget_utils").hide(); g.setBgColor(g.theme.bg); + wrapText(); draw(); return new Promise((resolve,reject) => { diff --git a/apps/kbedgewrite/metadata.json b/apps/kbedgewrite/metadata.json index cbf873a09..717cdbcba 100644 --- a/apps/kbedgewrite/metadata.json +++ b/apps/kbedgewrite/metadata.json @@ -1,14 +1,19 @@ { "id": "kbedgewrite", "name": "EdgeWrite keyboard", - "version":"0.01", + "version":"0.02", "description": "A library for text input via EdgeWrite swipe gestures", "icon": "app.png", "type":"textinput", "tags": "keyboard", "supports" : ["BANGLEJS2"], "readme": "README.md", + "screenshots" : [ { "url":"screenshot.png" } ], "storage": [ {"name":"textinput","url":"lib.js"}, - {"name":"kbedgewrite.charset.json","url":"characterset.json"} + {"name":"kbedgewrite.charset.json","url":"characterset.json"}, + {"name":"kbedgewrite.settings.js","url":"settings.js"} + ], + "data": [ + {"name":"kbedgewrite.json"} ] } diff --git a/apps/kbedgewrite/screenshot.png b/apps/kbedgewrite/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..48db615e10c08dd8d4e5798abf5ea3e7e694c013 GIT binary patch literal 2878 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 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,settings[name], values, labels); + } + + // Show the menu + E.showMenu({ + '' : { 'title' : 'EdgeWrite' }, + '< Back' : () => back(), + 'Font Size': stringInSettings('fontSize', [24, 32, 48], ['Small', 'Medium', 'Large']) + }); +}) \ No newline at end of file