From 5a3aabf220d66bd0883b86006bb5b06b2f991ece Mon Sep 17 00:00:00 2001 From: Evan Reichard Date: Sat, 1 Aug 2026 18:09:08 -0400 Subject: [PATCH] feat(ui): setText on text nodes Assigning .label repainted nothing until the caller also remembered to invalidate, and forgetting produced a screen that silently stopped updating with no error to follow. setText makes the pair atomic and skips the repaint when the text is unchanged, so an app can push a value on every tick without comparing first. --- sdcard/apps/launcher/main.lua | 6 +----- sdcard/lib/ui.lua | 7 +++++++ test/ui_layout.lua | 11 +++++++++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/sdcard/apps/launcher/main.lua b/sdcard/apps/launcher/main.lua index dbc9832..b9c4078 100644 --- a/sdcard/apps/launcher/main.lua +++ b/sdcard/apps/launcher/main.lua @@ -40,11 +40,7 @@ function init() end function on_tick() - local label = sys.clockSynced() and os.date("%H:%M:%S") or PLACEHOLDER - if label ~= clock.label then - clock.label = label - clock:invalidate() - end + clock:setText(sys.clockSynced() and os.date("%H:%M:%S") or PLACEHOLDER) end function draw() screen:draw() end diff --git a/sdcard/lib/ui.lua b/sdcard/lib/ui.lua index 851af23..dadb57a 100644 --- a/sdcard/lib/ui.lua +++ b/sdcard/lib/ui.lua @@ -260,6 +260,13 @@ function ui.text(label, spec) node.paint = function(self) gui.drawText(self.label, self.rect.x, self.rect.y, self.color, self.bg) end + -- Setting .label alone repaints nothing, which fails silently. Unchanged text also + -- costs nothing, so a caller can push a value every tick without thinking about it. + node.setText = function(self, text) + if text == self.label then return end + self.label = text + self:invalidate() + end return node end diff --git a/test/ui_layout.lua b/test/ui_layout.lua index a4b0142..7574e21 100644 --- a/test/ui_layout.lua +++ b/test/ui_layout.lua @@ -87,6 +87,17 @@ app:down(100, 20) app:up(100, 20) assert(fired == 1, "release inside must fire once") +-- setText repaints only on a real change, so a caller may push a value every tick. +local label = ui.text("12:00:00") +local clockScreen = ui.screen(ui.box{label}) +clockScreen:draw() +assert(not label.dirty, "a drawn node starts clean") +label:setText("12:00:00") +assert(not label.dirty, "unchanged text must not repaint") +label:setText("12:00:01") +assert(label.dirty, "changed text must repaint") +assert(label.label == "12:00:01", label.label) + -- The pressed look is held briefly, then cleared on a later draw. assert(button.pressed, "pressed look must outlast the release") device.now = device.now + 100