graphics library refactoring and bugfixes

This commit is contained in:
Mikayla Fischler 2022-06-16 11:19:32 -04:00
parent b628472d81
commit 971657c3d2
10 changed files with 85 additions and 49 deletions

View File

@ -3,6 +3,8 @@
-- --
local core = require("graphics.core") local core = require("graphics.core")
local log = require("scada-common.log")
local util = require("scada-common.util")
local element = {} local element = {}
@ -20,11 +22,15 @@ local element = {}
---@param args graphics_args_generic arguments ---@param args graphics_args_generic arguments
function element.new(args) function element.new(args)
local self = { local self = {
elem_type = debug.getinfo(2).name,
p_window = nil, ---@type table p_window = nil, ---@type table
position = { x = 1, y = 1 }, position = { x = 1, y = 1 },
bounds = { x1 = 1, y1 = 1, x2 = 1, y2 = 1} bounds = { x1 = 1, y1 = 1, x2 = 1, y2 = 1}
} }
---@fixme remove debug
log.dmesg("new " .. self.elem_type)
local protected = { local protected = {
window = nil, ---@type table window = nil, ---@type table
fg_bg = core.graphics.cpair(colors.white, colors.black), fg_bg = core.graphics.cpair(colors.white, colors.black),
@ -34,7 +40,10 @@ function element.new(args)
-- SETUP -- -- SETUP --
-- get the parent window -- get the parent window
self.p_window = args.window or args.parent.window() self.p_window = args.window
if self.p_window == nil and args.parent ~= nil then
self.p_window = args.parent.window()
end
-- check window -- check window
assert(self.p_window, "graphics.element: no parent window provided") assert(self.p_window, "graphics.element: no parent window provided")
@ -63,14 +72,18 @@ function element.new(args)
local f = protected.frame local f = protected.frame
protected.window = window.create(self.p_window, f.x, f.y, f.w, f.h, true) protected.window = window.create(self.p_window, f.x, f.y, f.w, f.h, true)
-- init display box -- init colors
if args.fg_bg ~= nil then if args.fg_bg ~= nil then
protected.window.setBackgroundColor(args.fg_bg.bkg)
protected.window.setTextColor(args.fg_bg.fgd)
protected.window.clear()
protected.fg_bg = args.fg_bg protected.fg_bg = args.fg_bg
elseif args.parent ~= nil then
protected.fg_bg = args.parent.get_fg_bg()
end end
-- set colors
protected.window.setBackgroundColor(protected.fg_bg.bkg)
protected.window.setTextColor(protected.fg_bg.fgd)
protected.window.clear()
-- record position -- record position
self.position.x, self.position.y = protected.window.getPosition() self.position.x, self.position.y = protected.window.getPosition()
@ -107,6 +120,9 @@ function element.new(args)
-- get the window object -- get the window object
function public.window() return protected.window end function public.window() return protected.window end
-- get the foreground/background colors
function public.get_fg_bg() return protected.fg_bg end
-- handle a monitor touch -- handle a monitor touch
---@param event monitor_touch monitor touch event ---@param event monitor_touch monitor touch event
function public.handle_touch(event) function public.handle_touch(event)

View File

@ -18,8 +18,11 @@ local element = require("graphics.element")
-- new push button -- new push button
---@param args push_button_args ---@param args push_button_args
local function push_button(args) local function push_button(args)
assert(type(args.text) == "string", "graphics.elements.button_push: text is a required field") assert(type(args.text) == "string", "graphics.elements.controls.push_button: text is a required field")
assert(type(args.callback) == "function", "graphics.elements.button_push: callback is a required field") assert(type(args.callback) == "function", "graphics.elements.controls.push_button: callback is a required field")
-- single line
args.height = 1
local text_width = string.len(args.text) local text_width = string.len(args.text)
args.width = math.max(text_width + 2, args.min_width) args.width = math.max(text_width + 2, args.min_width)

View File

@ -23,8 +23,8 @@ local function spinbox(args)
local fr_prec = args.fractional_precision local fr_prec = args.fractional_precision
local dec_point_x = args.whole_num_precision + 1 local dec_point_x = args.whole_num_precision + 1
assert(util.is_int(wn_prec), "graphics.element.spinbox_numeric: whole number precision must be an integer") 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.spinbox_numeric: fractional precision must be an integer") assert(util.is_int(fr_prec), "graphics.element.controls.spinbox_numeric: fractional precision must be an integer")
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", "graphics.element.spinbox_numeric: arrow_fg_bg is a required field")

View File

@ -17,9 +17,12 @@ local element = require("graphics.element")
-- new switch button (latch high/low) -- new switch button (latch high/low)
---@param args switch_button_args ---@param args switch_button_args
local function switch_button(args) local function switch_button(args)
assert(type(args.text) == "string", "graphics.elements.button_switch: text is a required field") assert(type(args.text) == "string", "graphics.elements.controls.switch_button: text is a required field")
assert(type(args.callback) == "function", "graphics.elements.button_switch: callback 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.button_switch: active_fg_bg is a required field") assert(type(args.active_fg_bg) == "table", "graphics.elements.controls.switch_button: active_fg_bg is a required field")
-- single line
args.height = 1
-- button state (convert nil to false if missing) -- button state (convert nil to false if missing)
local state = args.default or false local state = args.default or false

View File

@ -8,8 +8,8 @@ local element = require("graphics.element")
---@field label string indicator label ---@field label string indicator label
---@field unit? string indicator unit ---@field unit? string indicator unit
---@field format string data format (lua string format) ---@field format string data format (lua string format)
---@field label_unit_colors? cpair label foreground color (a), unit foreground color (b) ---@field lu_colors? cpair label foreground color (a), unit foreground color (b)
---@field initial_value any default value ---@field value any default value
---@field parent graphics_element ---@field parent graphics_element
---@field x? integer 1 if omitted ---@field x? integer 1 if omitted
---@field y? integer 1 if omitted ---@field y? integer 1 if omitted
@ -18,18 +18,21 @@ local element = require("graphics.element")
-- new data indicator -- new data indicator
---@param args data_indicator_args ---@param args data_indicator_args
local function data_indicator(args) local function data(args)
assert(type(args.label) == "string", "graphics.elements.indicator_data: label is a required field") assert(type(args.label) == "string", "graphics.elements.indicators.data: label is a required field")
assert(type(args.format) == "string", "graphics.elements.indicator_data: format is a required field") assert(type(args.format) == "string", "graphics.elements.indicators.data: format is a required field")
assert(args.initial_value ~= nil, "graphics.elements.indicator_data: initial_value is a required field") assert(args.value ~= nil, "graphics.elements.indicators.data: value is a required field")
assert(util.is_int(args.width), "graphics.elements.indicator_data: width is a required field") assert(util.is_int(args.width), "graphics.elements.indicators.data: width is a required field")
-- single line
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 -- label color
if args.label_unit_colors ~= nil then if args.lu_colors ~= nil then
e.window.setTextColor(args.label_unit_colors.color_a) e.window.setTextColor(args.lu_colors.color_a)
end end
-- write label -- write label
@ -50,17 +53,17 @@ local function data_indicator(args)
-- write label -- write label
if args.unit ~= nil then if args.unit ~= nil then
if args.label_unit_colors ~= nil then if args.lu_colors ~= nil then
e.window.setTextColor(args.label_unit_colors.color_b) e.window.setTextColor(args.lu_colors.color_b)
end end
e.window.write(" " .. args.unit) e.window.write(" " .. args.unit)
end end
end end
-- initial value draw -- initial value draw
e.on_update(args.initial_value) e.on_update(args.value)
return e.get() return e.get()
end end
return data_indicator return data

View File

@ -19,7 +19,6 @@ local element = require("graphics.element")
---@param args hbar_args ---@param args hbar_args
local function hbar(args) local function hbar(args)
-- properties/state -- properties/state
local bkg = ""
local last_num_bars = -1 local last_num_bars = -1
-- create new graphics element base object -- create new graphics element base object
@ -31,12 +30,10 @@ local function hbar(args)
assert(bar_width > 0, "graphics.elements.hbar: too small for bar") assert(bar_width > 0, "graphics.elements.hbar: too small for bar")
-- determine bar colors -- determine bar colors
---@fixme this doesnt work as intended
local bar_bkg = util.trinary(args.bar_fg_bg == nil, e.fg_bg.blit_bkg, args.bar_fg_bg.blit_bkg) local bar_bkg = util.trinary(args.bar_fg_bg == nil, e.fg_bg.blit_bkg, args.bar_fg_bg.blit_bkg)
local bar_fgd = util.trinary(args.bar_fg_bg == nil, e.fg_bg.blit_fgd, args.bar_fg_bg.blit_fgd) local bar_fgd = util.trinary(args.bar_fg_bg == nil, e.fg_bg.blit_fgd, args.bar_fg_bg.blit_fgd)
-- set background blit string
bkg = util.strrep(args.bar_fg_bg.blit_bkg, bar_width)
-- handle data changes -- handle data changes
function e.on_update(fraction) function e.on_update(fraction)
-- enforce minimum and maximum -- enforce minimum and maximum
@ -47,37 +44,43 @@ local function hbar(args)
end end
-- compute number of bars -- compute number of bars
local num_bars = util.round((fraction * 100) / (bar_width * 2)) local num_bars = util.round(fraction * (bar_width * 2))
util.print(num_bars)
-- redraw bar if changed -- redraw bar if changed
if num_bars ~= last_num_bars then if num_bars ~= last_num_bars then
last_num_bars = num_bars last_num_bars = num_bars
local fgd = "" local fgd = ""
local bkg = ""
local spaces = "" local spaces = ""
-- fill percentage -- fill percentage
for _ = 1, num_bars / 2 do for _ = 1, num_bars / 2 do
spaces = spaces .. " " spaces = spaces .. " "
fgd = fgd .. bar_fgd fgd = fgd .. bar_fgd
bkg = bkg .. bar_bkg
end end
-- add fractional bar if needed -- add fractional bar if needed
if num_bars % 2 == 1 then if num_bars % 2 == 1 then
spaces = spaces .. "\x95" spaces = spaces .. "\x95"
fgd = fgd .. bar_fgd fgd = fgd .. bar_bkg
bkg = bkg .. bar_fgd
end end
-- pad background -- pad background
for _ = 1, bar_width - ((num_bars / 2) + num_bars % 2) do for _ = 1, ((bar_width * 2) - num_bars) / 2 do
spaces = spaces .. " " spaces = spaces .. " "
fgd = fgd .. bar_bkg fgd = fgd .. bar_bkg
bkg = bkg .. bar_bkg
end end
-- draw bar -- draw bar
for y = 1, e.frame.h do for y = 1, e.frame.h do
e.window.setCursorPos(1, y) e.window.setCursorPos(1, y)
e.window.blit(spaces, fgd, bkg) -- intentionally swapped fgd/bkg since we use spaces as fill, but they are the opposite
e.window.blit(spaces, bkg, fgd)
end end
end end

View File

@ -11,7 +11,7 @@ local element = require("graphics.element")
---@class icon_indicator_args ---@class icon_indicator_args
---@field label string indicator label ---@field label string indicator label
---@field states table state color and symbol table ---@field states table state color and symbol table
---@field default? integer default state, defaults to 1 ---@field value? integer default state, defaults to 1
---@field min_label_width? integer label length if omitted ---@field min_label_width? integer label length if omitted
---@field parent graphics_element ---@field parent graphics_element
---@field x? integer 1 if omitted ---@field x? integer 1 if omitted
@ -20,9 +20,12 @@ local element = require("graphics.element")
-- new icon indicator -- new icon indicator
---@param args icon_indicator_args ---@param args icon_indicator_args
local function icon_indicator(args) local function icon(args)
assert(type(args.label) == "string", "graphics.elements.indicator_icon: label is a required field") assert(type(args.label) == "string", "graphics.elements.indicators.icon: label is a required field")
assert(type(args.states) == "table", "graphics.elements.indicator_icon: states is a required field") assert(type(args.states) == "table", "graphics.elements.indicators.icon: states is a required field")
-- single line
args.height = 1
-- determine width -- 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
@ -55,9 +58,9 @@ local function icon_indicator(args)
end end
-- initial icon draw -- initial icon draw
e.on_update(args.default or 1) e.on_update(args.value or 1)
return e.get() return e.get()
end end
return icon_indicator return icon

View File

@ -16,8 +16,11 @@ local element = require("graphics.element")
-- new indicator light -- new indicator light
---@param args indicator_light_args ---@param args indicator_light_args
local function indicator_light(args) local function indicator_light(args)
assert(type(args.label) == "string", "graphics.elements.indicator_light: label is a required field") assert(type(args.label) == "string", "graphics.elements.indicators.light: label is a required field")
assert(type(args.colors) == "table", "graphics.elements.indicator_light: colors is a required field") assert(type(args.colors) == "table", "graphics.elements.indicators.light: colors is a required field")
-- single line
args.height = 1
-- determine width -- determine width
args.width = math.max(args.min_label_width or 1, string.len(args.label)) + 3 args.width = math.max(args.min_label_width or 1, string.len(args.label)) + 3

View File

@ -10,7 +10,7 @@ local element = require("graphics.element")
---@class state_indicator_args ---@class state_indicator_args
---@field states table state color and text table ---@field states table state color and text table
---@field default? integer default state, defaults to 1 ---@field value? integer default state, defaults to 1
---@field min_width? integer max state text length if omitted ---@field min_width? integer max state text length if omitted
---@field parent graphics_element ---@field parent graphics_element
---@field x? integer 1 if omitted ---@field x? integer 1 if omitted
@ -21,11 +21,11 @@ local element = require("graphics.element")
-- new state indicator -- new state indicator
---@param args state_indicator_args ---@param args state_indicator_args
local function state_indicator(args) local function state_indicator(args)
assert(type(args.states) == "table", "graphics.elements.indicator_state: states is a required field") assert(type(args.states) == "table", "graphics.elements.indicators.state: states is a required field")
-- determine height -- determine height
if util.is_int(args.height) then if util.is_int(args.height) then
assert(args.height % 2 == 1, "graphics.elements.indicator_state: height should be an odd number") assert(args.height % 2 == 1, "graphics.elements.indicators.state: height should be an odd number")
else else
args.height = 1 args.height = 1
end end
@ -45,12 +45,14 @@ local function state_indicator(args)
local len = string.len(state_def.text) local len = string.len(state_def.text)
local lpad = math.floor((args.width - len) / 2) local lpad = math.floor((args.width - len) / 2)
local rpad = len - lpad local rpad = args.width - lpad
local text = util.spaces(lpad) .. state_def.text .. util.spaces(rpad)
table.insert(state_blit_cmds, { table.insert(state_blit_cmds, {
text = util.spaces(lpad) .. state_def.text .. util.spaces(rpad), text = text,
fgd = util.strrep(state_def.color.blit_fgd, 3), fgd = util.strrep(state_def.color.blit_fgd, string.len(text)),
bkg = util.strrep(state_def.color.blit_bkg, 3) bkg = util.strrep(state_def.color.blit_bkg, string.len(text))
}) })
end end
@ -66,7 +68,7 @@ local function state_indicator(args)
end end
-- initial draw -- initial draw
e.on_update(args.default or 1) e.on_update(args.value or 1)
return e.get() return e.get()
end end