feat(ui): layout toolkit, Lua launcher and touch press events

Apps describe nesting instead of coordinates: /lib/ui.lua borrows CSS block flow,
the box model and auto sizing, and owns hit testing, press capture and the pressed
repaint. The runtime gains require backed by the SD card, on_touch_down/on_touch_up,
text metrics and rounded gradient fills, so the launcher becomes an ordinary Lua app
and the firmware keeps only a fallback screen for an unreadable card.
This commit is contained in:
2026-07-31 22:27:34 -04:00
parent ad396d3f01
commit 4e5796fd51
10 changed files with 789 additions and 148 deletions
+25 -5
View File
@@ -1,18 +1,28 @@
-- Run: lua test/settings_calibration.lua
-- Stubs the firmware bindings so the settings app loads on a desktop Lua.
package.path = "sdcard/lib/?.lua;" .. package.path
local saved
local rotation = 0
local now = 0
gui = {
color = function() return 0 end,
setRotation = function() end,
width = function() return 320 end,
height = function() return 480 end,
width = function() return rotation % 180 == 0 and 320 or 480 end,
height = function() return rotation % 180 == 0 and 480 or 320 end,
clear = function() end,
fillRect = function() end,
drawText = function() end,
drawRect = function() end,
drawLine = function() end,
fillRoundRect = function() end,
drawRoundRect = function() end,
fontHeight = function() return 8 end,
textWidth = function(text) return #text * 6 end,
setRotation = function() end,
}
sys = {
millis = function() return now end,
exit = function() end,
setCalibration = function(...) saved = {...} return true end,
getRotation = function() return rotation end,
@@ -23,6 +33,15 @@ log = {info = function() end}
dofile("sdcard/apps/settings/main.lua")
-- Menu rows: pad 12, gap 8, title 8 tall, buttons 24 tall.
local ROTATION_ROW = {x = 100, y = 70}
local CALIBRATE_ROW = {x = 100, y = 38}
local function tap(point)
on_touch_down(point.x, point.y)
on_touch_up(point.x, point.y)
end
-- A perfectly linear panel spanning raw 200..3800 over 320x480 must round-trip
-- to those same extremes from the two inset samples.
local inset, w, h = 30, 320, 480
@@ -45,12 +64,13 @@ assert(fx0 > fx1, "flipped axis should descend")
-- Rotation cycles through the four quarter turns and wraps back to 0.
setup()
for _, expected in ipairs({90, 180, 270, 0}) do
on_touch(20, 85)
tap(ROTATION_ROW)
assert(sys.getRotation() == expected, "rotation " .. sys.getRotation())
end
-- Calibration collects one sample per target and saves on the second release.
setup()
on_touch(20, 45)
tap(CALIBRATE_ROW)
on_tick() -- release after the menu tap arms sampling
input.getRawTouch = function() return s1.x, s1.y end
on_tick()