2022-12-13 20:18:29 +00:00
-- Multi Button Graphics Element
2022-08-16 15:22:58 +00:00
local util = require ( " scada-common.util " )
2023-05-10 15:08:24 +00:00
local core = require ( " graphics.core " )
2022-11-25 03:49:35 +00:00
local element = require ( " graphics.element " )
2022-08-16 15:22:58 +00:00
---@class button_option
---@field text string
---@field fg_bg cpair
---@field active_fg_bg cpair
---@field _start_x integer starting touch x range (inclusive)
---@field _end_x integer ending touch x range (inclusive)
---@class multi_button_args
---@field options table button options
---@field callback function function to call on touch
2022-12-13 20:18:29 +00:00
---@field default? integer default state, defaults to options[1]
2022-08-16 15:22:58 +00:00
---@field min_width? integer text length + 2 if omitted
---@field parent graphics_element
---@field id? string element id
---@field x? integer 1 if omitted
2023-07-10 03:42:44 +00:00
---@field y? integer auto incremented if omitted
2022-08-16 15:22:58 +00:00
---@field height? integer parent height if omitted
---@field fg_bg? cpair foreground/background colors
2023-05-25 21:40:16 +00:00
---@field hidden? boolean true to hide on initial draw
2022-08-16 15:22:58 +00:00
-- new multi button (latch selection, exclusively one button at a time)
---@param args multi_button_args
---@return graphics_element element, element_id id
local function multi_button ( args )
2023-09-29 23:34:10 +00:00
assert ( type ( args.options ) == " table " , " controls.multi_button: options is a required field " )
assert ( # args.options > 0 , " controls.multi_button: at least one option is required " )
assert ( type ( args.callback ) == " function " , " controls.multi_button: callback is a required field " )
assert ( type ( args.default ) == " nil " or ( type ( args.default ) == " number " and args.default > 0 ) , " controls.multi_button: default must be nil or a number > 0 " )
assert ( type ( args.min_width ) == " nil " or ( type ( args.min_width ) == " number " and args.min_width > 0 ) , " controls.multi_button: min_width must be nil or a number > 0 " )
2022-08-16 15:22:58 +00:00
-- single line
2022-12-11 04:56:07 +00:00
args.height = 1
2022-08-16 15:22:58 +00:00
-- determine widths
local max_width = 1
for i = 1 , # args.options do
local opt = args.options [ i ] ---@type button_option
if string.len ( opt.text ) > max_width then
max_width = string.len ( opt.text )
end
end
2022-12-13 20:18:29 +00:00
local button_width = math.max ( max_width , args.min_width or 0 )
2022-08-16 15:22:58 +00:00
args.width = ( button_width * # args.options ) + # args.options + 1
-- create new graphics element base object
local e = element.new ( args )
2022-09-12 16:59:28 +00:00
-- button state (convert nil to 1 if missing)
e.value = args.default or 1
2022-08-16 15:22:58 +00:00
-- calculate required button information
local next_x = 2
for i = 1 , # args.options do
local opt = args.options [ i ] ---@type button_option
opt._start_x = next_x
opt._end_x = next_x + button_width - 1
next_x = next_x + ( button_width + 1 )
end
-- show the button state
2023-09-29 23:34:10 +00:00
function e . redraw ( )
2022-08-16 15:22:58 +00:00
for i = 1 , # args.options do
local opt = args.options [ i ] ---@type button_option
2023-08-31 01:11:57 +00:00
e.w_set_cur ( opt._start_x , 1 )
2022-08-16 15:22:58 +00:00
2022-09-12 16:59:28 +00:00
if e.value == i then
2022-08-16 15:22:58 +00:00
-- show as pressed
2023-08-31 01:11:57 +00:00
e.w_set_fgd ( opt.active_fg_bg . fgd )
e.w_set_bkg ( opt.active_fg_bg . bkg )
2022-08-16 15:22:58 +00:00
else
-- show as unpressed
2023-08-31 01:11:57 +00:00
e.w_set_fgd ( opt.fg_bg . fgd )
e.w_set_bkg ( opt.fg_bg . bkg )
2022-08-16 15:22:58 +00:00
end
2023-08-31 01:11:57 +00:00
e.w_write ( util.pad ( opt.text , button_width ) )
2022-08-16 15:22:58 +00:00
end
end
2023-05-10 15:08:24 +00:00
-- check which button a given x is within
---@return integer|nil button index or nil if not within a button
local function which_button ( x )
for i = 1 , # args.options do
local opt = args.options [ i ] ---@type button_option
if x >= opt._start_x and x <= opt._end_x then return i end
end
return nil
end
2023-04-09 01:33:54 +00:00
-- handle mouse interaction
---@param event mouse_interaction mouse event
function e . handle_mouse ( event )
2023-05-10 15:08:24 +00:00
-- if enabled and the button row was pressed...
2023-05-11 23:55:02 +00:00
if e.enabled and core.events . was_clicked ( event.type ) then
2023-05-10 15:08:24 +00:00
-- a button may have been pressed, which one was it?
local button_ini = which_button ( event.initial . x )
local button_cur = which_button ( event.current . x )
-- mouse up must always have started with a mouse down on the same button to count as a click
-- tap always has identical coordinates, so this always passes for taps
if button_ini == button_cur and button_cur ~= nil then
e.value = button_cur
2023-09-29 23:34:10 +00:00
e.redraw ( )
2023-05-10 15:08:24 +00:00
args.callback ( e.value )
2022-08-16 15:22:58 +00:00
end
end
end
2022-09-12 16:59:28 +00:00
-- set the value
---@param val integer new value
function e . set_value ( val )
e.value = val
2023-09-29 23:34:10 +00:00
e.redraw ( )
2022-09-12 16:59:28 +00:00
end
-- initial draw
2023-09-29 23:34:10 +00:00
e.redraw ( )
2022-09-12 16:59:28 +00:00
2023-05-30 23:51:10 +00:00
return e.complete ( )
2022-08-16 15:22:58 +00:00
end
return multi_button