#184 initial draft of listbox element and associated supervisor front panel test example

This commit is contained in:
Mikayla Fischler 2023-05-31 11:44:41 -04:00
parent 4c35233289
commit ef1ec220a4
3 changed files with 204 additions and 7 deletions

View File

@ -45,6 +45,7 @@ local element = {}
---|colormap_args
---|displaybox_args
---|div_args
---|listbox_args
---|multipane_args
---|pipenet_args
---|rectangle_args
@ -66,7 +67,6 @@ function element.new(args)
define_completed = false,
p_window = nil, ---@type table
position = { x = 1, y = 1 }, ---@type coordinate_2d
child_offset = { x = 0, y = 0 }, ---@type coordinate_2d
bounds = { x1 = 1, y1 = 1, x2 = 1, y2 = 1 }, ---@class element_bounds
next_y = 1,
subscriptions = {},
@ -112,7 +112,7 @@ function element.new(args)
else
local w, h = self.p_window.getSize()
protected.frame.x = args.x or 1
protected.frame.y = args.y or next_y
protected.frame.y = args.y or next_y
protected.frame.w = args.width or w
protected.frame.h = args.height or h
end

View File

@ -0,0 +1,190 @@
-- Scroll-able List Box Display Graphics Element
-- local log = require("scada-common.log")
local core = require("graphics.core")
local element = require("graphics.element")
local CLICK_TYPE = core.events.CLICK_TYPE
---@class listbox_args
---@field scroll_height integer height of internal scrolling container (must fit all elements vertically tiled)
---@field item_pad? integer spacing (lines) between items in the list (default 0)
---@field parent graphics_element
---@field id? string element id
---@field x? integer 1 if omitted
---@field y? integer 1 if omitted
---@field width? integer parent width if omitted
---@field height? integer parent height if omitted
---@field gframe? graphics_frame frame instead of x/y/width/height
---@field fg_bg? cpair foreground/background colors
---@field hidden? boolean true to hide on initial draw
---@class listbox_item
---@field id string|integer element ID
---@field e graphics_element element
---@field y integer y position
---@field h integer element height
-- new listbox element
---@nodiscard
---@param args listbox_args
---@return graphics_element element, element_id id
local function listbox(args)
-- create new graphics element base object
local e = element.new(args)
-- create content window for child elements
local scroll_frame = window.create(e.window, 1, 1, e.frame.w - 1, args.scroll_height, false)
e.content_window = scroll_frame
-- item list and scroll management
local list = {}
local item_pad = args.item_pad or 0
local scroll_offset = 0
local content_height = 0
local max_down_scroll = 0
-- bar control/tracking variables
local bar_height = 0 -- full height of bar
local bar_bounds = { 0, 0 } -- top and bottom of bar
local holding_bar = false -- bar is being held by mouse
local bar_grip_pos = 0 -- where the bar was gripped by mouse down
local mouse_last_y = 0 -- last reported y coordinate of drag
-- draw up/down arrows
e.window.setCursorPos(e.frame.w, 1)
e.window.write("\x1e")
e.window.setCursorPos(e.frame.w, e.frame.h)
e.window.write("\x1f")
-- render the scroll bar and re-cacluate height & bounds
local function draw_bar()
local offset = 2 + math.abs(scroll_offset)
bar_height = math.max(math.min(e.frame.h - 2 + max_down_scroll, e.frame.h - 2), 1)
bar_bounds = { offset, (bar_height + offset) - 1 }
for i = 2, e.frame.h - 1 do
if i >= offset and i < (bar_height + offset) then
e.window.setBackgroundColor(e.fg_bg.fgd)
else
e.window.setBackgroundColor(e.fg_bg.bkg)
end
e.window.setCursorPos(e.frame.w, i)
e.window.write(" ")
end
e.window.setBackgroundColor(e.fg_bg.bkg)
end
-- update item y positions and move elements
local function update_positions()
local next_y = 1
scroll_frame.setVisible(false)
scroll_frame.setBackgroundColor(e.fg_bg.bkg)
scroll_frame.setTextColor(e.fg_bg.fgd)
scroll_frame.clear()
for i = 1, #list do
local item = list[i] ---@type listbox_item
item.y = next_y
next_y = next_y + item.h + item_pad
item.e.reposition(1, item.y)
item.e.show()
end
content_height = next_y
max_down_scroll = math.min(-1 * (content_height - (e.frame.h + 1 + item_pad)), 0)
if scroll_offset < max_down_scroll then scroll_offset = max_down_scroll end
scroll_frame.reposition(1, 1 + scroll_offset)
scroll_frame.setVisible(true)
draw_bar()
-- log.info("content_height[" .. content_height .. "] max_down_scroll[" .. max_down_scroll .. "] scroll_offset[" .. scroll_offset .. "] bar_height[" .. bar_height .. "]")
end
-- scroll down the list
local function scroll_down()
if scroll_offset > max_down_scroll then
scroll_offset = scroll_offset - 1
update_positions()
end
end
-- scroll up the list
local function scroll_up()
if scroll_offset < 0 then
scroll_offset = scroll_offset + 1
update_positions()
end
end
-- handle a child element having been added to the list
---@param id string|integer element identifier
---@param child graphics_element child element
function e.on_added(id, child)
table.insert(list, { id = id, e = child, y = 0, h = child.get_height() })
update_positions()
end
-- handle a child element having been removed from the list
---@param id string|integer element identifier
function e.on_removed(id)
for idx, elem in ipairs(list) do
if elem.id == id then
table.remove(list, idx)
update_positions()
return
end
end
end
-- handle mouse interaction
---@param event mouse_interaction mouse event
function e.handle_mouse(event)
if e.enabled then
if event.type == CLICK_TYPE.TAP or event.type == CLICK_TYPE.DOWN then
if event.current.x == e.frame.w then
if event.current.y == 1 or event.current.y < bar_bounds[1] then
scroll_up()
elseif event.current.y == e.frame.h or event.current.y > bar_bounds[2] then
scroll_down()
else
-- clicked on bar
holding_bar = true
bar_grip_pos = event.current.y - bar_bounds[1]
mouse_last_y = event.current.y
end
end
elseif event.type == CLICK_TYPE.UP then
holding_bar = false
elseif event.type == CLICK_TYPE.DRAG then
if holding_bar then
-- if mouse is within vertical frame, including the grip point
if event.current.y > (1 + bar_grip_pos) and event.current.y <= ((e.frame.h - bar_height) + bar_grip_pos) then
if event.current.y < mouse_last_y then
scroll_up()
elseif event.current.y > mouse_last_y then
scroll_down()
end
mouse_last_y = event.current.y
end
end
elseif event.type == CLICK_TYPE.SCROLL_DOWN then
scroll_down()
elseif event.type == CLICK_TYPE.SCROLL_UP then
scroll_up()
end
end
end
return e.complete()
end
return listbox

View File

@ -74,7 +74,7 @@ local function init(panel)
-- plc page
local plc_page = Div{parent=page_div,x=1,y=1}
local plc_page = Div{parent=page_div,x=1,y=1,hidden=true}
local plc_list = Div{parent=plc_page,x=2,y=2,width=49}
for i = 1, config.NUM_REACTORS do
@ -106,12 +106,19 @@ local function init(panel)
-- rtu page
local rtu_page = Div{parent=page_div,x=1,y=1}
local rtu_list = Div{parent=rtu_page,x=2,y=2,width=49}
local rtu_page = Div{parent=page_div,x=1,y=1,hidden=true}
local rtu_list = ListBox{parent=rtu_page,x=2,y=2,height=15,width=49,scroll_height=1000,item_pad=1,fg_bg=cpair(colors.black,colors.white)}
local item_1 = Div{parent=rtu_list,height=3,fg_bg=cpair(colors.black,colors.red),hidden=true}
local item_2 = Div{parent=rtu_list,height=3,fg_bg=cpair(colors.black,colors.orange),hidden=true}
local item_3 = Div{parent=rtu_list,height=3,fg_bg=cpair(colors.black,colors.yellow),hidden=true}
local item_4 = Div{parent=rtu_list,height=3,fg_bg=cpair(colors.black,colors.green),hidden=true}
local item_5 = Div{parent=rtu_list,height=3,fg_bg=cpair(colors.black,colors.blue),hidden=true}
local item_6 = Div{parent=rtu_list,height=3,fg_bg=cpair(colors.black,colors.purple),hidden=true}
-- coordinator page
local crd_page = Div{parent=page_div,x=1,y=1}
local crd_page = Div{parent=page_div,x=1,y=1,hidden=true}
local crd_box = Div{parent=crd_page,x=2,y=2,width=49,height=4,fg_bg=cpair(colors.black,colors.white)}
local crd_conn = LED{parent=crd_box,x=2,y=2,label="CONNECTION",colors=cpair(colors.green,colors.green_off)}
@ -133,7 +140,7 @@ local function init(panel)
-- pocket page
local pkt_page = Div{parent=page_div,x=1,y=1}
local pkt_page = Div{parent=page_div,x=1,y=1,hidden=true}
local pkt_box = Div{parent=pkt_page,x=2,y=2,width=49}
local panes = { main_page, plc_page, rtu_page, crd_page, pkt_page }