From 80a7db488ac82fdf67a2d30fc3c9bf2709612c17 Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Sat, 2 Nov 2019 14:50:05 +0100 Subject: render: Add AnySoftKeyboard layout file renderer --- lulua/render.py | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/lulua/render.py b/lulua/render.py index acd15ab..be3bf68 100644 --- a/lulua/render.py +++ b/lulua/render.py @@ -18,17 +18,19 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -import argparse, sys, logging, pkg_resources, base64 +import argparse, sys, logging, pkg_resources, base64, unicodedata from collections import namedtuple, defaultdict +from itertools import chain from operator import attrgetter from datetime import datetime +from xml.etree import ElementTree import svgwrite import yaml from .layout import LITTLE, RING, MIDDLE, INDEX, THUMB, GenericLayout, defaultLayouts from .writer import Writer -from .keyboard import defaultKeyboards +from .keyboard import defaultKeyboards, LetterButton from .util import first, displayText RendererSettings = namedtuple ('RendererSetting', ['buttonMargin', 'middleGap', 'buttonWidth', 'rounded', 'shadowOffset', 'markerStroke']) @@ -356,6 +358,74 @@ def renderKeyman (args): text = ' '.join ([f'U+{ord (x):04X}' for x in text]) fd.write (f'+ [{comb}] > {text}\n') +def renderAsk (args): + """ + Render keyboard to Anysoft Keyboard XML layout file + + Can be packaged/used by including them into a language pack: + https://github.com/AnySoftKeyboard/LanguagePack + + Put the resulting file into + languages//pack/src/main/res/xml/.xml and edit the project + accordingly. + """ + keyboard = defaultKeyboards[args.keyboard] + layout = defaultLayouts[args.layout].specialize (keyboard) + + ET = ElementTree + namespaces = { + 'xmlns:android': 'http://schemas.android.com/apk/res/android', + 'xmlns:ask': 'http://schemas.android.com/apk/res-auto', + } + kbdelem = ET.Element ('Keyboard', attrib=namespaces) + for l, r in keyboard: + # exclude special buttons + buttons = list (filter (lambda x: isinstance (x, LetterButton) and not layout.isModifier (frozenset ([x])), chain (l, r))) + if not buttons: + continue + + i = keyboard.getRow (buttons[0]) + attrib = {'android:keyWidth': f'{100/len (buttons):.2f}%p'} + if i == len (keyboard.rows)-1: + # ignore the bottom row (mostly control characters), generic row is provided below + continue + elif i == 0: + # special top row + attrib['android:rowEdgeFlags'] = 'top' + attrib['android:keyHeight'] = '@integer/key_short_height' + rowelem = ET.SubElement (kbdelem, 'Row', attrib=attrib) + + for btn in buttons: + # android cannot process multiple characters per button, thus find + # a composed version (NFC) if possible + buttonText = [unicodedata.normalize ('NFC', x) if x is not None else '' for x in layout.getButtonText (btn)] + for t in buttonText: + if len (t) > 1: + logging.info (f'button {btn} has text with len>1 {t}, verify output') + attrib = { + 'android:codes': ','.join (map (lambda x: str (ord (x)), buttonText[0])), + 'android:keyLabel': buttonText[0], + 'android:popupCharacters': ''.join (buttonText[1:]), + # limit the number of hint characters shown + 'ask:hintLabel': ''.join (buttonText[1:3]), + } + keyelem = ET.SubElement (rowelem, 'Key', attrib=attrib) + + # add generic bottom row + rowelem = ET.SubElement (kbdelem, 'Row', {'android:rowEdgeFlags': 'bottom', 'android:keyHeight': '@integer/key_normal_height'}) + for attrib in [ + # return + {'ask:isFunctional': 'true', 'android:keyWidth': '25%p', 'android:codes': '10', 'android:keyEdgeFlags': 'left'}, + # space + {'ask:isFunctional': 'true', 'android:keyWidth': '50%p', 'android:codes': '32'}, + # backspace + {'ask:isFunctional': 'true', 'android:keyWidth': '25%p', 'android:codes': '-5', 'android:keyEdgeFlags': 'right', 'android:isRepeatable': 'true'}, + ]: + ET.SubElement (rowelem, 'Key', ) + + tree = ET.ElementTree (kbdelem) + tree.write (args.output, encoding='utf-8', xml_declaration=True) + def yamlload (s): try: with open (s) as fd: @@ -386,6 +456,8 @@ def render (): sp.set_defaults (func=renderXmodmap) sp = subparsers.add_parser('keyman') sp.set_defaults (func=renderKeyman) + sp = subparsers.add_parser('ask') + sp.set_defaults (func=renderAsk) parser.add_argument('output', metavar='FILE', help='Output file') logging.basicConfig (level=logging.INFO) -- cgit v1.2.3