#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

@ -360,6 +360,8 @@ function element.new(args, child_offset_x, child_offset_y)
---@param result any ---@param result any
function protected.response_callback(result) end function protected.response_callback(result) end
-- accessors and control --
-- get value -- get value
---@nodiscard ---@nodiscard
function protected.get_value() return protected.value end function protected.get_value() return protected.value end
@ -387,6 +389,9 @@ function element.new(args, child_offset_x, child_offset_y)
-- luacheck: pop -- luacheck: pop
---@diagnostic enable: unused-local, unused-vararg ---@diagnostic enable: unused-local, unused-vararg
-- re-draw this element
function protected.redraw() end
-- start animations -- start animations
function protected.start_anim() end function protected.start_anim() end

View File

@ -1,7 +1,5 @@
-- Color Map Graphics Element -- Color Map Graphics Element
local util = require("scada-common.util")
local element = require("graphics.element") local element = require("graphics.element")
---@class colormap_args ---@class colormap_args
@ -16,7 +14,7 @@ local element = require("graphics.element")
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function colormap(args) local function colormap(args)
local bkg = "008877FFCCEE114455DD9933BBAA2266" local bkg = "008877FFCCEE114455DD9933BBAA2266"
local spaces = util.spaces(32) local spaces = string.rep(" ", 32)
args.width = 32 args.width = 32
args.height = 1 args.height = 1
@ -25,8 +23,13 @@ local function colormap(args)
local e = element.new(args) local e = element.new(args)
-- draw color map -- draw color map
e.w_set_cur(1, 1) function e.redraw()
e.w_blit(spaces, bkg, bkg) e.w_set_cur(1, 1)
e.w_blit(spaces, bkg, bkg)
end
-- initial draw
e.redraw()
return e.complete() return e.complete()
end end

View File

@ -24,21 +24,17 @@ local MOUSE_CLICK = core.events.MOUSE_CLICK
---@param args app_button_args ---@param args app_button_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function app_button(args) local function app_button(args)
assert(type(args.text) == "string", "graphics.elements.controls.app: text is a required field") assert(type(args.text) == "string", "controls.app: text is a required field")
assert(type(args.title) == "string", "graphics.elements.controls.app: title is a required field") assert(type(args.title) == "string", "controls.app: title is a required field")
assert(type(args.callback) == "function", "graphics.elements.controls.app: callback is a required field") assert(type(args.callback) == "function", "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.app_fg_bg) == "table", "controls.app: app_fg_bg is a required field")
args.height = 4 args.height = 4
args.width = 5 args.width = 5
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) 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 -- draw the app button
local function draw() local function draw()
local fgd = args.app_fg_bg.fgd 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 if val then e.handle_mouse(core.events.mouse_generic(core.events.MOUSE_CLICK.UP, 1, 1)) end
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 -- initial draw
draw() e.redraw()
return e.complete() return e.complete()
end end

View File

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

View File

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

View File

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

View File

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

View File

@ -27,11 +27,12 @@ local element = require("graphics.element")
---@param args radio_2d_args ---@param args radio_2d_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function radio_2d_button(args) 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.options) == "table" and #args.options > 0, "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(util.is_int(args.rows) and util.is_int(args.columns), "controls.radio_2d: rows/columns must be integers")
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((args.rows * args.columns) >= #args.options, "controls.radio_2d: rows x columns size insufficient for provided number of options")
assert(type(args.default) == "nil" or (type(args.default) == "number" and args.default > 0), assert(type(args.radio_colors) == "table", "controls.radio_2d: radio_colors is a required field")
"graphics.elements.controls.radio_2d: default must be nil or a number > 0") 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 array = {}
local col_widths = {} local col_widths = {}
@ -74,8 +75,8 @@ local function radio_2d_button(args)
-- selected option (convert nil to 1 if missing) -- selected option (convert nil to 1 if missing)
e.value = args.default or 1 e.value = args.default or 1
-- show the args.options/states -- draw the element
local function draw() function e.redraw()
local col_x = 1 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) 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 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 e.value = elem.id
focused_opt = elem.id focused_opt = elem.id
draw() e.redraw()
if type(args.callback) == "function" then args.callback(e.value) end if type(args.callback) == "function" then args.callback(e.value) end
break break
end 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 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 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 e.value = focused_opt
draw() e.redraw()
if type(args.callback) == "function" then args.callback(e.value) end if type(args.callback) == "function" then args.callback(e.value) end
elseif event.key == keys.down then elseif event.key == keys.down then
if focused_opt < #args.options then if focused_opt < #args.options then
focused_opt = focused_opt + 1 focused_opt = focused_opt + 1
draw() e.redraw()
end end
elseif event.key == keys.up then elseif event.key == keys.up then
if focused_opt > 1 then if focused_opt > 1 then
focused_opt = focused_opt - 1 focused_opt = focused_opt - 1
draw() e.redraw()
end end
elseif event.key == keys.right then elseif event.key == keys.right then
if array[focus_y + 1] and array[focus_y + 1][focus_x] then if array[focus_y + 1] and array[focus_y + 1][focus_x] then
focused_opt = array[focus_y + 1][focus_x].id focused_opt = array[focus_y + 1][focus_x].id
else focused_opt = array[1][focus_x].id end else focused_opt = array[1][focus_x].id end
draw() e.redraw()
elseif event.key == keys.left then elseif event.key == keys.left then
if array[focus_y - 1] and array[focus_y - 1][focus_x] then if array[focus_y - 1] and array[focus_y - 1][focus_x] then
focused_opt = array[focus_y - 1][focus_x].id focused_opt = array[focus_y - 1][focus_x].id
draw() e.redraw()
elseif array[#array][focus_x] then elseif array[#array][focus_x] then
focused_opt = array[#array][focus_x].id focused_opt = array[#array][focus_x].id
draw() e.redraw()
end end
end end
end end
@ -183,20 +184,20 @@ local function radio_2d_button(args)
function e.set_value(val) function e.set_value(val)
if val > 0 and val <= #args.options then if val > 0 and val <= #args.options then
e.value = val e.value = val
draw() e.redraw()
end end
end end
-- handle focus -- handle focus
e.on_focused = draw e.on_focused = e.redraw
e.on_unfocused = draw e.on_unfocused = e.redraw
-- handle enable -- handle enable
e.on_enabled = draw e.on_enabled = e.redraw
e.on_disabled = draw e.on_disabled = e.redraw
-- initial draw -- initial draw
draw() e.redraw()
return e.complete() return e.complete()
end end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -42,30 +42,6 @@ local function number_field(args)
-- make an interactive field manager -- make an interactive field manager
local ifield = core.new_ifield(e, args.max_digits, args.fg_bg, args.dis_fg_bg) local ifield = core.new_ifield(e, args.max_digits, args.fg_bg, args.dis_fg_bg)
-- draw input
local function show()
if e.enabled then
e.w_set_bkg(args.fg_bg.bkg)
e.w_set_fgd(args.fg_bg.fgd)
else
e.w_set_bkg(args.dis_fg_bg.bkg)
e.w_set_fgd(args.dis_fg_bg.fgd)
end
-- clear and print
e.w_set_cur(1, 1)
e.w_write(string.rep(" ", e.frame.w))
e.w_set_cur(1, 1)
e.w_write(e.value)
-- show cursor if focused
if e.is_focused() and e.enabled then
e.w_set_fgd(colors.lightGray)
e.w_write("_")
end
end
-- handle mouse interaction -- handle mouse interaction
---@param event mouse_interaction mouse event ---@param event mouse_interaction mouse event
function e.handle_mouse(event) function e.handle_mouse(event)
@ -117,9 +93,7 @@ local function number_field(args)
-- set the value (must be a number) -- set the value (must be a number)
---@param val number number to show ---@param val number number to show
function e.set_value(val) function e.set_value(val)
if tonumber(val) then if tonumber(val) then ifield.set_value("" .. tonumber(val)) end
ifield.set_value("" .. tonumber(val))
end
end end
-- set minimum input value -- set minimum input value
@ -140,9 +114,6 @@ local function number_field(args)
end end
end end
-- handle focused
e.on_focused = show
-- handle unfocused -- handle unfocused
function e.on_unfocused() function e.on_unfocused()
local val = tonumber(e.value) local val = tonumber(e.value)
@ -162,12 +133,14 @@ local function number_field(args)
ifield.show() ifield.show()
end end
-- handle enable -- handle focus (not unfocus), enable, and redraw with show()
e.on_focused = ifield.show
e.on_enabled = ifield.show e.on_enabled = ifield.show
e.on_disabled = ifield.show e.on_disabled = ifield.show
e.redraw = ifield.show
-- initial draw -- initial draw
ifield.show() e.redraw()
return e.complete() return e.complete()
end end

View File

@ -88,16 +88,15 @@ local function text_field(args)
ifield.set_value(text) ifield.set_value(text)
end end
-- handle focus -- handle focus, enable, and redraw with show()
e.on_focused = ifield.show e.on_focused = ifield.show
e.on_unfocused = ifield.show e.on_unfocused = ifield.show
-- handle enable
e.on_enabled = ifield.show e.on_enabled = ifield.show
e.on_disabled = ifield.show e.on_disabled = ifield.show
e.redraw = ifield.show
-- initial draw -- initial draw
ifield.show() e.redraw()
local elem, id = e.complete() local elem, id = e.complete()
return elem, id, ifield.censor return elem, id, ifield.censor

View File

@ -25,13 +25,13 @@ local flasher = require("graphics.flasher")
---@param args alarm_indicator_light ---@param args alarm_indicator_light
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function alarm_indicator_light(args) local function alarm_indicator_light(args)
assert(type(args.label) == "string", "graphics.elements.indicators.alight: label is a required field") assert(type(args.label) == "string", "indicators.alight: label is a required field")
assert(type(args.c1) == "number", "graphics.elements.indicators.alight: c1 is a required field") assert(type(args.c1) == "number", "indicators.alight: c1 is a required field")
assert(type(args.c2) == "number", "graphics.elements.indicators.alight: c2 is a required field") assert(type(args.c2) == "number", "indicators.alight: c2 is a required field")
assert(type(args.c3) == "number", "graphics.elements.indicators.alight: c3 is a required field") assert(type(args.c3) == "number", "indicators.alight: c3 is a required field")
if args.flash then if args.flash then
assert(util.is_int(args.period), "graphics.elements.indicators.alight: period is a required field if flash is enabled") assert(util.is_int(args.period), "indicators.alight: period is a required field if flash is enabled")
end end
-- single line -- single line
@ -51,6 +51,8 @@ local function alarm_indicator_light(args)
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) local e = element.new(args)
e.value = 1
-- called by flasher when enabled -- called by flasher when enabled
local function flash_callback() local function flash_callback()
e.w_set_cur(1, 1) e.w_set_cur(1, 1)
@ -105,9 +107,14 @@ local function alarm_indicator_light(args)
---@param val integer indicator state ---@param val integer indicator state
function e.set_value(val) e.on_update(val) end function e.set_value(val) e.on_update(val) end
-- write label and initial indicator light -- draw label and indicator light
e.on_update(1) function e.redraw()
e.w_write(args.label) e.on_update(e.value)
e.w_write(args.label)
end
-- initial draw
e.redraw()
return e.complete() return e.complete()
end end

View File

@ -18,8 +18,8 @@ local element = require("graphics.element")
---@param args core_map_args ---@param args core_map_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function core_map(args) local function core_map(args)
assert(util.is_int(args.reactor_l), "graphics.elements.indicators.coremap: reactor_l is a required field") assert(util.is_int(args.reactor_l), "indicators.coremap: reactor_l is a required field")
assert(util.is_int(args.reactor_w), "graphics.elements.indicators.coremap: reactor_w is a required field") assert(util.is_int(args.reactor_w), "indicators.coremap: reactor_w is a required field")
-- require max dimensions -- require max dimensions
args.width = 18 args.width = 18
@ -31,6 +31,8 @@ local function core_map(args)
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) local e = element.new(args)
e.value = 0
local alternator = true local alternator = true
local core_l = args.reactor_l - 2 local core_l = args.reactor_l - 2
@ -157,11 +159,14 @@ local function core_map(args)
e.on_update(e.value) e.on_update(e.value)
end end
-- initial (one-time except for resize()) frame draw -- redraw both frame and core
draw_frame() function e.redraw()
draw_frame()
draw_core(e.value)
end
-- initial draw -- initial draw
e.on_update(0) e.redraw()
return e.complete() return e.complete()
end end

View File

@ -24,25 +24,17 @@ local element = require("graphics.element")
---@param args data_indicator_args ---@param args data_indicator_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function data(args) local function data(args)
assert(type(args.label) == "string", "graphics.elements.indicators.data: label is a required field") assert(type(args.label) == "string", "indicators.data: label is a required field")
assert(type(args.format) == "string", "graphics.elements.indicators.data: format is a required field") assert(type(args.format) == "string", "indicators.data: format is a required field")
assert(args.value ~= nil, "graphics.elements.indicators.data: value is a required field") assert(args.value ~= nil, "indicators.data: value is a required field")
assert(util.is_int(args.width), "graphics.elements.indicators.data: width is a required field") assert(util.is_int(args.width), "indicators.data: width is a required field")
-- single line
args.height = 1 args.height = 1
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) local e = element.new(args)
-- label color e.value = args.value
if args.lu_colors ~= nil then
e.w_set_fgd(args.lu_colors.color_a)
end
-- write label
e.w_set_cur(1, 1)
e.w_write(args.label)
local value_color = e.fg_bg.fgd local value_color = e.fg_bg.fgd
local label_len = string.len(args.label) local label_len = string.len(args.label)
@ -93,8 +85,17 @@ local function data(args)
e.on_update(e.value) e.on_update(e.value)
end end
-- initial value draw -- element redraw
e.on_update(args.value) function e.redraw()
if args.lu_colors ~= nil then e.w_set_fgd(args.lu_colors.color_a) end
e.w_set_cur(1, 1)
e.w_write(args.label)
e.on_update(e.value)
end
-- initial draw
e.redraw()
return e.complete() return e.complete()
end end

View File

@ -28,10 +28,12 @@ local function hbar(args)
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) local e = element.new(args)
e.value = 0.0
-- bar width is width - 5 characters for " 100%" if showing percent -- bar width is width - 5 characters for " 100%" if showing percent
local bar_width = util.trinary(args.show_percent, e.frame.w - 5, e.frame.w) local bar_width = util.trinary(args.show_percent, e.frame.w - 5, e.frame.w)
assert(bar_width > 0, "graphics.elements.indicators.hbar: too small for bar") assert(bar_width > 0, "indicators.hbar: too small for bar")
-- determine bar colors -- determine bar colors
local bar_bkg = e.fg_bg.blit_bkg local bar_bkg = e.fg_bg.blit_bkg
@ -105,20 +107,21 @@ local function hbar(args)
function e.recolor(bar_fg_bg) function e.recolor(bar_fg_bg)
bar_bkg = bar_fg_bg.blit_bkg bar_bkg = bar_fg_bg.blit_bkg
bar_fgd = bar_fg_bg.blit_fgd bar_fgd = bar_fg_bg.blit_fgd
e.redraw()
-- re-draw
last_num_bars = 0
if type(e.value) == "number" then
e.on_update(e.value)
end
end end
-- set the percentage value -- set the percentage value
---@param val number 0.0 to 1.0 ---@param val number 0.0 to 1.0
function e.set_value(val) e.on_update(val) end function e.set_value(val) e.on_update(val) end
-- initialize to 0 -- element redraw
e.on_update(0) function e.redraw()
last_num_bars = -1
e.on_update(e.value)
end
-- initial draw
e.redraw()
return e.complete() return e.complete()
end end

View File

@ -23,18 +23,17 @@ local element = require("graphics.element")
---@param args icon_indicator_args ---@param args icon_indicator_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function icon(args) local function icon(args)
assert(type(args.label) == "string", "graphics.elements.indicators.icon: label is a required field") assert(type(args.label) == "string", "indicators.icon: label is a required field")
assert(type(args.states) == "table", "graphics.elements.indicators.icon: states is a required field") assert(type(args.states) == "table", "indicators.icon: states is a required field")
-- single line
args.height = 1 args.height = 1
-- determine width
args.width = math.max(args.min_label_width or 1, string.len(args.label)) + 4 args.width = math.max(args.min_label_width or 1, string.len(args.label)) + 4
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) local e = element.new(args)
e.value = args.value or 1
-- state blit strings -- state blit strings
local state_blit_cmds = {} local state_blit_cmds = {}
for i = 1, #args.states do for i = 1, #args.states do
@ -47,10 +46,6 @@ local function icon(args)
}) })
end end
-- write label and initial indicator light
e.w_set_cur(5, 1)
e.w_write(args.label)
-- on state change -- on state change
---@param new_state integer indicator state ---@param new_state integer indicator state
function e.on_update(new_state) function e.on_update(new_state)
@ -64,8 +59,16 @@ local function icon(args)
---@param val integer indicator state ---@param val integer indicator state
function e.set_value(val) e.on_update(val) end function e.set_value(val) e.on_update(val) end
-- initial icon draw -- element redraw
e.on_update(args.value or 1) function e.redraw()
e.w_set_cur(5, 1)
e.w_write(args.label)
e.on_update(e.value)
end
-- initial draw
e.redraw()
return e.complete() return e.complete()
end end

View File

@ -23,25 +23,23 @@ local flasher = require("graphics.flasher")
---@param args indicator_led_args ---@param args indicator_led_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function indicator_led(args) local function indicator_led(args)
assert(type(args.label) == "string", "graphics.elements.indicators.led: label is a required field") assert(type(args.label) == "string", "indicators.led: label is a required field")
assert(type(args.colors) == "table", "graphics.elements.indicators.led: colors is a required field") assert(type(args.colors) == "table", "indicators.led: colors is a required field")
if args.flash then if args.flash then
assert(util.is_int(args.period), "graphics.elements.indicators.led: period is a required field if flash is enabled") assert(util.is_int(args.period), "indicators.led: period is a required field if flash is enabled")
end end
-- single line
args.height = 1 args.height = 1
-- determine width
args.width = math.max(args.min_label_width or 0, string.len(args.label)) + 2 args.width = math.max(args.min_label_width or 0, string.len(args.label)) + 2
-- flasher state
local flash_on = true local flash_on = true
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) local e = element.new(args)
e.value = false
-- called by flasher when enabled -- called by flasher when enabled
local function flash_callback() local function flash_callback()
e.w_set_cur(1, 1) e.w_set_cur(1, 1)
@ -88,13 +86,18 @@ local function indicator_led(args)
---@param val boolean indicator state ---@param val boolean indicator state
function e.set_value(val) e.on_update(val) end function e.set_value(val) e.on_update(val) end
-- write label and initial indicator light -- draw label and indicator light
e.on_update(false) function e.redraw()
if string.len(args.label) > 0 then e.on_update(e.value)
e.w_set_cur(3, 1) if string.len(args.label) > 0 then
e.w_write(args.label) e.w_set_cur(3, 1)
e.w_write(args.label)
end
end end
-- initial draw
e.redraw()
return e.complete() return e.complete()
end end

View File

@ -25,25 +25,20 @@ local flasher = require("graphics.flasher")
---@param args indicator_led_pair_args ---@param args indicator_led_pair_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function indicator_led_pair(args) local function indicator_led_pair(args)
assert(type(args.label) == "string", "graphics.elements.indicators.ledpair: label is a required field") assert(type(args.label) == "string", "indicators.ledpair: label is a required field")
assert(type(args.off) == "number", "graphics.elements.indicators.ledpair: off is a required field") assert(type(args.off) == "number", "indicators.ledpair: off is a required field")
assert(type(args.c1) == "number", "graphics.elements.indicators.ledpair: c1 is a required field") assert(type(args.c1) == "number", "indicators.ledpair: c1 is a required field")
assert(type(args.c2) == "number", "graphics.elements.indicators.ledpair: c2 is a required field") assert(type(args.c2) == "number", "indicators.ledpair: c2 is a required field")
if args.flash then if args.flash then
assert(util.is_int(args.period), "graphics.elements.indicators.ledpair: period is a required field if flash is enabled") assert(util.is_int(args.period), "indicators.ledpair: period is a required field if flash is enabled")
end end
-- single line
args.height = 1 args.height = 1
-- determine width
args.width = math.max(args.min_label_width or 0, string.len(args.label)) + 2 args.width = math.max(args.min_label_width or 0, string.len(args.label)) + 2
-- flasher state
local flash_on = true local flash_on = true
-- blit translations
local co = colors.toBlit(args.off) local co = colors.toBlit(args.off)
local c1 = colors.toBlit(args.c1) local c1 = colors.toBlit(args.c1)
local c2 = colors.toBlit(args.c2) local c2 = colors.toBlit(args.c2)
@ -51,7 +46,6 @@ local function indicator_led_pair(args)
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) local e = element.new(args)
-- init value for initial check in on_update
e.value = 1 e.value = 1
-- called by flasher when enabled -- called by flasher when enabled
@ -86,7 +80,6 @@ local function indicator_led_pair(args)
elseif new_state <= 1 then elseif new_state <= 1 then
flash_on = false flash_on = false
flasher.stop(flash_callback) flasher.stop(flash_callback)
e.w_blit("\x8c", co, e.fg_bg.blit_bkg) e.w_blit("\x8c", co, e.fg_bg.blit_bkg)
end end
elseif new_state == 2 then elseif new_state == 2 then
@ -102,13 +95,18 @@ local function indicator_led_pair(args)
---@param val integer indicator state ---@param val integer indicator state
function e.set_value(val) e.on_update(val) end function e.set_value(val) e.on_update(val) end
-- write label and initial indicator light -- draw label and indicator light
e.on_update(1) function e.redraw()
if string.len(args.label) > 0 then e.on_update(e.value)
e.w_set_cur(3, 1) if string.len(args.label) > 0 then
e.w_write(args.label) e.w_set_cur(3, 1)
e.w_write(args.label)
end
end end
-- initial draw
e.redraw()
return e.complete() return e.complete()
end end

View File

@ -18,19 +18,15 @@ local element = require("graphics.element")
---@param args indicator_led_rgb_args ---@param args indicator_led_rgb_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function indicator_led_rgb(args) local function indicator_led_rgb(args)
assert(type(args.label) == "string", "graphics.elements.indicators.ledrgb: label is a required field") assert(type(args.label) == "string", "indicators.ledrgb: label is a required field")
assert(type(args.colors) == "table", "graphics.elements.indicators.ledrgb: colors is a required field") assert(type(args.colors) == "table", "indicators.ledrgb: colors is a required field")
-- single line
args.height = 1 args.height = 1
-- determine width
args.width = math.max(args.min_label_width or 0, string.len(args.label)) + 2 args.width = math.max(args.min_label_width or 0, string.len(args.label)) + 2
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) local e = element.new(args)
-- init value for initial check in on_update
e.value = 1 e.value = 1
-- on state change -- on state change
@ -47,13 +43,18 @@ local function indicator_led_rgb(args)
---@param val integer indicator state ---@param val integer indicator state
function e.set_value(val) e.on_update(val) end function e.set_value(val) e.on_update(val) end
-- write label and initial indicator light -- draw label and indicator light
e.on_update(1) function e.redraw()
if string.len(args.label) > 0 then e.on_update(e.value)
e.w_set_cur(3, 1) if string.len(args.label) > 0 then
e.w_write(args.label) e.w_set_cur(3, 1)
e.w_write(args.label)
end
end end
-- initial draw
e.redraw()
return e.complete() return e.complete()
end end

View File

@ -23,25 +23,23 @@ local flasher = require("graphics.flasher")
---@param args indicator_light_args ---@param args indicator_light_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function indicator_light(args) local function indicator_light(args)
assert(type(args.label) == "string", "graphics.elements.indicators.light: label is a required field") assert(type(args.label) == "string", "indicators.light: label is a required field")
assert(type(args.colors) == "table", "graphics.elements.indicators.light: colors is a required field") assert(type(args.colors) == "table", "indicators.light: colors is a required field")
if args.flash then if args.flash then
assert(util.is_int(args.period), "graphics.elements.indicators.light: period is a required field if flash is enabled") assert(util.is_int(args.period), "indicators.light: period is a required field if flash is enabled")
end end
-- single line
args.height = 1 args.height = 1
-- determine width
args.width = math.max(args.min_label_width or 1, string.len(args.label)) + 2 args.width = math.max(args.min_label_width or 1, string.len(args.label)) + 2
-- flasher state
local flash_on = true local flash_on = true
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) local e = element.new(args)
e.value = false
-- called by flasher when enabled -- called by flasher when enabled
local function flash_callback() local function flash_callback()
e.w_set_cur(1, 1) e.w_set_cur(1, 1)
@ -88,10 +86,15 @@ local function indicator_light(args)
---@param val boolean indicator state ---@param val boolean indicator state
function e.set_value(val) e.on_update(val) end function e.set_value(val) e.on_update(val) end
-- write label and initial indicator light -- draw label and indicator light
e.on_update(false) function e.redraw()
e.w_set_cur(3, 1) e.on_update(false)
e.w_write(args.label) e.w_set_cur(3, 1)
e.w_write(args.label)
end
-- initial draw
e.redraw()
return e.complete() return e.complete()
end end

View File

@ -9,7 +9,7 @@ local element = require("graphics.element")
---@field format string power format override (lua string format) ---@field format string power format override (lua string format)
---@field rate boolean? whether to append /t to the end (power per tick) ---@field rate boolean? whether to append /t to the end (power per tick)
---@field lu_colors? cpair label foreground color (a), unit foreground color (b) ---@field lu_colors? cpair label foreground color (a), unit foreground color (b)
---@field value any default value ---@field value number default value
---@field parent graphics_element ---@field parent graphics_element
---@field id? string element id ---@field id? string element id
---@field x? integer 1 if omitted ---@field x? integer 1 if omitted
@ -23,26 +23,17 @@ local element = require("graphics.element")
---@param args power_indicator_args ---@param args power_indicator_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function power(args) local function power(args)
assert(args.value ~= nil, "graphics.elements.indicators.power: value is a required field") assert(type(args.value) == "number", "indicators.power: value is a required number field")
assert(util.is_int(args.width), "graphics.elements.indicators.power: width is a required field") assert(util.is_int(args.width), "indicators.power: width is a required field")
-- single line
args.height = 1 args.height = 1
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) local e = element.new(args)
-- label color e.value = args.value
if args.lu_colors ~= nil then
e.w_set_fgd(args.lu_colors.color_a)
end
-- write label local data_start = 0
e.w_set_cur(1, 1)
e.w_write(args.label)
local data_start = string.len(args.label) + 2
if string.len(args.label) == 0 then data_start = 1 end
-- on state change -- on state change
---@param value any new value ---@param value any new value
@ -77,8 +68,20 @@ local function power(args)
---@param val any new value ---@param val any new value
function e.set_value(val) e.on_update(val) end function e.set_value(val) e.on_update(val) end
-- initial value draw -- element redraw
e.on_update(args.value) function e.redraw()
if args.lu_colors ~= nil then e.w_set_fgd(args.lu_colors.color_a) end
e.w_set_cur(1, 1)
e.w_write(args.label)
data_start = string.len(args.label) + 2
if string.len(args.label) == 0 then data_start = 1 end
e.on_update(e.value)
end
-- initial draw
e.redraw()
return e.complete() return e.complete()
end end

View File

@ -10,7 +10,7 @@ local element = require("graphics.element")
---@field format string data format (lua string format) ---@field format string data format (lua string format)
---@field commas? boolean whether to use commas if a number is given (default to false) ---@field commas? boolean whether to use commas if a number is given (default to false)
---@field lu_colors? cpair label foreground color (a), unit foreground color (b) ---@field lu_colors? cpair label foreground color (a), unit foreground color (b)
---@field value any default value ---@field value number default value
---@field parent graphics_element ---@field parent graphics_element
---@field id? string element id ---@field id? string element id
---@field x? integer 1 if omitted ---@field x? integer 1 if omitted
@ -24,24 +24,17 @@ local element = require("graphics.element")
---@param args rad_indicator_args ---@param args rad_indicator_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function rad(args) local function rad(args)
assert(type(args.label) == "string", "graphics.elements.indicators.rad: label is a required field") assert(type(args.value) ~= "number", "indicators.rad: value is a required number field")
assert(type(args.format) == "string", "graphics.elements.indicators.rad: format is a required field") assert(type(args.label) == "string", "indicators.rad: label is a required field")
assert(util.is_int(args.width), "graphics.elements.indicators.rad: width is a required field") assert(type(args.format) == "string", "indicators.rad: format is a required field")
assert(util.is_int(args.width), "indicators.rad: width is a required field")
-- single line
args.height = 1 args.height = 1
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) local e = element.new(args)
-- label color e.value = types.new_zero_radiation_reading()
if args.lu_colors ~= nil then
e.w_set_fgd(args.lu_colors.color_a)
end
-- write label
e.w_set_cur(1, 1)
e.w_write(args.label)
local label_len = string.len(args.label) local label_len = string.len(args.label)
local data_start = 1 local data_start = 1
@ -82,8 +75,17 @@ local function rad(args)
---@param val any new value ---@param val any new value
function e.set_value(val) e.on_update(val) end function e.set_value(val) e.on_update(val) end
-- initial value draw -- element redraw
e.on_update(types.new_zero_radiation_reading()) function e.redraw()
if args.lu_colors ~= nil then e.w_set_fgd(args.lu_colors.color_a) end
e.w_set_cur(1, 1)
e.w_write(args.label)
e.on_update(e.value)
end
-- initial draw
e.redraw()
return e.complete() return e.complete()
end end

View File

@ -25,16 +25,14 @@ local element = require("graphics.element")
---@param args state_indicator_args ---@param args state_indicator_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function state_indicator(args) local function state_indicator(args)
assert(type(args.states) == "table", "graphics.elements.indicators.state: states is a required field") assert(type(args.states) == "table", "indicators.state: states is a required field")
-- determine height
if util.is_int(args.height) then if util.is_int(args.height) then
assert(args.height % 2 == 1, "graphics.elements.indicators.state: height should be an odd number") assert(args.height % 2 == 1, "indicators.state: height should be an odd number")
else else
args.height = 1 args.height = 1
end end
-- initial guess at width
args.width = args.min_width or 1 args.width = args.min_width or 1
-- state blit strings -- state blit strings
@ -42,7 +40,6 @@ local function state_indicator(args)
for i = 1, #args.states do for i = 1, #args.states do
local state_def = args.states[i] ---@type state_text_color local state_def = args.states[i] ---@type state_text_color
-- re-determine width
if string.len(state_def.text) > args.width then if string.len(state_def.text) > args.width then
args.width = string.len(state_def.text) args.width = string.len(state_def.text)
end end
@ -59,13 +56,20 @@ local function state_indicator(args)
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) local e = element.new(args)
e.value = args.value or 1
-- element redraw
function e.redraw()
local blit_cmd = state_blit_cmds[e.value]
e.w_set_cur(1, 1)
e.w_blit(blit_cmd.text, blit_cmd.fgd, blit_cmd.bkg)
end
-- on state change -- on state change
---@param new_state integer indicator state ---@param new_state integer indicator state
function e.on_update(new_state) function e.on_update(new_state)
local blit_cmd = state_blit_cmds[new_state]
e.value = new_state e.value = new_state
e.w_set_cur(1, 1) e.redraw()
e.w_blit(blit_cmd.text, blit_cmd.fgd, blit_cmd.bkg)
end end
-- set indicator state -- set indicator state
@ -73,7 +77,7 @@ local function state_indicator(args)
function e.set_value(val) e.on_update(val) end function e.set_value(val) e.on_update(val) end
-- initial draw -- initial draw
e.on_update(args.value or 1) e.redraw()
return e.complete() return e.complete()
end end

View File

@ -25,35 +25,29 @@ local flasher = require("graphics.flasher")
---@param args tristate_indicator_light_args ---@param args tristate_indicator_light_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function tristate_indicator_light(args) local function tristate_indicator_light(args)
assert(type(args.label) == "string", "graphics.elements.indicators.trilight: label is a required field") assert(type(args.label) == "string", "indicators.trilight: label is a required field")
assert(type(args.c1) == "number", "graphics.elements.indicators.trilight: c1 is a required field") assert(type(args.c1) == "number", "indicators.trilight: c1 is a required field")
assert(type(args.c2) == "number", "graphics.elements.indicators.trilight: c2 is a required field") assert(type(args.c2) == "number", "indicators.trilight: c2 is a required field")
assert(type(args.c3) == "number", "graphics.elements.indicators.trilight: c3 is a required field") assert(type(args.c3) == "number", "indicators.trilight: c3 is a required field")
if args.flash then if args.flash then
assert(util.is_int(args.period), "graphics.elements.indicators.trilight: period is a required field if flash is enabled") assert(util.is_int(args.period), "indicators.trilight: period is a required field if flash is enabled")
end end
-- single line
args.height = 1 args.height = 1
-- determine width
args.width = math.max(args.min_label_width or 1, string.len(args.label)) + 2 args.width = math.max(args.min_label_width or 1, string.len(args.label)) + 2
-- flasher state
local flash_on = true
-- blit translations
local c1 = colors.toBlit(args.c1)
local c2 = colors.toBlit(args.c2)
local c3 = colors.toBlit(args.c3)
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) local e = element.new(args)
-- init value for initial check in on_update
e.value = 1 e.value = 1
local flash_on = true
local c1 = colors.toBlit(args.c1)
local c2 = colors.toBlit(args.c2)
local c3 = colors.toBlit(args.c3)
-- called by flasher when enabled -- called by flasher when enabled
local function flash_callback() local function flash_callback()
e.w_set_cur(1, 1) e.w_set_cur(1, 1)
@ -102,9 +96,14 @@ local function tristate_indicator_light(args)
---@param val integer indicator state ---@param val integer indicator state
function e.set_value(val) e.on_update(val) end function e.set_value(val) e.on_update(val) end
-- write label and initial indicator light -- draw light and label
e.on_update(1) function e.redraw()
e.w_write(args.label) e.on_update(1)
e.w_write(args.label)
end
-- initial draw
e.redraw()
return e.complete() return e.complete()
end end

View File

@ -20,13 +20,13 @@ local element = require("graphics.element")
---@param args vbar_args ---@param args vbar_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function vbar(args) local function vbar(args)
-- properties/state
local last_num_bars = -1
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) local e = element.new(args)
-- blit strings e.value = 0.0
local last_num_bars = -1
local fgd = string.rep(e.fg_bg.blit_fgd, e.frame.w) local fgd = string.rep(e.fg_bg.blit_fgd, e.frame.w)
local bkg = string.rep(e.fg_bg.blit_bkg, e.frame.w) local bkg = string.rep(e.fg_bg.blit_bkg, e.frame.w)
local spaces = util.spaces(e.frame.w) local spaces = util.spaces(e.frame.w)
@ -52,10 +52,7 @@ local function vbar(args)
if num_bars ~= last_num_bars then if num_bars ~= last_num_bars then
last_num_bars = num_bars last_num_bars = num_bars
-- start bottom up
local y = e.frame.h local y = e.frame.h
-- start at base of vertical bar
e.w_set_cur(1, y) e.w_set_cur(1, y)
-- fill percentage -- fill percentage
@ -83,22 +80,26 @@ local function vbar(args)
end end
end end
-- set the percentage value
---@param val number 0.0 to 1.0
function e.set_value(val) e.on_update(val) end
-- element redraw
function e.redraw()
last_num_bars = -1
e.on_update(e.value)
end
-- change bar color -- change bar color
---@param fg_bg cpair new bar colors ---@param fg_bg cpair new bar colors
function e.recolor(fg_bg) function e.recolor(fg_bg)
fgd = string.rep(fg_bg.blit_fgd, e.frame.w) fgd = string.rep(fg_bg.blit_fgd, e.frame.w)
bkg = string.rep(fg_bg.blit_bkg, e.frame.w) bkg = string.rep(fg_bg.blit_bkg, e.frame.w)
e.redraw()
-- re-draw
last_num_bars = 0
if type(e.value) == "number" then
e.on_update(e.value)
end
end end
-- set the percentage value -- initial draw
---@param val number 0.0 to 1.0 e.redraw()
function e.set_value(val) e.on_update(val) end
return e.complete() return e.complete()
end end

View File

@ -274,8 +274,20 @@ local function listbox(args)
end end
end end
draw_arrows(0) -- element redraw
draw_bar() function e.redraw()
draw_arrows(0)
draw_bar()
-- redraw all children
for i = 1, #list do
local item = list[i] ---@type listbox_item
item.e.redraw()
end
end
-- initial draw
e.redraw()
return e.complete() return e.complete()
end end

View File

@ -19,23 +19,30 @@ local element = require("graphics.element")
---@param args multipane_args ---@param args multipane_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function multipane(args) local function multipane(args)
assert(type(args.panes) == "table", "graphics.elements.multipane: panes is a required field") assert(type(args.panes) == "table", "multipane: panes is a required field")
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) local e = element.new(args)
e.value = 1
-- show the selected pane
function e.redraw()
for i = 1, #args.panes do args.panes[i].hide() end
args.panes[e.value].show()
end
-- select which pane is shown -- select which pane is shown
---@param value integer pane to show ---@param value integer pane to show
function e.set_value(value) function e.set_value(value)
if (e.value ~= value) and (value > 0) and (value <= #args.panes) then if (e.value ~= value) and (value > 0) and (value <= #args.panes) then
e.value = value e.value = value
e.redraw()
for i = 1, #args.panes do args.panes[i].hide() end
args.panes[value].show()
end end
end end
e.set_value(1) -- initial draw
e.redraw()
return e.complete() return e.complete()
end end

View File

@ -24,12 +24,11 @@ local element = require("graphics.element")
---@param args pipenet_args ---@param args pipenet_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function pipenet(args) local function pipenet(args)
assert(type(args.pipes) == "table", "graphics.elements.indicators.pipenet: pipes is a required field") assert(type(args.pipes) == "table", "pipenet: pipes is a required field")
args.width = 0 args.width = 0
args.height = 0 args.height = 0
-- determine width/height
for i = 1, #args.pipes do for i = 1, #args.pipes do
local pipe = args.pipes[i] ---@type pipe local pipe = args.pipes[i] ---@type pipe
@ -57,8 +56,8 @@ local function pipenet(args)
if any_thin then break end if any_thin then break end
end end
if not any_thin then -- draw all pipes by drawing out lines
-- draw all pipes local function vector_draw()
for p = 1, #args.pipes do for p = 1, #args.pipes do
local pipe = args.pipes[p] ---@type pipe local pipe = args.pipes[p] ---@type pipe
@ -161,11 +160,109 @@ local function pipenet(args)
end end
end end
end end
else end
-- build map if using thin pipes, easist way to check adjacent blocks (cannot 'cheat' like with standard width)
-- draw a particular map cell
---@param map table 2D cell map
---@param x integer x coord
---@param y integer y coord
local function draw_map_cell(map, x, y)
local entry = map[x][y] ---@type _pipe_map_entry already confirmed not false
local char
local invert = false
local function check(cx, cy)
return (map[cx] ~= nil) and (map[cx][cy] ~= nil) and (map[cx][cy] ~= false) and (map[cx][cy].fg == entry.fg)
end
if entry.thin then
if check(x - 1, y) then -- if left
if check(x, y - 1) then -- if above
if check(x + 1, y) then -- if right
if check(x, y + 1) then -- if below
char = util.trinary(entry.atr, "\x91", "\x9d")
invert = entry.atr
else -- not below
char = util.trinary(entry.atr, "\x8e", "\x8d")
end
else -- not right
if check(x, y + 1) then -- if below
char = util.trinary(entry.atr, "\x91", "\x95")
invert = entry.atr
else -- not below
char = util.trinary(entry.atr, "\x8e", "\x85")
end
end
elseif check(x, y + 1) then-- not above, if below
if check(x + 1, y) then -- if right
char = util.trinary(entry.atr, "\x93", "\x9c")
invert = entry.atr
else -- not right
char = util.trinary(entry.atr, "\x93", "\x94")
invert = entry.atr
end
else -- not above, not below
char = "\x8c"
end
elseif check(x + 1, y) then -- not left, if right
if check(x, y - 1) then -- if above
if check(x, y + 1) then -- if below
char = util.trinary(entry.atr, "\x95", "\x9d")
invert = entry.atr
else -- not below
char = util.trinary(entry.atr, "\x8a", "\x8d")
end
else -- not above
if check(x, y + 1) then -- if below
char = util.trinary(entry.atr, "\x97", "\x9c")
invert = entry.atr
else -- not below
char = "\x8c"
end
end
else -- not left, not right
char = "\x95"
invert = entry.atr
end
else
if check(x, y - 1) then -- above
-- not below and (if left or right)
if (not check(x, y + 1)) and (check(x - 1, y) or check(x + 1, y)) then
char = util.trinary(entry.atr, "\x8f", " ")
invert = not entry.atr
else -- not below w/ sides only
char = " "
invert = true
end
elseif check(x, y + 1) then -- not above, if below
-- if left or right
if (check(x - 1, y) or check(x + 1, y)) then
char = "\x83"
invert = true
else -- not left or right
char = " "
invert = true
end
else -- not above, not below
char = util.trinary(entry.atr, "\x8f", "\x83")
invert = not entry.atr
end
end
e.w_set_cur(x, y)
if invert then
e.w_blit(char, entry.bg, entry.fg)
else
e.w_blit(char, entry.fg, entry.bg)
end
end
-- draw all pipes by assembling and marking up a 2D map<br>
-- this is an easy way to check adjacent blocks, which is required to properly draw thin pipes
local function map_draw()
local map = {} local map = {}
-- allocate map
for x = 1, args.width do for x = 1, args.width do
table.insert(map, {}) table.insert(map, {})
for _ = 1, args.height do table.insert(map[x], false) end for _ = 1, args.height do table.insert(map[x], false) end
@ -215,101 +312,19 @@ local function pipenet(args)
-- render -- render
for x = 1, args.width do for x = 1, args.width do
for y = 1, args.height do for y = 1, args.height do
local entry = map[x][y] ---@type _pipe_map_entry|false if map[x][y] ~= false then draw_map_cell(map, x, y) end
local char
local invert = false
if entry ~= false then
local function check(cx, cy)
return (map[cx] ~= nil) and (map[cx][cy] ~= nil) and (map[cx][cy] ~= false) and (map[cx][cy].fg == entry.fg)
end
if entry.thin then
if check(x - 1, y) then -- if left
if check(x, y - 1) then -- if above
if check(x + 1, y) then -- if right
if check(x, y + 1) then -- if below
char = util.trinary(entry.atr, "\x91", "\x9d")
invert = entry.atr
else -- not below
char = util.trinary(entry.atr, "\x8e", "\x8d")
end
else -- not right
if check(x, y + 1) then -- if below
char = util.trinary(entry.atr, "\x91", "\x95")
invert = entry.atr
else -- not below
char = util.trinary(entry.atr, "\x8e", "\x85")
end
end
elseif check(x, y + 1) then-- not above, if below
if check(x + 1, y) then -- if right
char = util.trinary(entry.atr, "\x93", "\x9c")
invert = entry.atr
else -- not right
char = util.trinary(entry.atr, "\x93", "\x94")
invert = entry.atr
end
else -- not above, not below
char = "\x8c"
end
elseif check(x + 1, y) then -- not left, if right
if check(x, y - 1) then -- if above
if check(x, y + 1) then -- if below
char = util.trinary(entry.atr, "\x95", "\x9d")
invert = entry.atr
else -- not below
char = util.trinary(entry.atr, "\x8a", "\x8d")
end
else -- not above
if check(x, y + 1) then -- if below
char = util.trinary(entry.atr, "\x97", "\x9c")
invert = entry.atr
else -- not below
char = "\x8c"
end
end
else -- not left, not right
char = "\x95"
invert = entry.atr
end
else
if check(x, y - 1) then -- above
-- not below and (if left or right)
if (not check(x, y + 1)) and (check(x - 1, y) or check(x + 1, y)) then
char = util.trinary(entry.atr, "\x8f", " ")
invert = not entry.atr
else -- not below w/ sides only
char = " "
invert = true
end
elseif check(x, y + 1) then -- not above, if below
-- if left or right
if (check(x - 1, y) or check(x + 1, y)) then
char = "\x83"
invert = true
else -- not left or right
char = " "
invert = true
end
else -- not above, not below
char = util.trinary(entry.atr, "\x8f", "\x83")
invert = not entry.atr
end
end
e.w_set_cur(x, y)
if invert then
e.w_blit(char, entry.bg, entry.fg)
else
e.w_blit(char, entry.fg, entry.bg)
end
end
end end
end end
end end
-- element redraw
function e.redraw()
if any_thin then map_draw() else vector_draw() end
end
-- initial draw
e.redraw()
return e.complete() return e.complete()
end end

View File

@ -22,7 +22,7 @@ local element = require("graphics.element")
---@param args rectangle_args ---@param args rectangle_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function rectangle(args) local function rectangle(args)
assert(args.border ~= nil or args.thin ~= true, "graphics.elements.rectangle: thin requires border to be provided") assert(args.border ~= nil or args.thin ~= true, "rectangle: thin requires border to be provided")
-- if thin, then width will always need to be 1 -- if thin, then width will always need to be 1
if args.thin == true then if args.thin == true then
@ -65,8 +65,8 @@ local function rectangle(args)
local inner_width = e.frame.w - width_x2 local inner_width = e.frame.w - width_x2
-- check dimensions -- check dimensions
assert(width_x2 <= e.frame.w, "graphics.elements.rectangle: border too thick for width") assert(width_x2 <= e.frame.w, "rectangle: border too thick for width")
assert(width_x2 <= e.frame.h, "graphics.elements.rectangle: border too thick for height") assert(width_x2 <= e.frame.h, "rectangle: border too thick for height")
-- form the basic line strings and top/bottom blit strings -- form the basic line strings and top/bottom blit strings
local spaces = util.spaces(e.frame.w) local spaces = util.spaces(e.frame.w)
@ -126,64 +126,69 @@ local function rectangle(args)
end end
-- draw rectangle with borders -- draw rectangle with borders
for y = 1, e.frame.h do function e.redraw()
e.w_set_cur(1, y) for y = 1, e.frame.h do
-- top border e.w_set_cur(1, y)
if y <= border_height then -- top border
-- partial pixel fill if y <= border_height then
if args.border.even and y == border_height then -- partial pixel fill
if args.thin == true then if args.border.even and y == border_height then
e.w_blit(p_a, p_inv_bg, p_inv_fg) if args.thin == true then
else e.w_blit(p_a, p_inv_bg, p_inv_fg)
local _fg = util.trinary(args.even_inner == true, string.rep(e.fg_bg.blit_bkg, e.frame.w), p_inv_bg) else
local _bg = util.trinary(args.even_inner == true, blit_bg_top_bot, p_inv_fg) local _fg = util.trinary(args.even_inner == true, string.rep(e.fg_bg.blit_bkg, e.frame.w), p_inv_bg)
local _bg = util.trinary(args.even_inner == true, blit_bg_top_bot, p_inv_fg)
if width_x2 % 3 == 1 then if width_x2 % 3 == 1 then
e.w_blit(p_b, _fg, _bg) e.w_blit(p_b, _fg, _bg)
elseif width_x2 % 3 == 2 then elseif width_x2 % 3 == 2 then
e.w_blit(p_a, _fg, _bg) e.w_blit(p_a, _fg, _bg)
else else
-- skip line -- skip line
e.w_blit(spaces, blit_fg, blit_bg_sides) e.w_blit(spaces, blit_fg, blit_bg_sides)
end end
end
else
e.w_blit(spaces, blit_fg, blit_bg_top_bot)
end
-- bottom border
elseif y > (e.frame.h - border_width) then
-- partial pixel fill
if args.border.even and y == ((e.frame.h - border_width) + 1) then
if args.thin == true then
if args.even_inner == true then
e.w_blit(p_b, blit_bg_top_bot, string.rep(e.fg_bg.blit_bkg, e.frame.w))
else
e.w_blit(p_b, string.rep(e.fg_bg.blit_bkg, e.frame.w), blit_bg_top_bot)
end end
else else
local _fg = util.trinary(args.even_inner == true, blit_bg_top_bot, p_inv_fg) e.w_blit(spaces, blit_fg, blit_bg_top_bot)
local _bg = util.trinary(args.even_inner == true, string.rep(e.fg_bg.blit_bkg, e.frame.w), blit_bg_top_bot) end
-- bottom border
if width_x2 % 3 == 1 then elseif y > (e.frame.h - border_width) then
e.w_blit(p_a, _fg, _bg) -- partial pixel fill
elseif width_x2 % 3 == 2 then if args.border.even and y == ((e.frame.h - border_width) + 1) then
e.w_blit(p_b, _fg, _bg) if args.thin == true then
if args.even_inner == true then
e.w_blit(p_b, blit_bg_top_bot, string.rep(e.fg_bg.blit_bkg, e.frame.w))
else
e.w_blit(p_b, string.rep(e.fg_bg.blit_bkg, e.frame.w), blit_bg_top_bot)
end
else else
-- skip line local _fg = util.trinary(args.even_inner == true, blit_bg_top_bot, p_inv_fg)
e.w_blit(spaces, blit_fg, blit_bg_sides) local _bg = util.trinary(args.even_inner == true, string.rep(e.fg_bg.blit_bkg, e.frame.w), blit_bg_top_bot)
if width_x2 % 3 == 1 then
e.w_blit(p_a, _fg, _bg)
elseif width_x2 % 3 == 2 then
e.w_blit(p_b, _fg, _bg)
else
-- skip line
e.w_blit(spaces, blit_fg, blit_bg_sides)
end
end end
else
e.w_blit(spaces, blit_fg, blit_bg_top_bot)
end end
else else
e.w_blit(spaces, blit_fg, blit_bg_top_bot) if args.thin == true then
end e.w_blit(p_s, blit_fg_sides, blit_bg_sides)
else else
if args.thin == true then e.w_blit(p_s, blit_fg, blit_bg_sides)
e.w_blit(p_s, blit_fg_sides, blit_bg_sides) end
else
e.w_blit(p_s, blit_fg, blit_bg_sides)
end end
end end
end end
-- initial draw of border
e.redraw()
end end
return e.complete() return e.complete()

View File

@ -24,19 +24,20 @@ local TEXT_ALIGN = core.TEXT_ALIGN
---@param args textbox_args ---@param args textbox_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function textbox(args) local function textbox(args)
assert(type(args.text) == "string", "graphics.elements.textbox: text is a required field") assert(type(args.text) == "string", "textbox: text is a required field")
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) local e = element.new(args)
e.value = args.text
local alignment = args.alignment or TEXT_ALIGN.LEFT local alignment = args.alignment or TEXT_ALIGN.LEFT
-- draw textbox -- draw textbox
function e.redraw()
e.window.clear()
local function display_text(text) local lines = util.strwrap(e.value, e.frame.w)
e.value = text
local lines = util.strwrap(text, e.frame.w)
for i = 1, #lines do for i = 1, #lines do
if i > e.frame.h then break end if i > e.frame.h then break end
@ -56,15 +57,16 @@ local function textbox(args)
end end
end end
display_text(args.text)
-- set the string value and re-draw the text -- set the string value and re-draw the text
---@param val string value ---@param val string value
function e.set_value(val) function e.set_value(val)
e.window.clear() e.value = val
display_text(val) e.redraw()
end end
-- initial draw
e.redraw()
return e.complete() return e.complete()
end end

View File

@ -22,13 +22,11 @@ local element = require("graphics.element")
---@param args tiling_args ---@param args tiling_args
---@return graphics_element element, element_id id ---@return graphics_element element, element_id id
local function tiling(args) local function tiling(args)
assert(type(args.fill_c) == "table", "graphics.elements.tiling: fill_c is a required field") assert(type(args.fill_c) == "table", "tiling: fill_c is a required field")
-- create new graphics element base object -- create new graphics element base object
local e = element.new(args) local e = element.new(args)
-- draw tiling box
local fill_a = args.fill_c.blit_a local fill_a = args.fill_c.blit_a
local fill_b = args.fill_c.blit_b local fill_b = args.fill_c.blit_b
@ -38,13 +36,9 @@ local function tiling(args)
local start_y = 1 local start_y = 1
local inner_width = math.floor(e.frame.w / util.trinary(even, 2, 1)) local inner_width = math.floor(e.frame.w / util.trinary(even, 2, 1))
local inner_height = e.frame.h local inner_height = e.frame.h
local alternator = true
-- border -- border
if args.border_c ~= nil then if args.border_c ~= nil then
e.w_set_bkg(args.border_c)
e.window.clear()
start_x = 1 + util.trinary(even, 2, 1) start_x = 1 + util.trinary(even, 2, 1)
start_y = 2 start_y = 2
@ -53,35 +47,48 @@ local function tiling(args)
end end
-- check dimensions -- check dimensions
assert(inner_width > 0, "graphics.elements.tiling: inner_width <= 0") assert(inner_width > 0, "tiling: inner_width <= 0")
assert(inner_height > 0, "graphics.elements.tiling: inner_height <= 0") assert(inner_height > 0, "tiling: inner_height <= 0")
assert(start_x <= inner_width, "graphics.elements.tiling: start_x > inner_width") assert(start_x <= inner_width, "tiling: start_x > inner_width")
assert(start_y <= inner_height, "graphics.elements.tiling: start_y > inner_height") assert(start_y <= inner_height, "tiling: start_y > inner_height")
-- create pattern -- draw tiling box
for y = start_y, inner_height + (start_y - 1) do function e.redraw()
e.w_set_cur(start_x, y) local alternator = true
for _ = 1, inner_width do
if alternator then
if even then
e.w_blit(" ", "00", fill_a .. fill_a)
else
e.w_blit(" ", "0", fill_a)
end
else
if even then
e.w_blit(" ", "00", fill_b .. fill_b)
else
e.w_blit(" ", "0", fill_b)
end
end
alternator = not alternator if args.border_c ~= nil then
e.w_set_bkg(args.border_c)
e.window.clear()
end end
if inner_width % 2 == 0 then alternator = not alternator end -- draw pattern
for y = start_y, inner_height + (start_y - 1) do
e.w_set_cur(start_x, y)
for _ = 1, inner_width do
if alternator then
if even then
e.w_blit(" ", "00", fill_a .. fill_a)
else
e.w_blit(" ", "0", fill_a)
end
else
if even then
e.w_blit(" ", "00", fill_b .. fill_b)
else
e.w_blit(" ", "0", fill_b)
end
end
alternator = not alternator
end
if inner_width % 2 == 0 then alternator = not alternator end
end
end end
-- initial draw
e.redraw()
return e.complete() return e.complete()
end end