diff --git a/graphics/core.lua b/graphics/core.lua index db2585b..9e31101 100644 --- a/graphics/core.lua +++ b/graphics/core.lua @@ -7,7 +7,7 @@ local flasher = require("graphics.flasher") local core = {} -core.version = "2.0.3" +core.version = "2.0.4" core.flasher = flasher core.events = events diff --git a/graphics/element.lua b/graphics/element.lua index 1044799..517e230 100644 --- a/graphics/element.lua +++ b/graphics/element.lua @@ -105,6 +105,7 @@ function element.new(args, child_offset_x, child_offset_y) value = nil, ---@type any window = nil, ---@type table content_window = nil, ---@type table|nil + mouse_window_shift = { x = 0, y = 0 }, fg_bg = core.cpair(colors.white, colors.black), frame = core.gframe(1, 1, 1, 1), children = {}, @@ -344,6 +345,10 @@ function element.new(args, child_offset_x, child_offset_y) -- handle this element having been unfocused 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 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 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 ---@nodiscard ---@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 self.focused = true protected.on_focused() + if not self.is_root then args.parent.__child_focused(public) end end end @@ -704,10 +717,11 @@ function element.new(args, child_offset_x, child_offset_y) end 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) - 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 -- clicked out, unfocus this element and children public.unfocus_all() diff --git a/graphics/elements/listbox.lua b/graphics/elements/listbox.lua index d138e19..a8f9929 100644 --- a/graphics/elements/listbox.lua +++ b/graphics/elements/listbox.lua @@ -158,6 +158,9 @@ local function listbox(args) scroll_frame.reposition(1, 1 + scroll_offset) scroll_frame.setVisible(true) + -- shift mouse events + e.mouse_window_shift.y = scroll_offset + draw_bar() end @@ -219,6 +222,28 @@ local function listbox(args) 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 ---@param event mouse_interaction mouse event function e.handle_mouse(event) @@ -226,23 +251,27 @@ local function listbox(args) if event.type == MOUSE_CLICK.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 + 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 + 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 + 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 + end end end elseif event.type == MOUSE_CLICK.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() + 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 - draw_arrows(-1) scroll_down() + if event.current.y == e.frame.h then draw_arrows(-1) end else -- clicked on bar holding_bar = true