-- Run: lua test/keyboard.lua -- The keyboard's geometry: which key covers a point, and where each key is drawn. This -- is the part that decides what a tap enters, and it is pure, so it is asserted here. -- Painting, press highlighting and the 80 ms hold need the panel and the node tree, so -- they are emulator behaviour. package.path = "sdcard/.lua/lib/?.lua;test/?.lua;" .. package.path require("fake_device").install() local keyboard = require "keyboard" -- The board as ui.screen would place it: full width inside 8px of padding. local RECT = { x = 8, y = 8, w = 304, h = 4 * 30 + 3 * 5 } local function keys(page) local found = {} keyboard.eachKey(page, RECT, function(id, label, x, y, width) found[#found + 1] = { id = id, label = label, x = x, y = y, w = width } end) return found end local function centreOf(page, label) for _, key in ipairs(keys(page)) do if key.label == label then return key.x + key.w // 2, key.y + 15 end end error("no key labelled " .. label) end local function entered(page, label) local x, y = centreOf(page, label) local id, action, char = keyboard.keyAt(page, RECT, x, y) return id, action, char end -- Rows are staggered and centred like a physical keyboard: ten keys, then nine, then -- seven between two wide keys. local lower = keys "lower" assert(lower[1].label == "q" and lower[10].label == "p", "top row is qwertyuiop") assert(lower[1].x == RECT.x + 4, "top row was not centred, x=" .. lower[1].x) assert(lower[11].label == "a" and lower[11].x == RECT.x + 19, "home row was not centred, x=" .. lower[11].x) -- Every key that gets painted must be findable by a tap at its centre, or a key exists -- that cannot be pressed. for _, page in ipairs { "lower", "upper", "numbers", "symbols" } do for _, key in ipairs(keys(page)) do local id = keyboard.keyAt(page, RECT, key.x + key.w // 2, key.y + 15) assert(id == key.id, string.format("%s key %q at %d,%d hit %s", page, key.label, key.x, key.y, tostring(id))) end end -- Gaps between keys are dead, so a tap that lands between two of them enters neither. assert( keyboard.keyAt("lower", RECT, lower[1].x + lower[1].w + 1, lower[1].y + 15) == nil, "the gap between q and w entered a key" ) assert(keyboard.keyAt("lower", RECT, lower[1].x, RECT.y + 30 + 2) == nil, "the gap between rows entered a key") assert(keyboard.keyAt("lower", RECT, lower[1].x, RECT.y - 1) == nil, "above the board hit a key") -- Characters report themselves; the rest report what they do. local _, action, char = entered("lower", "q") assert(action == "char" and char == "q", "q did not enter itself") local _, upperAction, upperChar = entered("upper", "Q") assert(upperAction == "char" and upperChar == "Q", "the upper page did not enter Q") assert(select(2, entered("lower", "shift")) == "shift", "shift key") assert(select(2, entered("lower", "<-")) == "backspace", "backspace key") assert(select(2, entered("lower", "123")) == "mode", "mode key") assert(select(2, entered("lower", "space")) == "space", "space key") assert(select(2, entered("lower", "OK")) == "submit", "submit key") -- The number page swaps shift for the symbols toggle, which is the only key whose -- meaning changes with the page it is on. assert(select(2, entered("numbers", "#+=")) == "symbols", "numbers page toggles symbols") assert(select(2, entered("symbols", "123")) == "symbols", "symbols page toggles back") assert(select(2, entered("numbers", "ABC")) == "mode", "numbers page returns to letters") -- Rows shift and shrink between pages, but a key id keeps its place, so the highlight -- drawn on press lands on the key that was pressed even after the labels change. local function positions(page) local at = {} for _, key in ipairs(keys(page)) do at[key.id] = key.x end return at end local lowerAt, upperAt = positions "lower", positions "upper" for id, x in pairs(lowerAt) do assert(upperAt[id] == x, "key " .. id .. " moved when the page changed case") end print "ok"