mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
191 lines
6.6 KiB
Lua
191 lines
6.6 KiB
Lua
|
-- 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
|