diff --git a/ui/elements/element.lua b/ui/elements/element.lua index 0a2c021..c302198 100644 --- a/ui/elements/element.lua +++ b/ui/elements/element.lua @@ -26,6 +26,35 @@ local function set_hold_element(elem) end end +---get outermost parent of element +---@param elem any +---@return unknown +local function get_parent(elem) + if elem.parent then + return get_parent(elem.parent) + end + return elem +end + +---find a sufficiently expandable parent +---@param elem any +---@param amount_x number +---@param amount_y number +---@return any +local function find_expandable_parent(elem, amount_x, amount_y) + elem.changed = true + if elem.expandable_x >= amount_x and elem.expandable_y >= amount_y then + return elem + else + if elem.parent then + return find_expandable_parent(elem.parent, amount_x, amount_y) + else + log("no have sufficiently expandable parent!") + return elem + end + end +end + ---create a new element, implements base functionality for all other elements (does nothing on its own) ---@param options any ---@return table @@ -66,6 +95,42 @@ function element:new(options) return self end +---call after modifying element +function element:update_size() + self.changed = true + local old_width, old_height = self.width, self.height + self:calculate_layout(self.last_available_width, self.last_available_height) + local x = self.width - old_width + local y = self.height - old_height + if x == 0 and y == 0 then + return + end + if x > 0 or y > 0 then + self.last_space_maker = find_expandable_parent(self, x, y) + if not self.last_space_maker.mutated then + self.last_space_maker = nil + return + end + self.last_space_maker:mutated() + elseif self.last_space_maker then + local parent = self.parent + while parent ~= self.last_space_maker do + parent.changed = true + parent = parent.parent + end + self.last_space_maker:mutated() + end +end + +function element:_update_child_expand() + if self.parent and self.parent.prevent_child_expand then + if self.parent.prevent_child_expand then + self.expandable_x = 0 + self.expandable_y = 0 + end + end +end + ---set the style of the element ---@param style table function element:set_style(style) @@ -112,20 +177,15 @@ function element:calculate_layout(width, height) else log("Element has no calculate_element_layout function?") end - self.expandable_x = width - self.width - self.expandable_y = height - self.height + self.expandable_x = math.max(width - self.width, 0) + self.expandable_y = math.max(height - self.height, 0) + self:_update_child_expand() return self.width, self.height end ---follows the references to element's parent until an element has no parent, this element is returned ---@return table function element:get_root() - local function get_parent(elem) - if elem.parent then - return get_parent(elem.parent) - end - return elem - end return get_parent(self) end diff --git a/ui/elements/quad.lua b/ui/elements/quad.lua index cf9fec3..c79eaf1 100644 --- a/ui/elements/quad.lua +++ b/ui/elements/quad.lua @@ -18,6 +18,7 @@ function quad:new(options) border_color = { 1, 1, 1, 1 }, background_color = { 0, 0, 0, 1 }, vertices = {}, + prevent_child_expand = "all", }, quad), options ) diff --git a/ui/layout/collapse.lua b/ui/layout/collapse.lua index de5e24b..1f9869d 100644 --- a/ui/layout/collapse.lua +++ b/ui/layout/collapse.lua @@ -1,6 +1,7 @@ local log = require("log")(...) local animated_transform = require("ui.anim.transform") local signal = require("ui.anim.signal") +local update_expand = require("ui.elements.element")._update_child_expand local collapse = {} collapse.__index = collapse @@ -60,6 +61,7 @@ function collapse:new(element, options) scale = true, pos = true, }, + prevent_child_expand = "all", }, collapse) obj.element.parent = obj if options.style then @@ -178,8 +180,9 @@ function collapse:calculate_layout(width, height) end self.content_length, self.content_thickness = self.wh2lt(content_width, content_height) self.width, self.height = self.lt2wh(self.pos(), self.content_thickness) - self.expandable_x = width - self.width - self.expandable_y = height - self.height + self.expandable_x = math.max(width - self.width, 0) + self.expandable_y = math.max(height - self.height, 0) + update_expand(self) self.changed = false return self.width, self.height end diff --git a/ui/layout/flex.lua b/ui/layout/flex.lua index 6f7a135..b5f740c 100644 --- a/ui/layout/flex.lua +++ b/ui/layout/flex.lua @@ -1,4 +1,5 @@ local animated_transform = require("ui.anim.transform") +local element = require("ui.elements.element") ---@class flex ---@field direction string direction the flex container will position elements in @@ -19,6 +20,7 @@ local animated_transform = require("ui.anim.transform") -- amount that children can expand (in pixels) ---@field expandable_x number ---@field expandable_y number +---@field prevent_child_expand string ---@field changed boolean something changed, requires layout recalculation local flex = {} flex.__index = flex @@ -71,6 +73,10 @@ function flex:new(elements, options) scale = true, }, }, flex) + obj.prevent_child_expand = "horizontal" + if options.direction == "column" then + obj.prevent_child_expand = "vertical" + end for i = 1, #elements do elements[i].parent = obj elements[i].parent_index = i @@ -408,8 +414,9 @@ function flex:calculate_layout(width, height) end self.width, self.height = lt2wh(final_length, final_thickness) - self.expandable_x = width - self.width - self.expandable_y = height - self.height + self.expandable_x = math.max(width - self.width, 0) + self.expandable_y = math.max(height - self.height, 0) + element._update_child_expand(self) self.changed = false return self.width, self.height end diff --git a/ui/layout/scroll.lua b/ui/layout/scroll.lua index 13aabac..80e21ca 100644 --- a/ui/layout/scroll.lua +++ b/ui/layout/scroll.lua @@ -2,6 +2,7 @@ local animated_transform = require("ui.anim.transform") local signal = require("ui.anim.signal") local ease = require("ui.anim.ease") local extmath = require("ui.extmath") +local update_expand = require("ui.elements.element")._update_child_expand -- how much has to be moved on touchscreen until scrolling starts (if 0 clicking in a scrollable container becomes impossible as it's always registered as scroll since touch is analog input which will always have small fluctuations) local SCROLL_THRESHOLD = 10 @@ -83,6 +84,7 @@ function scroll:new(element, options) -- position on screen x = 0, y = 0, + prevent_child_expand = "all", }, scroll) obj.element.parent = obj if options.style then @@ -394,6 +396,11 @@ function scroll:calculate_layout(width, height) self.scroll_pos:set_immediate_value(self.scroll_target) end self.expandable_x, self.expandable_y = self.lt2wh(math.huge, avail_thick - thick) + self.expandable_x = math.max(self.expandable_x, 0) + self.expandable_y = math.max(self.expandable_y, 0) + update_expand(self) + local _, expand_thick = self.wh2lt(self.expandable_x, self.expandable_y) + self.expandable_x, self.expandable_y = self.lt2wh(math.huge, expand_thick) self.width, self.height = self.lt2wh(math.min(self.content_length, avail_len), thick) self.changed = false return self.width, self.height diff --git a/ui/overlay/settings.lua b/ui/overlay/settings.lua index 69e1916..8ea59fe 100644 --- a/ui/overlay/settings.lua +++ b/ui/overlay/settings.lua @@ -101,8 +101,7 @@ local function create_setting(name, property, value) else text.raw_text = property.display_name .. ": " .. state end - text.changed = true - layout:mutated() + text:update_size() if property.onchange then if property.onchange(state) then return true @@ -131,7 +130,7 @@ local function create_setting(name, property, value) end, click_handler = function(self) self.element:wait_for_input():done(function() - self:mutated() + self:update_size() end) return true end,