Files
slate32/sdcard/apps/settings/main.lua
T
evan 4e5796fd51 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.
2026-07-31 22:27:34 -04:00

110 lines
2.9 KiB
Lua

local ui = require("ui")
TICK_MS = 50
local INSET = 30
local BLACK, WHITE, RED = gui.color(0, 0, 0), gui.color(255, 255, 255), gui.color(255, 0, 0)
local ACCENT = gui.color(0, 120, 255)
local screen, message
local mode = "menu"
local samples, pending, armed = {}, nil, false
local buildMenu, startCalibration, cycleRotation
-- Two inset targets give a raw-per-pixel slope; extrapolate it to the screen edges.
-- Exposed as a global so test/settings_calibration.lua can exercise it.
function computeCalibration(s1, s2, w, h, inset)
local sx = (s2.x - s1.x) / (w - 2 * inset)
local sy = (s2.y - s1.y) / (h - 2 * inset)
return math.floor(s1.x - sx * inset), math.floor(s1.y - sy * inset),
math.floor(s2.x + sx * inset), math.floor(s2.y + sy * inset)
end
local function target(n)
-- Calibration always runs in the rotation-0 frame, so use panel geometry.
if n == 1 then return INSET, INSET end
return 320 - INSET, 480 - INSET
end
local function drawTarget(n)
local x, y = target(n)
gui.clear(WHITE)
gui.drawText("tap the cross", 10, 10, BLACK, WHITE)
gui.drawLine(x - 12, y, x + 12, y, RED)
gui.drawLine(x, y - 12, x, y + 12, RED)
end
local function finishCalibration()
local ok = sys.setCalibration(computeCalibration(samples[1], samples[2], 320, 480, INSET))
message = ok and "calibration saved" or "save failed"
mode = "menu"
gui.setRotation(sys.getRotation() / 90)
buildMenu()
end
function startCalibration()
message = nil
mode = "calibrate"
samples, pending, armed = {}, nil, false
gui.setRotation(0)
drawTarget(1)
end
function cycleRotation()
local ok = sys.setRotation((sys.getRotation() + 90) % 360)
message = ok and "rotation saved" or "save failed"
buildMenu() -- the frame changed size, so lay out again
end
function buildMenu()
local items = {
pad = 12,
gap = 8,
ui.text("settings"),
ui.button{label = "calibrate touch", on_press = startCalibration},
ui.button{label = "rotation: " .. sys.getRotation() .. " deg", on_press = cycleRotation},
ui.button{label = "exit", on_press = sys.exit},
}
if message then items[#items + 1] = ui.text(message) end
items.color, items.bg = BLACK, WHITE
items.press_bg, items.press_color = ACCENT, WHITE
screen = ui.screen(ui.box(items))
end
function setup()
buildMenu()
end
function draw()
if mode == "menu" then screen:draw() end
end
function on_touch_down(x, y)
if mode == "menu" then screen:down(x, y) end
end
function on_touch_up(x, y)
if mode == "menu" then screen:up(x, y) end
end
function on_tick()
if mode ~= "calibrate" then return end
local rx, ry = input.getRawTouch()
if not armed then
if not rx then armed = true end
return
end
if rx then
pending = {x = rx, y = ry}
elseif pending then
samples[#samples + 1] = pending
pending = nil
if #samples == 1 then
drawTarget(2)
else
finishCalibration()
end
end
end