152 lines
4.6 KiB
Lua
152 lines
4.6 KiB
Lua
-- DESCRIPTION: Two-button recursive keyboard experiment
|
|
|
|
local ROWS = {
|
|
{ "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P" },
|
|
{ "A", "S", "D", "F", "G", "H", "J", "K", "L" },
|
|
{ "Z", "X", "C", "V", "B", "N", "M" }
|
|
}
|
|
|
|
local KEYS = {}
|
|
for _, row in ipairs(ROWS) do
|
|
for _, key in ipairs(row) do KEYS[#KEYS + 1] = key end
|
|
end
|
|
local MAX_TEXT_BYTES = 80
|
|
|
|
local text = ""
|
|
local candidates = {}
|
|
local history = {}
|
|
local uppercase = false
|
|
local needsDraw = true
|
|
local firstDraw = true
|
|
|
|
local function copyKeys(source, first, last)
|
|
local result = {}
|
|
for index = first, last do result[#result + 1] = source[index] end
|
|
return result
|
|
end
|
|
|
|
local function resetCandidates()
|
|
candidates = copyKeys(KEYS, 1, #KEYS)
|
|
history = {}
|
|
end
|
|
|
|
local function halves()
|
|
local midpoint = math.ceil(#candidates / 2)
|
|
return copyKeys(candidates, 1, midpoint), copyKeys(candidates, midpoint + 1, #candidates)
|
|
end
|
|
|
|
local function append(value)
|
|
if #text + #value <= MAX_TEXT_BYTES then text = text .. value end
|
|
end
|
|
|
|
local function chooseHalf(left)
|
|
local leftHalf, rightHalf = halves()
|
|
history[#history + 1] = candidates
|
|
candidates = left and leftHalf or rightHalf
|
|
|
|
if #candidates == 1 then
|
|
local letter = candidates[1]
|
|
append(uppercase and letter or letter:lower())
|
|
resetCandidates()
|
|
end
|
|
needsDraw = true
|
|
end
|
|
|
|
local function undo()
|
|
if #history == 0 then
|
|
sys.exit()
|
|
return
|
|
end
|
|
candidates = history[#history]
|
|
history[#history] = nil
|
|
needsDraw = true
|
|
end
|
|
|
|
-- Side Lookup - Rendering needs per-key shading, but candidates are stored in layout order.
|
|
local function sides()
|
|
local leftHalf, rightHalf = halves()
|
|
local result = {}
|
|
for _, key in ipairs(leftHalf) do result[key] = "left" end
|
|
for _, key in ipairs(rightHalf) do result[key] = "right" end
|
|
return result
|
|
end
|
|
|
|
local function drawKeyboard(width, top, bottom)
|
|
local gap = 6
|
|
local margin = 16
|
|
local keyWidth = math.floor((width - margin * 2 - gap * 9) / 10)
|
|
local keyHeight = math.min(56, math.floor((bottom - top - gap * 2) / 3))
|
|
local side = sides()
|
|
|
|
for rowIndex, row in ipairs(ROWS) do
|
|
local rowWidth = #row * keyWidth + (#row - 1) * gap
|
|
local startX = math.floor((width - rowWidth) / 2)
|
|
local y = top + (rowIndex - 1) * (keyHeight + gap)
|
|
|
|
for columnIndex, key in ipairs(row) do
|
|
local keySide = side[key]
|
|
if keySide then
|
|
local x = startX + (columnIndex - 1) * (keyWidth + gap)
|
|
local shaded = keySide == "right"
|
|
gui.fillRoundedRect(x, y, keyWidth, keyHeight, 4, shaded and COLOR_DARK_GRAY or COLOR_WHITE)
|
|
gui.drawRoundedRect(x, y, keyWidth, keyHeight, 1, 4, COLOR_BLACK)
|
|
local label = uppercase and key or key:lower()
|
|
local textX = x + math.floor((keyWidth - gui.getTextWidth(FONT_NOTOSANS_16, label, STYLE_BOLD)) / 2)
|
|
gui.drawText(FONT_NOTOSANS_16, textX, y + math.floor(keyHeight / 2) - 10, label,
|
|
shaded and COLOR_WHITE or COLOR_BLACK, STYLE_BOLD)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function render()
|
|
local width = gui.width()
|
|
local height = gui.height()
|
|
local margin = 20
|
|
|
|
gui.clear()
|
|
gui.drawCenteredText(FONT_UI_12, 24, "Binary Keyboard", COLOR_BLACK, STYLE_BOLD)
|
|
gui.drawRoundedRect(margin, 65, width - margin * 2, 82, 2, 8, COLOR_BLACK)
|
|
local visibleText = text == "" and "Type with the side buttons" or text:sub(-38)
|
|
gui.drawText(FONT_UI_12, margin + 12, 96, visibleText, COLOR_BLACK, STYLE_NORMAL)
|
|
|
|
gui.drawCenteredText(FONT_UI_10, 170, "UP = plain DOWN = shaded", COLOR_BLACK, STYLE_NORMAL)
|
|
drawKeyboard(width, 195, height - 90)
|
|
|
|
gui.drawButtonHints(#history == 0 and "Exit" or "Undo", "Space", "Delete", uppercase and "lower" or "UPPER")
|
|
gui.refresh(firstDraw and REFRESH_HALF or REFRESH_FAST)
|
|
firstDraw = false
|
|
needsDraw = false
|
|
end
|
|
|
|
function init()
|
|
text = ""
|
|
uppercase = false
|
|
firstDraw = true
|
|
needsDraw = true
|
|
resetCandidates()
|
|
end
|
|
|
|
function draw()
|
|
if input.wasPressed("up") then
|
|
chooseHalf(true)
|
|
elseif input.wasPressed("down") then
|
|
chooseHalf(false)
|
|
elseif input.wasPressed("back") then
|
|
undo()
|
|
elseif input.wasPressed("confirm") then
|
|
append(" ")
|
|
resetCandidates()
|
|
needsDraw = true
|
|
elseif input.wasPressed("left") then
|
|
if #text > 0 then text = text:sub(1, -2) end
|
|
resetCandidates()
|
|
needsDraw = true
|
|
elseif input.wasPressed("right") then
|
|
uppercase = not uppercase
|
|
needsDraw = true
|
|
end
|
|
|
|
if needsDraw then render() end
|
|
end
|