listbox improvements, tabbing while staying in frame (autoscroll)

This commit is contained in:
Mikayla Fischler 2023-10-15 17:02:48 -04:00
parent 43e545b6ae
commit 01caca48dc
3 changed files with 53 additions and 10 deletions

View File

@ -7,7 +7,7 @@ local flasher = require("graphics.flasher")
local core = {} local core = {}
core.version = "2.0.3" core.version = "2.0.4"
core.flasher = flasher core.flasher = flasher
core.events = events core.events = events

View File

@ -105,6 +105,7 @@ function element.new(args, child_offset_x, child_offset_y)
value = nil, ---@type any value = nil, ---@type any
window = nil, ---@type table window = nil, ---@type table
content_window = nil, ---@type table|nil content_window = nil, ---@type table|nil
mouse_window_shift = { x = 0, y = 0 },
fg_bg = core.cpair(colors.white, colors.black), fg_bg = core.cpair(colors.white, colors.black),
frame = core.gframe(1, 1, 1, 1), frame = core.gframe(1, 1, 1, 1),
children = {}, children = {},
@ -344,6 +345,10 @@ function element.new(args, child_offset_x, child_offset_y)
-- handle this element having been unfocused -- handle this element having been unfocused
function protected.on_unfocused() end function protected.on_unfocused() end
-- handle this element having had a child focused
---@param child graphics_element
function protected.on_child_focused(child) end
-- handle this element having been shown -- handle this element having been shown
function protected.on_shown() end function protected.on_shown() end
@ -520,6 +525,13 @@ function element.new(args, child_offset_x, child_offset_y)
else args.parent.__focus_child(child) end else args.parent.__focus_child(child) end
end end
-- a child was focused, used to make sure it is actually visible to the user in the content frame
---@param child graphics_element
function public.__child_focused(child)
protected.on_child_focused(child)
if not self.is_root then args.parent.__child_focused(public) end
end
-- get a child element -- get a child element
---@nodiscard ---@nodiscard
---@param id element_id ---@param id element_id
@ -652,6 +664,7 @@ function element.new(args, child_offset_x, child_offset_y)
if args.can_focus and protected.enabled and not self.focused then if args.can_focus and protected.enabled and not self.focused then
self.focused = true self.focused = true
protected.on_focused() protected.on_focused()
if not self.is_root then args.parent.__child_focused(public) end
end end
end end
@ -704,10 +717,11 @@ function element.new(args, child_offset_x, child_offset_y)
end end
local event_T = events.mouse_transposed(event, self.position.x, self.position.y) local event_T = events.mouse_transposed(event, self.position.x, self.position.y)
-- handle the mouse event then pass to children
protected.handle_mouse(event_T) protected.handle_mouse(event_T)
for _, child in pairs(protected.children) do child.get().handle_mouse(event_T) end
-- shift child event if the content window has moved then pass to children
local c_event_T = events.mouse_transposed(event_T, protected.mouse_window_shift.x + 1, protected.mouse_window_shift.y + 1)
for _, child in pairs(protected.children) do child.get().handle_mouse(c_event_T) end
elseif event.type == events.MOUSE_CLICK.DOWN or event.type == events.MOUSE_CLICK.TAP then elseif event.type == events.MOUSE_CLICK.DOWN or event.type == events.MOUSE_CLICK.TAP then
-- clicked out, unfocus this element and children -- clicked out, unfocus this element and children
public.unfocus_all() public.unfocus_all()

View File

@ -158,6 +158,9 @@ local function listbox(args)
scroll_frame.reposition(1, 1 + scroll_offset) scroll_frame.reposition(1, 1 + scroll_offset)
scroll_frame.setVisible(true) scroll_frame.setVisible(true)
-- shift mouse events
e.mouse_window_shift.y = scroll_offset
draw_bar() draw_bar()
end end
@ -219,6 +222,28 @@ local function listbox(args)
end end
end end
-- handle a child in the list being focused, make sure it is visible
function e.on_child_focused(child)
for i = 1, #list do
local item = list[i] ---@type listbox_item
if item.e == child then
if (item.y + scroll_offset) <= 0 then
scroll_offset = 1 - item.y
update_positions()
draw_bar()
elseif (item.y + scroll_offset) == 1 then
-- do nothing, it's right at the top (if the bottom doesn't fit we can't easily fix that)
elseif ((item.h + item.y - 1) + scroll_offset) > e.frame.h then
scroll_offset = 1 - ((item.h + item.y) - e.frame.h)
update_positions()
draw_bar()
end
return
end
end
end
-- handle mouse interaction -- handle mouse interaction
---@param event mouse_interaction mouse event ---@param event mouse_interaction mouse event
function e.handle_mouse(event) function e.handle_mouse(event)
@ -226,23 +251,27 @@ local function listbox(args)
if event.type == MOUSE_CLICK.TAP then if event.type == MOUSE_CLICK.TAP then
if event.current.x == e.frame.w then if event.current.x == e.frame.w then
if event.current.y == 1 or event.current.y < bar_bounds[1] then if event.current.y == 1 or event.current.y < bar_bounds[1] then
draw_arrows(1)
scroll_up() scroll_up()
if event.current.y == 1 then
draw_arrows(1)
if args.nav_active ~= nil then tcd.dispatch(0.25, function () draw_arrows(0) end) end if args.nav_active ~= nil then tcd.dispatch(0.25, function () draw_arrows(0) end) end
end
elseif event.current.y == e.frame.h or event.current.y > bar_bounds[2] then elseif event.current.y == e.frame.h or event.current.y > bar_bounds[2] then
draw_arrows(-1)
scroll_down() scroll_down()
if event.current.y == e.frame.h then
draw_arrows(-1)
if args.nav_active ~= nil then tcd.dispatch(0.25, function () draw_arrows(0) end) end if args.nav_active ~= nil then tcd.dispatch(0.25, function () draw_arrows(0) end) end
end end
end end
end
elseif event.type == MOUSE_CLICK.DOWN then elseif event.type == MOUSE_CLICK.DOWN then
if event.current.x == e.frame.w then if event.current.x == e.frame.w then
if event.current.y == 1 or event.current.y < bar_bounds[1] then if event.current.y == 1 or event.current.y < bar_bounds[1] then
draw_arrows(1)
scroll_up() scroll_up()
if event.current.y == 1 then draw_arrows(1) end
elseif event.current.y == e.frame.h or event.current.y > bar_bounds[2] then elseif event.current.y == e.frame.h or event.current.y > bar_bounds[2] then
draw_arrows(-1)
scroll_down() scroll_down()
if event.current.y == e.frame.h then draw_arrows(-1) end
else else
-- clicked on bar -- clicked on bar
holding_bar = true holding_bar = true