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 000000000..48db615e1 Binary files /dev/null and b/apps/kbedgewrite/screenshot.png differ diff --git a/apps/kbedgewrite/settings.js b/apps/kbedgewrite/settings.js new file mode 100644 index 000000000..d6cc851e6 --- /dev/null +++ b/apps/kbedgewrite/settings.js @@ -0,0 +1,40 @@ +(function(back) { + var FILE = 'kbedgewrite.json'; + + // Load settings + var settings = Object.assign({ + fontSize: 32 + }, require('Storage').readJSON(FILE, true) || {}); + + function setSetting(key,value) { + settings[key] = value; + require('Storage').writeJSON(FILE, settings); + } + + // 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,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