#344 element redraws and shorter assert messages

This commit is contained in:
Mikayla Fischler
2023-09-29 19:34:10 -04:00
parent 70831b49d2
commit ed4180a072
35 changed files with 610 additions and 556 deletions

View File

@@ -24,21 +24,17 @@ local MOUSE_CLICK = core.events.MOUSE_CLICK
---@param args app_button_args
---@return graphics_element element, element_id id
local function app_button(args)
assert(type(args.text) == "string", "graphics.elements.controls.app: text is a required field")
assert(type(args.title) == "string", "graphics.elements.controls.app: title is a required field")
assert(type(args.callback) == "function", "graphics.elements.controls.app: callback is a required field")
assert(type(args.app_fg_bg) == "table", "graphics.elements.controls.app: app_fg_bg is a required field")
assert(type(args.text) == "string", "controls.app: text is a required field")
assert(type(args.title) == "string", "controls.app: title is a required field")
assert(type(args.callback) == "function", "controls.app: callback is a required field")
assert(type(args.app_fg_bg) == "table", "controls.app: app_fg_bg is a required field")
args.height = 4
args.width = 5
args.width = 5
-- create new graphics element base object
local e = element.new(args)
-- write app title, centered
e.w_set_cur(math.floor((e.frame.w - string.len(args.title)) / 2) + 1, 4)
e.w_write(args.title)
-- draw the app button
local function draw()
local fgd = args.app_fg_bg.fgd
@@ -120,8 +116,18 @@ local function app_button(args)
if val then e.handle_mouse(core.events.mouse_generic(core.events.MOUSE_CLICK.UP, 1, 1)) end
end
-- element redraw
function e.redraw()
-- write app title, centered
e.w_set_cur(math.floor((e.frame.w - string.len(args.title)) / 2) + 1, 4)
e.w_write(args.title)
-- draw button
draw()
end
-- initial draw
draw()
e.redraw()
return e.complete()
end

View File

@@ -18,8 +18,8 @@ local element = require("graphics.element")
---@param args checkbox_args
---@return graphics_element element, element_id id
local function checkbox(args)
assert(type(args.label) == "string", "graphics.elements.controls.checkbox: label is a required field")
assert(type(args.box_fg_bg) == "table", "graphics.elements.controls.checkbox: box_fg_bg is a required field")
assert(type(args.label) == "string", "controls.checkbox: label is a required field")
assert(type(args.box_fg_bg) == "table", "controls.checkbox: box_fg_bg is a required field")
args.can_focus = true
args.height = 1
@@ -105,9 +105,14 @@ local function checkbox(args)
e.on_enabled = draw_label
e.on_disabled = draw_label
-- element redraw
function e.redraw()
draw()
draw_label()
end
-- initial draw
draw()
draw_label()
e.redraw()
return e.complete()
end

View File

@@ -21,21 +21,16 @@ local element = require("graphics.element")
---@param args hazard_button_args
---@return graphics_element element, element_id id
local function hazard_button(args)
assert(type(args.text) == "string", "graphics.elements.controls.hazard_button: text is a required field")
assert(type(args.accent) == "number", "graphics.elements.controls.hazard_button: accent is a required field")
assert(type(args.callback) == "function", "graphics.elements.controls.hazard_button: callback is a required field")
assert(type(args.text) == "string", "controls.hazard_button: text is a required field")
assert(type(args.accent) == "number", "controls.hazard_button: accent is a required field")
assert(type(args.callback) == "function", "controls.hazard_button: callback is a required field")
-- static dimensions
args.height = 3
args.width = string.len(args.text) + 4
-- create new graphics element base object
local e = element.new(args)
-- write the button text
e.w_set_cur(3, 2)
e.w_write(args.text)
-- draw border
---@param accent color accent color
local function draw_border(accent)
@@ -158,7 +153,6 @@ local function hazard_button(args)
-- 1.5 second timeout
tcd.dispatch(1.5, on_timeout)
-- call the touch callback
args.callback()
end
end
@@ -195,8 +189,16 @@ local function hazard_button(args)
e.w_write(args.text)
end
-- initial draw of border
draw_border(args.accent)
-- element redraw
function e.redraw()
-- write the button text and draw border
e.w_set_cur(3, 2)
e.w_write(args.text)
draw_border(args.accent)
end
-- initial draw
e.redraw()
return e.complete()
end

View File

@@ -29,13 +29,11 @@ local element = require("graphics.element")
---@param args multi_button_args
---@return graphics_element element, element_id id
local function multi_button(args)
assert(type(args.options) == "table", "graphics.elements.controls.multi_button: options is a required field")
assert(#args.options > 0, "graphics.elements.controls.multi_button: at least one option is required")
assert(type(args.callback) == "function", "graphics.elements.controls.multi_button: callback is a required field")
assert(type(args.default) == "nil" or (type(args.default) == "number" and args.default > 0),
"graphics.elements.controls.multi_button: default must be nil or a number > 0")
assert(type(args.min_width) == "nil" or (type(args.min_width) == "number" and args.min_width > 0),
"graphics.elements.controls.multi_button: min_width must be nil or a number > 0")
assert(type(args.options) == "table", "controls.multi_button: options is a required field")
assert(#args.options > 0, "controls.multi_button: at least one option is required")
assert(type(args.callback) == "function", "controls.multi_button: callback is a required field")
assert(type(args.default) == "nil" or (type(args.default) == "number" and args.default > 0), "controls.multi_button: default must be nil or a number > 0")
assert(type(args.min_width) == "nil" or (type(args.min_width) == "number" and args.min_width > 0), "controls.multi_button: min_width must be nil or a number > 0")
-- single line
args.height = 1
@@ -71,7 +69,7 @@ local function multi_button(args)
end
-- show the button state
local function draw()
function e.redraw()
for i = 1, #args.options do
local opt = args.options[i] ---@type button_option
@@ -115,7 +113,7 @@ local function multi_button(args)
-- tap always has identical coordinates, so this always passes for taps
if button_ini == button_cur and button_cur ~= nil then
e.value = button_cur
draw()
e.redraw()
args.callback(e.value)
end
end
@@ -125,11 +123,11 @@ local function multi_button(args)
---@param val integer new value
function e.set_value(val)
e.value = val
draw()
e.redraw()
end
-- initial draw
draw()
e.redraw()
return e.complete()
end

View File

@@ -26,10 +26,9 @@ local KEY_CLICK = core.events.KEY_CLICK
---@param args push_button_args
---@return graphics_element element, element_id id
local function push_button(args)
assert(type(args.text) == "string", "graphics.elements.controls.push_button: text is a required field")
assert(type(args.callback) == "function", "graphics.elements.controls.push_button: callback is a required field")
assert(type(args.min_width) == "nil" or (type(args.min_width) == "number" and args.min_width > 0),
"graphics.elements.controls.push_button: min_width must be nil or a number > 0")
assert(type(args.text) == "string", "controls.push_button: text is a required field")
assert(type(args.callback) == "function", "controls.push_button: callback is a required field")
assert(type(args.min_width) == "nil" or (type(args.min_width) == "number" and args.min_width > 0), "controls.push_button: min_width must be nil or a number > 0")
local text_width = string.len(args.text)
@@ -46,7 +45,7 @@ local function push_button(args)
local v_pad = math.floor(e.frame.h / 2) + 1
-- draw the button
local function draw()
function e.redraw()
e.window.clear()
-- write the button text
@@ -60,7 +59,7 @@ local function push_button(args)
e.value = true
e.w_set_fgd(args.active_fg_bg.fgd)
e.w_set_bkg(args.active_fg_bg.bkg)
draw()
e.redraw()
end
end
@@ -70,7 +69,7 @@ local function push_button(args)
e.value = false
e.w_set_fgd(e.fg_bg.fgd)
e.w_set_bkg(e.fg_bg.bkg)
draw()
e.redraw()
end
end
@@ -117,7 +116,7 @@ local function push_button(args)
e.value = false
e.w_set_fgd(e.fg_bg.fgd)
e.w_set_bkg(e.fg_bg.bkg)
draw()
e.redraw()
end
end
@@ -127,7 +126,7 @@ local function push_button(args)
e.value = false
e.w_set_fgd(args.dis_fg_bg.fgd)
e.w_set_bkg(args.dis_fg_bg.bkg)
draw()
e.redraw()
end
end
@@ -136,7 +135,7 @@ local function push_button(args)
e.on_unfocused = show_unpressed
-- initial draw
draw()
e.redraw()
return e.complete()
end

View File

@@ -27,11 +27,12 @@ local element = require("graphics.element")
---@param args radio_2d_args
---@return graphics_element element, element_id id
local function radio_2d_button(args)
assert(type(args.options) == "table" and #args.options > 0, "graphics.elements.controls.radio_2d: options should be a table with length >= 1")
assert(type(args.radio_colors) == "table", "graphics.elements.controls.radio_2d: radio_colors is a required field")
assert(type(args.select_color) == "number" or type(args.color_map) == "table", "graphics.elements.controls.radio_2d: select_color or color_map is required")
assert(type(args.default) == "nil" or (type(args.default) == "number" and args.default > 0),
"graphics.elements.controls.radio_2d: default must be nil or a number > 0")
assert(type(args.options) == "table" and #args.options > 0, "controls.radio_2d: options should be a table with length >= 1")
assert(util.is_int(args.rows) and util.is_int(args.columns), "controls.radio_2d: rows/columns must be integers")
assert((args.rows * args.columns) >= #args.options, "controls.radio_2d: rows x columns size insufficient for provided number of options")
assert(type(args.radio_colors) == "table", "controls.radio_2d: radio_colors is a required field")
assert(type(args.select_color) == "number" or type(args.color_map) == "table", "controls.radio_2d: select_color or color_map is required")
assert(type(args.default) == "nil" or (type(args.default) == "number" and args.default > 0), "controls.radio_2d: default must be nil or a number > 0")
local array = {}
local col_widths = {}
@@ -74,8 +75,8 @@ local function radio_2d_button(args)
-- selected option (convert nil to 1 if missing)
e.value = args.default or 1
-- show the args.options/states
local function draw()
-- draw the element
function e.redraw()
local col_x = 1
local radio_color_b = util.trinary(type(args.disable_color) == "number" and not e.enabled, args.disable_color, args.radio_colors.color_b)
@@ -135,7 +136,7 @@ local function radio_2d_button(args)
if elem ~= nil and event.initial.x >= elem.x_1 and event.initial.x <= elem.x_2 and event.current.x >= elem.x_1 and event.current.x <= elem.x_2 then
e.value = elem.id
focused_opt = elem.id
draw()
e.redraw()
if type(args.callback) == "function" then args.callback(e.value) end
break
end
@@ -149,30 +150,30 @@ local function radio_2d_button(args)
if event.type == core.events.KEY_CLICK.DOWN or event.type == core.events.KEY_CLICK.HELD then
if event.type == core.events.KEY_CLICK.DOWN and (event.key == keys.space or event.key == keys.enter or event.key == keys.numPadEnter) then
e.value = focused_opt
draw()
e.redraw()
if type(args.callback) == "function" then args.callback(e.value) end
elseif event.key == keys.down then
if focused_opt < #args.options then
focused_opt = focused_opt + 1
draw()
e.redraw()
end
elseif event.key == keys.up then
if focused_opt > 1 then
focused_opt = focused_opt - 1
draw()
e.redraw()
end
elseif event.key == keys.right then
if array[focus_y + 1] and array[focus_y + 1][focus_x] then
focused_opt = array[focus_y + 1][focus_x].id
else focused_opt = array[1][focus_x].id end
draw()
e.redraw()
elseif event.key == keys.left then
if array[focus_y - 1] and array[focus_y - 1][focus_x] then
focused_opt = array[focus_y - 1][focus_x].id
draw()
e.redraw()
elseif array[#array][focus_x] then
focused_opt = array[#array][focus_x].id
draw()
e.redraw()
end
end
end
@@ -183,20 +184,20 @@ local function radio_2d_button(args)
function e.set_value(val)
if val > 0 and val <= #args.options then
e.value = val
draw()
e.redraw()
end
end
-- handle focus
e.on_focused = draw
e.on_unfocused = draw
e.on_focused = e.redraw
e.on_unfocused = e.redraw
-- handle enable
e.on_enabled = draw
e.on_disabled = draw
e.on_enabled = e.redraw
e.on_disabled = e.redraw
-- initial draw
draw()
e.redraw()
return e.complete()
end

View File

@@ -25,14 +25,14 @@ local KEY_CLICK = core.events.KEY_CLICK
---@param args radio_button_args
---@return graphics_element element, element_id id
local function radio_button(args)
assert(type(args.options) == "table", "graphics.elements.controls.radio_button: options is a required field")
assert(#args.options > 0, "graphics.elements.controls.radio_button: at least one option is required")
assert(type(args.radio_colors) == "table", "graphics.elements.controls.radio_button: radio_colors is a required field")
assert(type(args.select_color) == "number", "graphics.elements.controls.radio_button: select_color is a required field")
assert(type(args.options) == "table", "controls.radio_button: options is a required field")
assert(#args.options > 0, "controls.radio_button: at least one option is required")
assert(type(args.radio_colors) == "table", "controls.radio_button: radio_colors is a required field")
assert(type(args.select_color) == "number", "controls.radio_button: select_color is a required field")
assert(type(args.default) == "nil" or (type(args.default) == "number" and args.default > 0),
"graphics.elements.controls.radio_button: default must be nil or a number > 0")
"controls.radio_button: default must be nil or a number > 0")
assert(type(args.min_width) == "nil" or (type(args.min_width) == "number" and args.min_width > 0),
"graphics.elements.controls.radio_button: min_width must be nil or a number > 0")
"controls.radio_button: min_width must be nil or a number > 0")
-- determine widths
local max_width = 1
@@ -59,7 +59,7 @@ local function radio_button(args)
e.value = args.default or 1
-- show the button state
local function draw()
function e.redraw()
for i = 1, #args.options do
local opt = args.options[i] ---@type string
@@ -97,7 +97,7 @@ local function radio_button(args)
if args.options[event.current.y] ~= nil then
e.value = event.current.y
focused_opt = e.value
draw()
e.redraw()
if type(args.callback) == "function" then args.callback(e.value) end
end
end
@@ -109,17 +109,17 @@ local function radio_button(args)
if event.type == KEY_CLICK.DOWN or event.type == KEY_CLICK.HELD then
if event.type == KEY_CLICK.DOWN and (event.key == keys.space or event.key == keys.enter or event.key == keys.numPadEnter) then
e.value = focused_opt
draw()
e.redraw()
if type(args.callback) == "function" then args.callback(e.value) end
elseif event.key == keys.down then
if focused_opt < #args.options then
focused_opt = focused_opt + 1
draw()
e.redraw()
end
elseif event.key == keys.up then
if focused_opt > 1 then
focused_opt = focused_opt - 1
draw()
e.redraw()
end
end
end
@@ -130,20 +130,20 @@ local function radio_button(args)
function e.set_value(val)
if val > 0 and val <= #args.options then
e.value = val
draw()
e.redraw()
end
end
-- handle focus
e.on_focused = draw
e.on_unfocused = draw
e.on_focused = e.redraw
e.on_unfocused = e.redraw
-- handle enable
e.on_enabled = draw
e.on_disabled = draw
e.on_enabled = e.redraw
e.on_disabled = e.redraw
-- initial draw
draw()
e.redraw()
return e.complete()
end

View File

@@ -1,6 +1,7 @@
-- Sidebar Graphics Element
local tcd = require("scada-common.tcd")
local util = require("scada-common.util")
local core = require("graphics.core")
local element = require("graphics.element")
@@ -26,11 +27,10 @@ local MOUSE_CLICK = core.events.MOUSE_CLICK
---@param args sidebar_args
---@return graphics_element element, element_id id
local function sidebar(args)
assert(type(args.tabs) == "table", "graphics.elements.controls.sidebar: tabs is a required field")
assert(#args.tabs > 0, "graphics.elements.controls.sidebar: at least one tab is required")
assert(type(args.callback) == "function", "graphics.elements.controls.sidebar: callback is a required field")
assert(type(args.tabs) == "table", "controls.sidebar: tabs is a required field")
assert(#args.tabs > 0, "controls.sidebar: at least one tab is required")
assert(type(args.callback) == "function", "controls.sidebar: callback is a required field")
-- always 3 wide
args.width = 3
-- create new graphics element base object
@@ -41,10 +41,14 @@ local function sidebar(args)
-- default to 1st tab
e.value = 1
local was_pressed = false
-- show the button state
---@param pressed boolean if the currently selected tab should appear as actively pressed
---@param pressed? boolean if the currently selected tab should appear as actively pressed
---@param pressed_idx? integer optional index to show as held (that is not yet selected)
local function draw(pressed, pressed_idx)
pressed = util.trinary(pressed == nil, was_pressed, pressed)
was_pressed = pressed
pressed_idx = pressed_idx or e.value
for i = 1, #args.tabs do
@@ -65,12 +69,8 @@ local function sidebar(args)
e.w_write(" ")
e.w_set_cur(1, y + 1)
if e.value == i then
-- show as selected
e.w_write(" " .. tab.char .. "\x10")
else
-- show as unselected
e.w_write(" " .. tab.char .. " ")
end
else e.w_write(" " .. tab.char .. " ") end
e.w_set_cur(1, y + 2)
e.w_write(" ")
end
@@ -113,8 +113,10 @@ local function sidebar(args)
draw(false)
end
-- initial draw
draw(false)
-- element redraw
e.redraw = draw
e.redraw()
return e.complete()
end

View File

@@ -29,8 +29,8 @@ local function spinbox(args)
local wn_prec = args.whole_num_precision
local fr_prec = args.fractional_precision
assert(util.is_int(wn_prec), "graphics.element.controls.spinbox_numeric: whole number precision must be an integer")
assert(util.is_int(fr_prec), "graphics.element.controls.spinbox_numeric: fractional precision must be an integer")
assert(util.is_int(wn_prec), "controls.spinbox_numeric: whole number precision must be an integer")
assert(util.is_int(fr_prec), "controls.spinbox_numeric: fractional precision must be an integer")
local fmt, fmt_init ---@type string, string
@@ -44,7 +44,7 @@ local function spinbox(args)
local dec_point_x = args.whole_num_precision + 1
assert(type(args.arrow_fg_bg) == "table", "graphics.element.spinbox_numeric: arrow_fg_bg is a required field")
assert(type(args.arrow_fg_bg) == "table", "controls.spinbox_numeric: arrow_fg_bg is a required field")
-- determine widths
args.width = wn_prec + fr_prec + util.trinary(fr_prec > 0, 1, 0)
@@ -72,8 +72,6 @@ local function spinbox(args)
end
end
draw_arrows(args.arrow_fg_bg.fgd)
-- populate digits from current value
local function set_digits()
local initial_str = util.sprintf(fmt_init, e.value)
@@ -125,9 +123,6 @@ local function spinbox(args)
e.w_write(util.sprintf(fmt, e.value))
end
-- init with the default value
show_num()
-- handle mouse interaction
---@param event mouse_interaction mouse event
function e.handle_mouse(event)
@@ -138,10 +133,8 @@ local function spinbox(args)
local idx = util.trinary(event.current.x > dec_point_x, event.current.x - 1, event.current.x)
if digits[idx] ~= nil then
if event.current.y == 1 then
-- increment
digits[idx] = digits[idx] + 1
elseif event.current.y == 3 then
-- decrement
digits[idx] = digits[idx] - 1
end
@@ -176,18 +169,19 @@ local function spinbox(args)
end
-- enable this input
function e.on_enabled()
draw_arrows(args.arrow_fg_bg.fgd)
end
function e.on_enabled() draw_arrows(args.arrow_fg_bg.fgd) end
-- disable this input
function e.on_disabled()
draw_arrows(args.arrow_disable or colors.lightGray)
function e.on_disabled() draw_arrows(args.arrow_disable or colors.lightGray) end
-- element redraw
function e.redraw()
show_num()
draw_arrows(util.trinary(e.enabled, args.arrow_fg_bg.fgd, args.arrow_disable or colors.lightGray))
end
-- default to zero, init digits table
e.value = 0
set_digits()
-- initial draw
e.redraw()
return e.complete()
end

View File

@@ -21,15 +21,13 @@ local element = require("graphics.element")
---@param args switch_button_args
---@return graphics_element element, element_id id
local function switch_button(args)
assert(type(args.text) == "string", "graphics.elements.controls.switch_button: text is a required field")
assert(type(args.callback) == "function", "graphics.elements.controls.switch_button: callback is a required field")
assert(type(args.active_fg_bg) == "table", "graphics.elements.controls.switch_button: active_fg_bg is a required field")
assert(type(args.min_width) == "nil" or (type(args.min_width) == "number" and args.min_width > 0),
"graphics.elements.controls.switch_button: min_width must be nil or a number > 0")
assert(type(args.text) == "string", "controls.switch_button: text is a required field")
assert(type(args.callback) == "function", "controls.switch_button: callback is a required field")
assert(type(args.active_fg_bg) == "table", "controls.switch_button: active_fg_bg is a required field")
assert(type(args.min_width) == "nil" or (type(args.min_width) == "number" and args.min_width > 0), "controls.switch_button: min_width must be nil or a number > 0")
local text_width = string.len(args.text)
-- single line height, calculate width
args.height = 1
args.min_width = args.min_width or 0
args.width = math.max(text_width, args.min_width)
@@ -37,44 +35,32 @@ local function switch_button(args)
-- create new graphics element base object
local e = element.new(args)
-- button state (convert nil to false if missing)
e.value = args.default or false
local h_pad = math.floor((e.frame.w - text_width) / 2) + 1
local v_pad = math.floor(e.frame.h / 2) + 1
-- show the button state
local function draw_state()
function e.redraw()
if e.value then
-- show as pressed
e.w_set_fgd(args.active_fg_bg.fgd)
e.w_set_bkg(args.active_fg_bg.bkg)
else
-- show as unpressed
e.w_set_fgd(e.fg_bg.fgd)
e.w_set_bkg(e.fg_bg.bkg)
end
-- clear to redraw background
e.window.clear()
-- write the button text
e.w_set_cur(h_pad, v_pad)
e.w_write(args.text)
end
-- initial draw
draw_state()
-- handle mouse interaction
---@param event mouse_interaction mouse event
function e.handle_mouse(event)
if e.enabled and core.events.was_clicked(event.type) then
-- toggle state
e.value = not e.value
draw_state()
-- call the touch callback with state
e.redraw()
args.callback(e.value)
end
end
@@ -82,11 +68,13 @@ local function switch_button(args)
-- set the value
---@param val boolean new value
function e.set_value(val)
-- set state
e.value = val
draw_state()
e.redraw()
end
-- initial draw
e.redraw()
return e.complete()
end

View File

@@ -27,13 +27,11 @@ local element = require("graphics.element")
---@param args tabbar_args
---@return graphics_element element, element_id id
local function tabbar(args)
assert(type(args.tabs) == "table", "graphics.elements.controls.tabbar: tabs is a required field")
assert(#args.tabs > 0, "graphics.elements.controls.tabbar: at least one tab is required")
assert(type(args.callback) == "function", "graphics.elements.controls.tabbar: callback is a required field")
assert(type(args.min_width) == "nil" or (type(args.min_width) == "number" and args.min_width > 0),
"graphics.elements.controls.tabbar: min_width must be nil or a number > 0")
assert(type(args.tabs) == "table", "controls.tabbar: tabs is a required field")
assert(#args.tabs > 0, "controls.tabbar: at least one tab is required")
assert(type(args.callback) == "function", "controls.tabbar: callback is a required field")
assert(type(args.min_width) == "nil" or (type(args.min_width) == "number" and args.min_width > 0), "controls.tabbar: min_width must be nil or a number > 0")
-- always 1 tall
args.height = 1
-- determine widths
@@ -67,7 +65,7 @@ local function tabbar(args)
end
-- show the tab state
local function draw()
function e.redraw()
for i = 1, #args.tabs do
local tab = args.tabs[i] ---@type tabbar_tab
@@ -109,7 +107,7 @@ local function tabbar(args)
-- tap always has identical coordinates, so this always passes for taps
if tab_ini == tab_cur and tab_cur ~= nil then
e.value = tab_cur
draw()
e.redraw()
args.callback(e.value)
end
end
@@ -119,11 +117,11 @@ local function tabbar(args)
---@param val integer new value
function e.set_value(val)
e.value = val
draw()
e.redraw()
end
-- initial draw
draw()
e.redraw()
return e.complete()
end