mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
listbox improvements
This commit is contained in:
parent
ef1ec220a4
commit
153a83e569
@ -62,7 +62,7 @@ local element = {}
|
||||
---@param args graphics_args arguments
|
||||
function element.new(args)
|
||||
local self = {
|
||||
id = -1,
|
||||
id = nil, ---@type element_id|nil
|
||||
elem_type = debug.getinfo(2).name,
|
||||
define_completed = false,
|
||||
p_window = nil, ---@type table
|
||||
@ -179,13 +179,13 @@ function element.new(args)
|
||||
---@diagnostic disable: unused-local, unused-vararg
|
||||
|
||||
-- handle a child element having been added
|
||||
---@param id string|integer element identifier
|
||||
---@param id element_id element identifier
|
||||
---@param child graphics_element child element
|
||||
function protected.on_added(id, child)
|
||||
end
|
||||
|
||||
-- handle a child element having been removed
|
||||
---@param id string|integer element identifier
|
||||
---@param id element_id element identifier
|
||||
function protected.on_removed(id)
|
||||
end
|
||||
|
||||
@ -282,6 +282,7 @@ function element.new(args)
|
||||
|
||||
-- prepare the template
|
||||
if args.parent == nil then
|
||||
self.id = args.id or "__ROOT__"
|
||||
protected.prepare_template(1)
|
||||
else
|
||||
self.id = args.parent.__add_child(args.id, protected)
|
||||
@ -321,6 +322,8 @@ function element.new(args)
|
||||
v.delete()
|
||||
protected.children[k] = nil
|
||||
end
|
||||
|
||||
args.parent.__remove_child(self.id)
|
||||
end
|
||||
|
||||
-- ELEMENT TREE --
|
||||
@ -346,8 +349,17 @@ function element.new(args)
|
||||
end
|
||||
end
|
||||
|
||||
-- remove a child element
|
||||
---@param key element_id id
|
||||
function public.__remove_child(key)
|
||||
if protected.children[key] ~= nil then
|
||||
protected.on_removed(key)
|
||||
protected.children[key] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- actions to take upon a child element becoming ready (initial draw/construction completed)
|
||||
---@param key string|integer id
|
||||
---@param key element_id id
|
||||
---@param child graphics_element
|
||||
function public.__child_ready(key, child)
|
||||
protected.on_added(key, child)
|
||||
@ -364,8 +376,8 @@ function element.new(args)
|
||||
function public.remove(id)
|
||||
if protected.children[id] ~= nil then
|
||||
protected.children[id].delete()
|
||||
protected.children[id] = nil
|
||||
protected.on_removed(id)
|
||||
protected.children[id] = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
-- Scroll-able List Box Display Graphics Element
|
||||
|
||||
-- local log = require("scada-common.log")
|
||||
local tcd = require("scada-common.tcallbackdsp")
|
||||
|
||||
local core = require("graphics.core")
|
||||
local element = require("graphics.element")
|
||||
@ -10,6 +10,8 @@ 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 nav_fg_bg? cpair foreground/background colors for scroll arrows and bar area
|
||||
---@field nav_active? cpair active colors for bar held down or arrow held down
|
||||
---@field parent graphics_element
|
||||
---@field id? string element id
|
||||
---@field x? integer 1 if omitted
|
||||
@ -39,37 +41,90 @@ local function listbox(args)
|
||||
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 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
|
||||
local max_bar_height = e.frame.h - 2
|
||||
local bar_height = 0 -- full height of bar
|
||||
local bar_bounds = { 0, 0 } -- top and bottom of bar
|
||||
local bar_is_scaled = false -- if the scrollbar doesn't have a 1:1 ratio with lines
|
||||
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")
|
||||
-- draw scroll bar arrows, optionally showing one of them as pressed
|
||||
---@param pressed_arrow? integer arrow to show as pressed (1 = scroll up, 0 = neither, -1 = scroll down)
|
||||
local function draw_arrows(pressed_arrow)
|
||||
local nav_fg_bg = args.nav_fg_bg or e.fg_bg
|
||||
local active_fg_bg = args.nav_active or nav_fg_bg
|
||||
|
||||
-- draw up/down arrows
|
||||
if pressed_arrow == 1 then
|
||||
e.window.setTextColor(active_fg_bg.fgd)
|
||||
e.window.setBackgroundColor(active_fg_bg.bkg)
|
||||
e.window.setCursorPos(e.frame.w, 1)
|
||||
e.window.write("\x1e")
|
||||
e.window.setTextColor(nav_fg_bg.fgd)
|
||||
e.window.setBackgroundColor(nav_fg_bg.bkg)
|
||||
e.window.setCursorPos(e.frame.w, e.frame.h)
|
||||
e.window.write("\x1f")
|
||||
elseif pressed_arrow == -1 then
|
||||
e.window.setTextColor(nav_fg_bg.fgd)
|
||||
e.window.setBackgroundColor(nav_fg_bg.bkg)
|
||||
e.window.setCursorPos(e.frame.w, 1)
|
||||
e.window.write("\x1e")
|
||||
e.window.setTextColor(active_fg_bg.fgd)
|
||||
e.window.setBackgroundColor(active_fg_bg.bkg)
|
||||
e.window.setCursorPos(e.frame.w, e.frame.h)
|
||||
e.window.write("\x1f")
|
||||
else
|
||||
e.window.setTextColor(nav_fg_bg.fgd)
|
||||
e.window.setBackgroundColor(nav_fg_bg.bkg)
|
||||
e.window.setCursorPos(e.frame.w, 1)
|
||||
e.window.write("\x1e")
|
||||
e.window.setCursorPos(e.frame.w, e.frame.h)
|
||||
e.window.write("\x1f")
|
||||
end
|
||||
|
||||
e.window.setTextColor(e.fg_bg.fgd)
|
||||
e.window.setBackgroundColor(e.fg_bg.bkg)
|
||||
end
|
||||
|
||||
-- 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_height = math.min(max_bar_height + max_down_scroll, max_bar_height)
|
||||
|
||||
if bar_height < 1 then
|
||||
bar_is_scaled = true
|
||||
-- can't do a 1:1 ratio
|
||||
-- use minimum size bar with scaled offset
|
||||
local scroll_progress = scroll_offset / max_down_scroll
|
||||
offset = 2 + math.floor(scroll_progress * (max_bar_height - 1))
|
||||
bar_height = 1
|
||||
else
|
||||
bar_is_scaled = false
|
||||
end
|
||||
|
||||
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)
|
||||
if (i >= offset and i < (bar_height + offset)) and (bar_height ~= max_bar_height) then
|
||||
if args.nav_fg_bg ~= nil then
|
||||
e.window.setBackgroundColor(args.nav_fg_bg.fgd)
|
||||
else
|
||||
e.window.setBackgroundColor(e.fg_bg.fgd)
|
||||
end
|
||||
else
|
||||
e.window.setBackgroundColor(e.fg_bg.bkg)
|
||||
if args.nav_fg_bg ~= nil then
|
||||
e.window.setBackgroundColor(args.nav_fg_bg.bkg)
|
||||
else
|
||||
e.window.setBackgroundColor(e.fg_bg.bkg)
|
||||
end
|
||||
end
|
||||
|
||||
e.window.setCursorPos(e.frame.w, i)
|
||||
@ -104,22 +159,42 @@ local function listbox(args)
|
||||
scroll_frame.setVisible(true)
|
||||
|
||||
draw_bar()
|
||||
end
|
||||
|
||||
-- log.info("content_height[" .. content_height .. "] max_down_scroll[" .. max_down_scroll .. "] scroll_offset[" .. scroll_offset .. "] bar_height[" .. bar_height .. "]")
|
||||
-- determine where to scroll to based on a scrollbar being dragged without a 1:1 relationship
|
||||
---@param direction -1|1 negative 1 to scroll up by one, positive 1 to scroll down by one
|
||||
local function scaled_bar_scroll(direction)
|
||||
local scroll_progress = scroll_offset / max_down_scroll
|
||||
local bar_position = math.floor(scroll_progress * (max_bar_height - 1))
|
||||
|
||||
-- check what moving the scroll bar up or down would mean for the scroll progress
|
||||
scroll_progress = (bar_position + direction) / (max_bar_height - 1)
|
||||
|
||||
return math.max(math.floor(scroll_progress * max_down_scroll), max_down_scroll)
|
||||
end
|
||||
|
||||
-- scroll down the list
|
||||
local function scroll_down()
|
||||
local function scroll_down(scaled)
|
||||
if scroll_offset > max_down_scroll then
|
||||
scroll_offset = scroll_offset - 1
|
||||
if scaled then
|
||||
scroll_offset = scaled_bar_scroll(1)
|
||||
else
|
||||
scroll_offset = scroll_offset - 1
|
||||
end
|
||||
|
||||
update_positions()
|
||||
end
|
||||
end
|
||||
|
||||
-- scroll up the list
|
||||
local function scroll_up()
|
||||
local function scroll_up(scaled)
|
||||
if scroll_offset < 0 then
|
||||
scroll_offset = scroll_offset + 1
|
||||
if scaled then
|
||||
scroll_offset = scaled_bar_scroll(-1)
|
||||
else
|
||||
scroll_offset = scroll_offset + 1
|
||||
end
|
||||
|
||||
update_positions()
|
||||
end
|
||||
end
|
||||
@ -148,11 +223,25 @@ local function listbox(args)
|
||||
---@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.type == CLICK_TYPE.TAP then
|
||||
if event.current.x == e.frame.w then
|
||||
if event.current.y == 1 or event.current.y < bar_bounds[1] then
|
||||
draw_arrows(1)
|
||||
scroll_up()
|
||||
if args.nav_active ~= nil then tcd.dispatch(0.25, function () draw_arrows(0) end) end
|
||||
elseif event.current.y == e.frame.h or event.current.y > bar_bounds[2] then
|
||||
draw_arrows(-1)
|
||||
scroll_down()
|
||||
if args.nav_active ~= nil then tcd.dispatch(0.25, function () draw_arrows(0) end) end
|
||||
end
|
||||
end
|
||||
elseif 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
|
||||
draw_arrows(1)
|
||||
scroll_up()
|
||||
elseif event.current.y == e.frame.h or event.current.y > bar_bounds[2] then
|
||||
draw_arrows(-1)
|
||||
scroll_down()
|
||||
else
|
||||
-- clicked on bar
|
||||
@ -163,14 +252,15 @@ local function listbox(args)
|
||||
end
|
||||
elseif event.type == CLICK_TYPE.UP then
|
||||
holding_bar = false
|
||||
draw_arrows(0)
|
||||
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()
|
||||
scroll_up(bar_is_scaled)
|
||||
elseif event.current.y > mouse_last_y then
|
||||
scroll_down()
|
||||
scroll_down(bar_is_scaled)
|
||||
end
|
||||
|
||||
mouse_last_y = event.current.y
|
||||
@ -184,6 +274,9 @@ local function listbox(args)
|
||||
end
|
||||
end
|
||||
|
||||
draw_arrows(0)
|
||||
draw_bar()
|
||||
|
||||
return e.complete()
|
||||
end
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user