mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
196 lines
6.2 KiB
Lua
196 lines
6.2 KiB
Lua
-- Spinbox Numeric Graphics Element
|
|
|
|
local util = require("scada-common.util")
|
|
|
|
local core = require("graphics.core")
|
|
local element = require("graphics.element")
|
|
|
|
---@class spinbox_args
|
|
---@field default? number default value, defaults to 0.0
|
|
---@field min? number default 0, currently must be 0 or greater
|
|
---@field max? number default max number that can be displayed with the digits configuration
|
|
---@field whole_num_precision integer number of whole number digits
|
|
---@field fractional_precision integer number of fractional digits
|
|
---@field arrow_fg_bg cpair arrow foreground/background colors
|
|
---@field arrow_disable? color color when disabled (default light gray)
|
|
---@field parent graphics_element
|
|
---@field id? string element id
|
|
---@field x? integer 1 if omitted
|
|
---@field y? integer auto incremented if omitted
|
|
---@field fg_bg? cpair foreground/background colors
|
|
---@field hidden? boolean true to hide on initial draw
|
|
|
|
-- new spinbox control (minimum value is 0)
|
|
---@param args spinbox_args
|
|
---@return graphics_element element, element_id id
|
|
local function spinbox(args)
|
|
-- properties
|
|
local digits = {}
|
|
local wn_prec = args.whole_num_precision
|
|
local fr_prec = args.fractional_precision
|
|
|
|
assert(util.is_int(wn_prec), "graphics.element.controls.spinbox_numeric: whole number precision must be an integer")
|
|
assert(util.is_int(fr_prec), "graphics.element.controls.spinbox_numeric: fractional precision must be an integer")
|
|
|
|
local fmt, fmt_init ---@type string, string
|
|
|
|
if fr_prec > 0 then
|
|
fmt = "%" .. (wn_prec + fr_prec + 1) .. "." .. fr_prec .. "f"
|
|
fmt_init = "%0" .. (wn_prec + fr_prec + 1) .. "." .. fr_prec .. "f"
|
|
else
|
|
fmt = "%" .. wn_prec .. "d"
|
|
fmt_init = "%0" .. wn_prec .. "d"
|
|
end
|
|
|
|
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")
|
|
|
|
-- determine widths
|
|
args.width = wn_prec + fr_prec + util.trinary(fr_prec > 0, 1, 0)
|
|
args.height = 3
|
|
|
|
-- create new graphics element base object
|
|
local e = element.new(args)
|
|
|
|
-- set initial value
|
|
e.value = args.default or 0
|
|
|
|
-- draw the arrows
|
|
local function draw_arrows(color)
|
|
e.window.setBackgroundColor(args.arrow_fg_bg.bkg)
|
|
e.window.setTextColor(color)
|
|
e.window.setCursorPos(1, 1)
|
|
e.window.write(util.strrep("\x1e", wn_prec))
|
|
e.window.setCursorPos(1, 3)
|
|
e.window.write(util.strrep("\x1f", wn_prec))
|
|
if fr_prec > 0 then
|
|
e.window.setCursorPos(1 + wn_prec, 1)
|
|
e.window.write(" " .. util.strrep("\x1e", fr_prec))
|
|
e.window.setCursorPos(1 + wn_prec, 3)
|
|
e.window.write(" " .. util.strrep("\x1f", fr_prec))
|
|
end
|
|
end
|
|
|
|
draw_arrows(args.arrow_fg_bg.fgd)
|
|
|
|
-- populate digits from current value
|
|
local function set_digits()
|
|
local initial_str = util.sprintf(fmt_init, e.value)
|
|
|
|
digits = {}
|
|
---@diagnostic disable-next-line: discard-returns
|
|
initial_str:gsub("%d", function (char) table.insert(digits, char) end)
|
|
end
|
|
|
|
-- update the value per digits table
|
|
local function update_value()
|
|
e.value = 0
|
|
for i = 1, #digits do
|
|
local pow = math.abs(wn_prec - i)
|
|
if i <= wn_prec then
|
|
e.value = e.value + (digits[i] * (10 ^ pow))
|
|
else
|
|
e.value = e.value + (digits[i] * (10 ^ -pow))
|
|
end
|
|
end
|
|
end
|
|
|
|
-- print out the current value
|
|
local function show_num()
|
|
-- enforce limits
|
|
if (type(args.min) == "number") and (e.value < args.min) then
|
|
e.value = args.min
|
|
set_digits()
|
|
elseif e.value < 0 then
|
|
e.value = 0
|
|
set_digits()
|
|
else
|
|
if string.len(util.sprintf(fmt, e.value)) > args.width then
|
|
-- max printable exceeded, so max out to all 9s
|
|
for i = 1, #digits do digits[i] = 9 end
|
|
update_value()
|
|
elseif (type(args.max) == "number") and (e.value > args.max) then
|
|
e.value = args.max
|
|
set_digits()
|
|
else
|
|
set_digits()
|
|
end
|
|
end
|
|
|
|
-- draw
|
|
e.window.setBackgroundColor(e.fg_bg.bkg)
|
|
e.window.setTextColor(e.fg_bg.fgd)
|
|
e.window.setCursorPos(1, 2)
|
|
e.window.write(util.sprintf(fmt, e.value))
|
|
end
|
|
|
|
-- init with the default value
|
|
show_num()
|
|
|
|
-- handle mouse interaction
|
|
---@param event mouse_interaction mouse event
|
|
function e.handle_mouse(event)
|
|
-- only handle if on an increment or decrement arrow
|
|
if e.enabled and core.events.was_clicked(event.type) and
|
|
(event.current.x ~= dec_point_x) and (event.current.y ~= 2) then
|
|
if event.current.x == event.initial.x and event.current.y == event.initial.y then
|
|
local idx = util.trinary(event.current.x > dec_point_x, event.current.x - 1, event.current.x)
|
|
if digits[idx] ~= nil then
|
|
if event.current.y == 1 then
|
|
-- increment
|
|
digits[idx] = digits[idx] + 1
|
|
elseif event.current.y == 3 then
|
|
-- decrement
|
|
digits[idx] = digits[idx] - 1
|
|
end
|
|
|
|
update_value()
|
|
show_num()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- set the value
|
|
---@param val number number to show
|
|
function e.set_value(val)
|
|
e.value = val
|
|
show_num()
|
|
end
|
|
|
|
-- set minimum input value
|
|
---@param min integer minimum allowed value
|
|
function e.set_min(min)
|
|
if min >= 0 then
|
|
args.min = min
|
|
show_num()
|
|
end
|
|
end
|
|
|
|
-- set maximum input value
|
|
---@param max integer maximum allowed value
|
|
function e.set_max(max)
|
|
args.max = max
|
|
show_num()
|
|
end
|
|
|
|
-- enable this input
|
|
function e.enable()
|
|
draw_arrows(args.arrow_fg_bg.fgd)
|
|
end
|
|
|
|
-- disable this input
|
|
function e.disable()
|
|
draw_arrows(args.arrow_disable or colors.lightGray)
|
|
end
|
|
|
|
-- default to zero, init digits table
|
|
e.value = 0
|
|
set_digits()
|
|
|
|
return e.complete()
|
|
end
|
|
|
|
return spinbox
|