4e5796fd51
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.
110 lines
2.9 KiB
Lua
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
|