feat(appstore): cache catalogs on the SD card
Select opens the cached catalog when its sidecar URL still matches, so browsing costs no network or TLS memory; Refresh fetches. Caches are replaced through a backup so an interrupted download cannot leave an empty catalog behind. Switch to structured log.debug/info/error milestones and add BinaryKeyboard, a two-button recursive text entry experiment.
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
-- DESCRIPTION: Two-button recursive keyboard experiment
|
||||
|
||||
local KEYS = {
|
||||
"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 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
|
||||
|
||||
local function drawPanel(x, y, width, height, title, values)
|
||||
gui.drawRoundedRect(x, y, width, height, 2, 8, COLOR_BLACK)
|
||||
gui.drawText(FONT_UI_10, x + 12, y + 32, title, COLOR_BLACK, STYLE_BOLD)
|
||||
gui.drawText(FONT_UI_12, x + 12, y + math.floor(height / 2), table.concat(values), COLOR_BLACK, STYLE_NORMAL)
|
||||
end
|
||||
|
||||
local function render()
|
||||
local width = gui.width()
|
||||
local height = gui.height()
|
||||
local margin = 20
|
||||
local gap = 12
|
||||
local panelWidth = math.floor((width - margin * 2 - gap) / 2)
|
||||
local panelTop = 185
|
||||
local panelHeight = math.min(220, height - panelTop - 100)
|
||||
local leftHalf, rightHalf = halves()
|
||||
|
||||
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)
|
||||
|
||||
local progress = string.rep("•", #history)
|
||||
gui.drawCenteredText(FONT_UI_10, 155, progress == "" and "Choose a half" or progress, COLOR_BLACK, STYLE_NORMAL)
|
||||
drawPanel(margin, panelTop, panelWidth, panelHeight, "LEFT SIDE", leftHalf)
|
||||
drawPanel(margin + panelWidth + gap, panelTop, panelWidth, panelHeight, "RIGHT SIDE", rightHalf)
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user