mirror of
https://github.com/Insality/druid.git
synced 2025-09-27 18:12:19 +02:00
Update docs, config generations, fix linter
This commit is contained in:
@@ -367,6 +367,12 @@ function druid__data_list.scroll_to_index(self, index) end
|
||||
---@return druid.data_list Current DataList instance
|
||||
function druid__data_list.set_data(self, data) end
|
||||
|
||||
--- Set refresh function for DataList component
|
||||
---@param self druid.data_list @{DataList}
|
||||
---@param is_use_cache boolean Use cache version of DataList. Requires make setup of components in on_element_add callback and clean in on_element_remove
|
||||
---@return druid.data_list Current DataList instance
|
||||
function druid__data_list.set_use_cache(self, is_use_cache) end
|
||||
|
||||
|
||||
---@class druid.drag : druid.base_component
|
||||
---@field can_x boolean Is drag component process vertical dragging.
|
||||
@@ -374,8 +380,8 @@ function druid__data_list.set_data(self, data) end
|
||||
---@field is_drag boolean Is component now dragging
|
||||
---@field is_touch boolean Is component now touching
|
||||
---@field node node Drag node
|
||||
---@field on_drag druid.event on drag progress callback(self, dx, dy, total_x, total_y)
|
||||
---@field on_drag_end druid.event Event on drag end callback(self, total_x, total_y)
|
||||
---@field on_drag druid.event on drag progress callback(self, dx, dy, total_x, total_y, touch)
|
||||
---@field on_drag_end druid.event Event on drag end callback(self, total_x, total_y, touch)
|
||||
---@field on_drag_start druid.event Event on drag start callback(self, touch)
|
||||
---@field on_touch_end druid.event Event on touch end callback(self)
|
||||
---@field on_touch_start druid.event Event on touch start callback(self)
|
||||
@@ -559,7 +565,8 @@ function druid__event.unsubscribe(self, callback, callback_context) end
|
||||
---@field button druid.button Button component from click_node
|
||||
---@field click_node node|nil Button trigger node
|
||||
---@field node node Visual node
|
||||
---@field on_change_state druid.event On change state callback(self, state)
|
||||
---@field on_hotkey_pressed druid.event On hotkey released callback(self, argument)
|
||||
---@field on_hotkey_released druid.event On hotkey released callback(self, argument)
|
||||
---@field style druid.hotkey.style Component style params.
|
||||
local druid__hotkey = {}
|
||||
|
||||
@@ -803,67 +810,8 @@ function druid__lang_text.translate(self, locale_id, a, b, c, d, e, f, g) end
|
||||
---@class druid.layout : druid.base_component
|
||||
---@field mode string Current layout mode
|
||||
---@field node node Layout node
|
||||
---@field on_size_changed druid.event On window resize callback(self, new_size)
|
||||
local druid__layout = {}
|
||||
|
||||
--- Set node for layout node to fit inside it.
|
||||
--- Pass nil to reset
|
||||
---@param self druid.layout @{Layout}
|
||||
---@param node node|nil
|
||||
---@return druid.layout @{Layout}
|
||||
function druid__layout.fit_into_node(self, node) end
|
||||
|
||||
--- Set size for layout node to fit inside it
|
||||
---@param self druid.layout @{Layout}
|
||||
---@param target_size vector3
|
||||
---@return druid.layout @{Layout}
|
||||
function druid__layout.fit_into_size(self, target_size) end
|
||||
|
||||
--- Set current size for layout node to fit inside it
|
||||
---@param self druid.layout @{Layout}
|
||||
---@return druid.layout @{Layout}
|
||||
function druid__layout.fit_into_window(self) end
|
||||
|
||||
--- The @{Layout} constructor
|
||||
---@param self druid.layout @{Layout}
|
||||
---@param node node Gui node
|
||||
---@param mode string The layout mode (from const.LAYOUT_MODE)
|
||||
---@param on_size_changed_callback function|nil The callback on window resize
|
||||
function druid__layout.init(self, node, mode, on_size_changed_callback) end
|
||||
|
||||
--- Set max gui upscale for FIT adjust mode (or side).
|
||||
--- It happens on bigger render gui screen
|
||||
---@param self druid.layout @{Layout}
|
||||
---@param max_gui_upscale number
|
||||
---@return druid.layout @{Layout}
|
||||
function druid__layout.set_max_gui_upscale(self, max_gui_upscale) end
|
||||
|
||||
--- Set maximum size of layout node
|
||||
---@param self druid.layout @{Layout}
|
||||
---@param max_size vector3
|
||||
---@return druid.layout @{Layout}
|
||||
function druid__layout.set_max_size(self, max_size) end
|
||||
|
||||
--- Set minimal size of layout node
|
||||
---@param self druid.layout @{Layout}
|
||||
---@param min_size vector3
|
||||
---@return druid.layout @{Layout}
|
||||
function druid__layout.set_min_size(self, min_size) end
|
||||
|
||||
--- Set new origin position of layout node.
|
||||
--- You should apply this on node movement
|
||||
---@param self druid.layout @{Layout}
|
||||
---@param new_origin_position vector3
|
||||
---@return druid.layout @{Layout}
|
||||
function druid__layout.set_origin_position(self, new_origin_position) end
|
||||
|
||||
--- Set new origin size of layout node.
|
||||
--- You should apply this on node manual size change
|
||||
---@param self druid.layout @{Layout}
|
||||
---@param new_origin_size vector3
|
||||
---@return druid.layout @{Layout}
|
||||
function druid__layout.set_origin_size(self, new_origin_size) end
|
||||
|
||||
|
||||
---@class druid.pin_knob : druid.base_component
|
||||
---@field druid druid_instance The component druid instance
|
||||
@@ -1056,9 +1004,9 @@ function druid__rich_text.get_words() end
|
||||
|
||||
--- The @{RichText} constructor
|
||||
---@param self druid.rich_text @{RichText}
|
||||
---@param template string The Rich Text template name
|
||||
---@param nodes table The node table, if prefab was copied by gui.clone_tree()
|
||||
function druid__rich_text.init(self, template, nodes) end
|
||||
---@param text_node node|string The text node to make Rich Text
|
||||
---@param value string|nil The initial text value. Default will be gui.get_text(text_node)
|
||||
function druid__rich_text.init(self, text_node, value) end
|
||||
|
||||
--- Set text for Rich Text
|
||||
---@param self druid.rich_text @{RichText}
|
||||
@@ -1097,6 +1045,7 @@ local druid__rich_text__style = {}
|
||||
---@field style druid.scroll.style Component style params.
|
||||
---@field target_position vector3 Current scroll target position
|
||||
---@field view_node node Scroll view node
|
||||
---@field view_size vector3 Scroll view size
|
||||
local druid__scroll = {}
|
||||
|
||||
--- Bind the grid component (Static or Dynamic) to recalculate scroll size on grid changes
|
||||
@@ -1205,6 +1154,10 @@ function druid__scroll.set_vertical_scroll(self, state) end
|
||||
---@return druid.scroll Current scroll instance
|
||||
function druid__scroll.set_view_size(self, size) end
|
||||
|
||||
--- Refresh scroll view size
|
||||
---@param self druid.scroll @{Scroll}
|
||||
function druid__scroll.update_view_size(self) end
|
||||
|
||||
|
||||
---@class druid.scroll.style
|
||||
---@field ANIM_SPEED number|nil Scroll gui.animation speed for scroll_to function. Default: 2
|
||||
@@ -1550,7 +1503,7 @@ local druid__timer = {}
|
||||
--- The @{Timer} constructor
|
||||
---@param self druid.timer @{Timer}
|
||||
---@param node node Gui text node
|
||||
---@param seconds_from number Start timer value in seconds
|
||||
---@param seconds_from number|nil Start timer value in seconds
|
||||
---@param seconds_to number|nil End timer value in seconds
|
||||
---@param callback function|nil Function on timer end
|
||||
function druid__timer.init(self, node, seconds_from, seconds_to, callback) end
|
||||
@@ -1579,14 +1532,6 @@ local druid_instance = {}
|
||||
---@param self druid_instance
|
||||
function druid_instance.final(self) end
|
||||
|
||||
--- Create new component.
|
||||
---@generic T
|
||||
---@param self druid_instance
|
||||
---@param component T Component module
|
||||
---@param ... any Other component params to pass it to component:init function
|
||||
---@return T Component instance
|
||||
function druid_instance.new(self, component, ...) end
|
||||
|
||||
--- Create @{BackHandler} component
|
||||
---@param self druid_instance
|
||||
---@param callback function|nil @The callback(self, custom_args) to call on back event
|
||||
@@ -1683,9 +1628,8 @@ function druid_instance.new_lang_text(self, node, locale_id, adjust_type) end
|
||||
---@param self druid_instance
|
||||
---@param node string|node The_node id or gui.get_node(node_id).
|
||||
---@param mode string The layout mode
|
||||
---@param on_size_changed_callback function|nil The callback on window resize
|
||||
---@return druid.layout @{Layout} component
|
||||
function druid_instance.new_layout(self, node, mode, on_size_changed_callback) end
|
||||
function druid_instance.new_layout(self, node, mode) end
|
||||
|
||||
--- Create @{Progress} component
|
||||
---@param self druid_instance
|
||||
@@ -1752,7 +1696,7 @@ function druid_instance.new_text(self, node, value, no_adjust) end
|
||||
--- Create @{Timer} component
|
||||
---@param self druid_instance
|
||||
---@param node string|node Gui text node
|
||||
---@param seconds_from number|nil Start timer value in seconds
|
||||
---@param seconds_from number Start timer value in seconds
|
||||
---@param seconds_to number|nil End timer value in seconds
|
||||
---@param callback function|nil Function on timer end
|
||||
---@return druid.timer @{Timer} component
|
||||
@@ -2021,3 +1965,22 @@ function helper.table_to_string(t) end
|
||||
---@field height number
|
||||
---@field max_ascent number
|
||||
---@field max_descent number
|
||||
|
||||
---@class utf8
|
||||
---@field len fun(string: string): number
|
||||
---@field sub fun(string: string, i: number, j: number): string
|
||||
---@field gmatch fun(string: string, pattern: string): fun(): string
|
||||
---@field gsub fun(string: string, pattern: string, repl: string, n: number): string
|
||||
---@field char fun(...: number): string
|
||||
---@field byte fun(string: string, i: number, j: number): number
|
||||
|
||||
|
||||
---Add generics to some functions.
|
||||
|
||||
---Create new component.
|
||||
---@generic T: druid.base_component
|
||||
---@param self druid_instance
|
||||
---@param component T Component module
|
||||
---@param ... any Other component params to pass it to component:init function
|
||||
---@return T Component instance
|
||||
function druid_instance.new(self, component, ...) end
|
@@ -344,10 +344,6 @@ function Button.on_input(self, action_id, action)
|
||||
return false
|
||||
end
|
||||
|
||||
if not self:is_enabled() then
|
||||
return false
|
||||
end
|
||||
|
||||
local is_consume = true
|
||||
local is_pick = true
|
||||
local is_key_trigger = (action_id == self.key_trigger)
|
||||
|
@@ -22,10 +22,10 @@
|
||||
--- Event on drag start callback(self, touch)
|
||||
-- @tfield DruidEvent on_drag_start @{DruidEvent}
|
||||
|
||||
--- on drag progress callback(self, dx, dy, total_x, total_y)
|
||||
--- on drag progress callback(self, dx, dy, total_x, total_y, touch)
|
||||
-- @tfield DruidEvent on_drag Event @{DruidEvent}
|
||||
|
||||
--- Event on drag end callback(self, total_x, total_y)
|
||||
--- Event on drag end callback(self, total_x, total_y, touch)
|
||||
-- @tfield DruidEvent on_drag_end @{DruidEvent}
|
||||
|
||||
--- Is component now touching
|
||||
|
@@ -50,6 +50,9 @@
|
||||
--- Scroll view node
|
||||
-- @tfield node view_node
|
||||
|
||||
--- Scroll view size
|
||||
-- @tfield vector3 view_size
|
||||
|
||||
--- Scroll content node
|
||||
-- @tfield node content_node
|
||||
|
||||
|
@@ -211,6 +211,9 @@ local function update_text_with_trim(self, trim_postfix)
|
||||
text_length = text_length - 1
|
||||
new_text = utf8.sub(self.last_value, 1, text_length)
|
||||
text_width = self:get_text_size(new_text .. trim_postfix)
|
||||
if text_length == 0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
gui.set_text(self.node, new_text .. trim_postfix)
|
||||
|
@@ -227,7 +227,7 @@ function M._fill_properties(word, metrics, settings)
|
||||
else
|
||||
-- Text properties
|
||||
word.scale = settings.scale * word.relative_scale * settings.adjust_scale
|
||||
word.pivot = gui.PIVOT_W -- With this pivot adjustments works correctly, but with another some misalignment
|
||||
word.pivot = gui.PIVOT_SW -- With this pivot adjustments works more correctly than with other pivots
|
||||
word.size = vmath.vector3(metrics.width, metrics.height, 0)
|
||||
word.offset = vmath.vector3(metrics.offset_x, metrics.offset_y, 0)
|
||||
end
|
||||
|
@@ -1,45 +0,0 @@
|
||||
fonts {
|
||||
name: "game"
|
||||
font: "/example/assets/fonts/game.font"
|
||||
}
|
||||
textures {
|
||||
name: "items"
|
||||
texture: "/example/assets/images/kenney.atlas"
|
||||
}
|
||||
nodes {
|
||||
size {
|
||||
x: 400.0
|
||||
y: 100.0
|
||||
}
|
||||
type: TYPE_BOX
|
||||
id: "root"
|
||||
inherit_alpha: true
|
||||
visible: false
|
||||
}
|
||||
nodes {
|
||||
position {
|
||||
x: -200.0
|
||||
}
|
||||
size {
|
||||
x: 400.0
|
||||
y: 100.0
|
||||
}
|
||||
type: TYPE_TEXT
|
||||
text: "Rich text"
|
||||
font: "game"
|
||||
id: "text_prefab"
|
||||
pivot: PIVOT_W
|
||||
parent: "root"
|
||||
inherit_alpha: true
|
||||
outline_alpha: 0.0
|
||||
shadow_alpha: 0.0
|
||||
}
|
||||
nodes {
|
||||
type: TYPE_BOX
|
||||
id: "icon_prefab"
|
||||
parent: "root"
|
||||
inherit_alpha: true
|
||||
size_mode: SIZE_MODE_AUTO
|
||||
}
|
||||
material: "/builtins/materials/gui.material"
|
||||
adjust_reference: ADJUST_REFERENCE_PARENT
|
@@ -109,8 +109,8 @@ local RichText = component.create("rich_text")
|
||||
|
||||
--- The @{RichText} constructor
|
||||
-- @tparam RichText self @{RichText}
|
||||
-- @tparam string template The Rich Text template name
|
||||
-- @tparam table nodes The node table, if prefab was copied by gui.clone_tree()
|
||||
-- @tparam node|string text_node The text node to make Rich Text
|
||||
-- @tparam string|nil value The initial text value. Default will be gui.get_text(text_node)
|
||||
function RichText.init(self, text_node, value)
|
||||
self.root = self:get_node(text_node)
|
||||
self.text_prefab = self.root
|
||||
|
@@ -25,35 +25,6 @@ end
|
||||
|
||||
function M.get_commands()
|
||||
return {
|
||||
{
|
||||
label = "Print GUI Scheme",
|
||||
|
||||
locations = { "Outline" },
|
||||
|
||||
query = {
|
||||
selection = {type = "outline", cardinality = "many"}
|
||||
},
|
||||
|
||||
active = function(opts)
|
||||
return true
|
||||
end,
|
||||
|
||||
run = function(opts)
|
||||
print("local SCHEME = {")
|
||||
|
||||
for i = 1, #opts.selection do
|
||||
local file = opts.selection[i]
|
||||
if editor.can_get(file, "id") then
|
||||
local id = editor.get(file, "id")
|
||||
print("\t" .. string.upper(id) .. " = \"" .. id .. "\",")
|
||||
end
|
||||
end
|
||||
|
||||
print("}")
|
||||
print("")
|
||||
end
|
||||
},
|
||||
|
||||
{
|
||||
label = "Assign layers",
|
||||
|
||||
|
@@ -84,6 +84,8 @@ end
|
||||
|
||||
--- Set refresh function for DataList component
|
||||
-- @tparam DataList self @{DataList}
|
||||
-- @tparam boolean is_use_cache Use cache version of DataList. Requires make setup of components in on_element_add callback and clean in on_element_remove
|
||||
-- @treturn druid.data_list Current DataList instance
|
||||
function DataList.set_use_cache(self, is_use_cache)
|
||||
self._is_use_cache = is_use_cache
|
||||
return self
|
||||
@@ -280,7 +282,7 @@ end
|
||||
function DataList._refresh(self)
|
||||
self.scroll:set_size(self.grid:get_size_for(#self._data))
|
||||
|
||||
local start_pos = -self.scroll.position
|
||||
local start_pos = -self.scroll.position --[[@as vector3]]
|
||||
local start_index = self.grid:get_index(start_pos)
|
||||
start_index = math.max(1, start_index)
|
||||
|
||||
|
@@ -1,392 +0,0 @@
|
||||
local helper = require("druid.helper")
|
||||
local component = require("druid.component")
|
||||
|
||||
---@class druid.figma_layout.row_data
|
||||
---@field width number
|
||||
---@field height number
|
||||
---@field count number
|
||||
|
||||
---@class druid.figma_layout.rows_data
|
||||
---@field total_width number
|
||||
---@field total_height number
|
||||
---@field nodes_width table<node, number>
|
||||
---@field nodes_height table<node, number>
|
||||
---@field rows druid.figma_layout.row_data[]>
|
||||
|
||||
---@class druid.figma_layout: druid.base_component
|
||||
local M = component.create("layout")
|
||||
|
||||
|
||||
--- The @{Layout} constructor
|
||||
-- @tparam Layout self @{Layout}
|
||||
-- @tparam node node Gui node
|
||||
-- @tparam string layout_type The layout mode (from const.LAYOUT_MODE)
|
||||
-- @tparam function|nil on_size_changed_callback The callback on window resize
|
||||
function M:init(node, layout_type)
|
||||
self.node = self:get_node(node)
|
||||
|
||||
print(self.node)
|
||||
self.is_dirty = true
|
||||
self.entities = {}
|
||||
self.margin = { x = 0, y = 0 }
|
||||
self.padding = gui.get_slice9(self.node)
|
||||
self.type = layout_type or "horizontal"
|
||||
self.is_resize_width = false
|
||||
self.is_resize_height = false
|
||||
self.is_justify = false
|
||||
end
|
||||
|
||||
|
||||
function M:update()
|
||||
if not self.is_dirty then
|
||||
return
|
||||
end
|
||||
|
||||
self:refresh_layout()
|
||||
end
|
||||
|
||||
|
||||
---@param margin_x number|nil
|
||||
---@param margin_y number|nil
|
||||
---@return druid.figma_layout
|
||||
function M:set_margin(margin_x, margin_y)
|
||||
self.margin.x = margin_x or self.margin.x
|
||||
self.margin.y = margin_y or self.margin.y
|
||||
self.is_dirty = true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
---@param padding vector4 The vector4 with padding values, where x - left, y - top, z - right, w - bottom
|
||||
function M:set_padding(padding)
|
||||
self.padding = padding
|
||||
self.is_dirty = true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function M:set_dirty()
|
||||
self.is_dirty = true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
---@param is_justify boolean
|
||||
---@return druid.figma_layout
|
||||
function M:set_justify(is_justify)
|
||||
self.is_justify = is_justify
|
||||
self.is_dirty = true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
---@param type string The layout type: "horizontal", "vertical", "horizontal_wrap"
|
||||
function M:set_type(type)
|
||||
self.type = type
|
||||
self.is_dirty = true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
---@param is_hug_width boolean
|
||||
---@param is_hug_height boolean
|
||||
---@return druid.figma_layout
|
||||
function M:set_hug_content(is_hug_width, is_hug_height)
|
||||
self.is_resize_width = is_hug_width or false
|
||||
self.is_resize_height = is_hug_height or false
|
||||
self.is_dirty = true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
---@param node_or_node_id string|node
|
||||
---@return druid.figma_layout
|
||||
function M:add(node_or_node_id)
|
||||
-- Acquire node from entity or by id
|
||||
local node = node_or_node_id
|
||||
if type(node_or_node_id) == "table" then
|
||||
assert(node_or_node_id.node, "The entity should have a node")
|
||||
node = node_or_node_id.node
|
||||
else
|
||||
---@cast node_or_node_id string|node
|
||||
node = self:get_node(node_or_node_id)
|
||||
end
|
||||
|
||||
---@cast node node
|
||||
table.insert(self.entities, node)
|
||||
gui.set_parent(node, self.node)
|
||||
|
||||
self.is_dirty = true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
---@return druid.figma_layout
|
||||
function M:refresh_layout()
|
||||
local layout_node = self.node
|
||||
|
||||
local entities = self.entities
|
||||
local type = self.type -- vertical, horizontal, horizontal_wrap
|
||||
local margin = self.margin -- {x: horizontal, y: vertical} in pixels, between elements
|
||||
local padding = self.padding -- {x: left, y: top, z: right, w: bottom} in pixels
|
||||
local is_justify = self.is_justify
|
||||
local size = gui.get_size(layout_node)
|
||||
local max_width = size.x - padding.x - padding.z
|
||||
local max_height = size.y - padding.y - padding.w
|
||||
local layout_pivot_offset = helper.get_pivot_offset(gui.get_pivot(layout_node)) -- {x: -0.5, y: -0.5} - is left bot, {x: 0.5, y: 0.5} - is right top
|
||||
|
||||
local rows_data = self:calculate_rows_data()
|
||||
local rows = rows_data.rows
|
||||
local row_index = 1
|
||||
local row = rows[row_index]
|
||||
|
||||
-- Current x and Current y is a top left corner of the node
|
||||
local current_x = -row.width * (0.5 + layout_pivot_offset.x)
|
||||
local current_y = rows_data.total_height * (0.5 - layout_pivot_offset.y)
|
||||
|
||||
if is_justify then
|
||||
if (type == "horizontal" or type == "horizontal_wrap") and row.count > 1 then
|
||||
current_x = -max_width * (0.5 + layout_pivot_offset.x)
|
||||
end
|
||||
if type == "vertical" then
|
||||
current_y = max_height * (0.5 - layout_pivot_offset.y)
|
||||
end
|
||||
end
|
||||
|
||||
for index = 1, #entities do
|
||||
local node = entities[index]
|
||||
local node_width = rows_data.nodes_width[node]
|
||||
local node_height = rows_data.nodes_height[node]
|
||||
local pivot_offset = helper.get_pivot_offset(gui.get_pivot(node))
|
||||
|
||||
if node_width > 0 and node_height > 0 then
|
||||
-- Calculate position for current node
|
||||
local position_x, position_y
|
||||
|
||||
if type == "horizontal" then
|
||||
position_x = current_x + node_width * (0.5 + pivot_offset.x)
|
||||
position_y = current_y - row.height * (0.5 - pivot_offset.y)
|
||||
|
||||
local node_margin = margin.x
|
||||
if is_justify and row.count > 1 then
|
||||
node_margin = (max_width - row.width) / (row.count - 1) + margin.x
|
||||
end
|
||||
current_x = current_x + node_width + node_margin
|
||||
end
|
||||
|
||||
if type == "vertical" then
|
||||
position_x = current_x + row.width * (0.5 - pivot_offset.x)
|
||||
position_y = current_y - node_height * (0.5 + pivot_offset.y)
|
||||
|
||||
local node_margin = margin.y
|
||||
if is_justify then
|
||||
node_margin = (max_height - rows_data.total_height) / (#rows - 1) + margin.y
|
||||
end
|
||||
|
||||
current_y = current_y - node_height - node_margin
|
||||
end
|
||||
|
||||
if type == "horizontal_wrap" then
|
||||
local width = row.width
|
||||
if is_justify and row.count > 0 then
|
||||
width = math.max(row.width, max_width)
|
||||
end
|
||||
local new_row_width = width * (0.5 - layout_pivot_offset.x)
|
||||
|
||||
-- Compare with eps due the float loss and element flickering
|
||||
if current_x + node_width - new_row_width > 0.0001 then
|
||||
if row_index < #rows then
|
||||
row_index = row_index + 1
|
||||
row = rows[row_index]
|
||||
end
|
||||
|
||||
current_x = -row.width * (0.5 + layout_pivot_offset.x)
|
||||
current_y = current_y - row.height - margin.y
|
||||
if is_justify and row.count > 1 then
|
||||
current_x = -max_width * (0.5 + layout_pivot_offset.x)
|
||||
end
|
||||
end
|
||||
|
||||
position_x = current_x + node_width * (0.5 + pivot_offset.x)
|
||||
position_y = current_y - row.height * (0.5 - pivot_offset.y)
|
||||
|
||||
local node_margin = margin.x
|
||||
if is_justify and row.count > 1 then
|
||||
node_margin = (max_width - row.width) / (row.count - 1) + margin.x
|
||||
end
|
||||
current_x = current_x + node_width + node_margin
|
||||
end
|
||||
|
||||
do -- Padding offset
|
||||
if layout_pivot_offset.x == -0.5 then
|
||||
position_x = position_x + padding.x
|
||||
end
|
||||
if layout_pivot_offset.y == 0.5 then
|
||||
position_y = position_y - padding.y
|
||||
end
|
||||
if layout_pivot_offset.x == 0.5 then
|
||||
position_x = position_x - padding.z
|
||||
end
|
||||
if layout_pivot_offset.y == -0.5 then
|
||||
position_y = position_y + padding.w
|
||||
end
|
||||
end
|
||||
|
||||
self:set_node_position(node, position_x, position_y)
|
||||
end
|
||||
end
|
||||
|
||||
if self.is_resize_width or self.is_resize_height then
|
||||
if self.is_resize_width then
|
||||
size.x = rows_data.total_width + padding.x + padding.z
|
||||
end
|
||||
if self.is_resize_height then
|
||||
size.y = rows_data.total_height + padding.y + padding.w
|
||||
end
|
||||
gui.set_size(layout_node, size)
|
||||
end
|
||||
|
||||
self.is_dirty = false
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
---@return druid.figma_layout
|
||||
function M:clear_layout()
|
||||
for index = #self.entities, 1, -1 do
|
||||
self.entities[index] = nil
|
||||
end
|
||||
|
||||
self.is_dirty = true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
---@param node node
|
||||
---@return number, number
|
||||
function M.get_node_size(node)
|
||||
if not gui.is_enabled(node, false) then
|
||||
return 0, 0
|
||||
end
|
||||
|
||||
local scale = gui.get_scale(node)
|
||||
|
||||
-- If node has text - get text size instead of node size
|
||||
if gui.get_text(node) then
|
||||
local text_metrics = helper.get_text_metrics_from_node(node)
|
||||
return text_metrics.width * scale.x, text_metrics.height * scale.y
|
||||
end
|
||||
|
||||
local size = gui.get_size(node)
|
||||
return size.x * scale.x, size.y * scale.y
|
||||
end
|
||||
|
||||
|
||||
---Calculate rows data for layout. Contains total width, height and rows info (width, height, count of elements in row)
|
||||
---@private
|
||||
---@return druid.figma_layout.rows_data
|
||||
function M:calculate_rows_data()
|
||||
local entities = self.entities
|
||||
local margin = self.margin
|
||||
local type = self.type
|
||||
local padding = self.padding
|
||||
|
||||
local size = gui.get_size(self.node)
|
||||
local max_width = size.x - padding.x - padding.z
|
||||
|
||||
-- Collect rows info about width, height and count of elements in row
|
||||
local current_row = { width = 0, height = 0, count = 0 }
|
||||
local rows_data = {
|
||||
total_width = 0,
|
||||
total_height = 0,
|
||||
nodes_width = {},
|
||||
nodes_height = {},
|
||||
rows = { current_row }
|
||||
}
|
||||
|
||||
for index = 1, #entities do
|
||||
local node = entities[index]
|
||||
local node_width = rows_data.nodes_width[node]
|
||||
local node_height = rows_data.nodes_height[node]
|
||||
|
||||
-- Get node size if it's not calculated yet
|
||||
if not node_width or not node_height then
|
||||
node_width, node_height = M.get_node_size(node)
|
||||
rows_data.nodes_width[node] = node_width
|
||||
rows_data.nodes_height[node] = node_height
|
||||
end
|
||||
|
||||
if node_width > 0 and node_height > 0 then
|
||||
if type == "horizontal" then
|
||||
current_row.width = current_row.width + node_width + margin.x
|
||||
current_row.height = math.max(current_row.height, node_height)
|
||||
current_row.count = current_row.count + 1
|
||||
end
|
||||
|
||||
if type == "vertical" then
|
||||
if current_row.count > 0 then
|
||||
current_row = { width = 0, height = 0, count = 0 }
|
||||
table.insert(rows_data.rows, current_row)
|
||||
end
|
||||
|
||||
current_row.width = math.max(current_row.width, node_width + margin.x)
|
||||
current_row.height = node_height
|
||||
current_row.count = current_row.count + 1
|
||||
end
|
||||
|
||||
if type == "horizontal_wrap" then
|
||||
if current_row.width + node_width > max_width and current_row.count > 0 then
|
||||
current_row = { width = 0, height = 0, count = 0 }
|
||||
table.insert(rows_data.rows, current_row)
|
||||
end
|
||||
|
||||
current_row.width = current_row.width + node_width + margin.x
|
||||
current_row.height = math.max(current_row.height, node_height)
|
||||
current_row.count = current_row.count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove last margin of each row
|
||||
-- Calculate total width and height
|
||||
local rows_count = #rows_data.rows
|
||||
for index = 1, rows_count do
|
||||
local row = rows_data.rows[index]
|
||||
if row.width > 0 then
|
||||
row.width = row.width - margin.x
|
||||
end
|
||||
|
||||
rows_data.total_width = math.max(rows_data.total_width, row.width)
|
||||
rows_data.total_height = rows_data.total_height + row.height
|
||||
end
|
||||
|
||||
rows_data.total_height = rows_data.total_height + margin.y * (rows_count - 1)
|
||||
return rows_data
|
||||
end
|
||||
|
||||
|
||||
---@private
|
||||
---@param node node
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@return node
|
||||
function M:set_node_position(node, x, y)
|
||||
local position = gui.get_position(node)
|
||||
position.x = x
|
||||
position.y = y
|
||||
gui.set_position(node, position)
|
||||
|
||||
return node
|
||||
end
|
||||
|
||||
|
||||
return M
|
@@ -27,7 +27,6 @@
|
||||
local helper = require("druid.helper")
|
||||
local component = require("druid.component")
|
||||
local Event = require("druid.event")
|
||||
local const = require("druid.const")
|
||||
|
||||
local Hotkey = component.create("hotkey")
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
-- Copyright (c) 2021 Maksim Tuprikov <insality@gmail.com>. This code is licensed under MIT license
|
||||
-- Copyright (c) 2024 Maksim Tuprikov <insality@gmail.com>. This code is licensed under MIT license
|
||||
|
||||
--- Layout management on node
|
||||
--
|
||||
@@ -13,205 +13,406 @@
|
||||
--- Current layout mode
|
||||
-- @tfield string mode
|
||||
|
||||
---On window resize callback(self, new_size)
|
||||
-- @tfield DruidEvent on_size_changed @{DruidEvent}
|
||||
|
||||
---
|
||||
|
||||
|
||||
local const = require("druid.const")
|
||||
local helper = require("druid.helper")
|
||||
local component = require("druid.component")
|
||||
local Event = require("druid.event")
|
||||
|
||||
-- @class druid.layout.row_data
|
||||
-- @tfield width number
|
||||
-- @tfield height number
|
||||
-- @tfield count number
|
||||
|
||||
local Layout = component.create("layout")
|
||||
-- @class druid.layout.rows_data
|
||||
-- @tfield total_width number
|
||||
-- @tfield total_height number
|
||||
-- @tfield nodes_width table<node, number>
|
||||
-- @tfield nodes_height table<node, number>
|
||||
-- @tfield rows druid.layout.row_data[]>
|
||||
|
||||
-- @class druid.layout: druid.base_component
|
||||
local M = component.create("layout")
|
||||
|
||||
--- The @{Layout} constructor
|
||||
-- The @{Layout} constructor
|
||||
-- @tparam Layout self @{Layout}
|
||||
-- @tparam node node Gui node
|
||||
-- @tparam string mode The layout mode (from const.LAYOUT_MODE)
|
||||
-- @tparam string layout_type The layout mode (from const.LAYOUT_MODE)
|
||||
-- @tparam function|nil on_size_changed_callback The callback on window resize
|
||||
function Layout.init(self, node, mode, on_size_changed_callback)
|
||||
function M.init(self, node, layout_type)
|
||||
self.node = self:get_node(node)
|
||||
|
||||
self._min_size = nil
|
||||
self._max_size = nil
|
||||
self._current_size = vmath.vector3(0)
|
||||
self._inited = false
|
||||
self._max_gui_upscale = nil
|
||||
self._fit_node = nil
|
||||
|
||||
self._anchors = {}
|
||||
|
||||
self.mode = mode or const.LAYOUT_MODE.FIT
|
||||
|
||||
self.on_size_changed = Event(on_size_changed_callback)
|
||||
self.is_dirty = true
|
||||
self.entities = {}
|
||||
self.margin = { x = 0, y = 0 }
|
||||
self.padding = gui.get_slice9(self.node)
|
||||
self.type = layout_type or "horizontal"
|
||||
self.is_resize_width = false
|
||||
self.is_resize_height = false
|
||||
self.is_justify = false
|
||||
end
|
||||
|
||||
|
||||
function Layout.on_late_init(self)
|
||||
self._inited = true
|
||||
self.origin_size = self.origin_size or gui.get_size(self.node)
|
||||
self.fit_size = self.fit_size or vmath.vector3(self.origin_size)
|
||||
self.pivot = helper.get_pivot_offset(gui.get_pivot(self.node))
|
||||
self.origin_position = gui.get_position(self.node)
|
||||
self.position = vmath.vector3(self.origin_position)
|
||||
gui.set_size_mode(self.node, gui.SIZE_MODE_MANUAL)
|
||||
gui.set_adjust_mode(self.node, gui.ADJUST_FIT)
|
||||
self:on_window_resized()
|
||||
end
|
||||
|
||||
|
||||
function Layout.on_window_resized(self)
|
||||
if not self._inited then
|
||||
function M:update()
|
||||
if not self.is_dirty then
|
||||
return
|
||||
end
|
||||
|
||||
local x_koef, y_koef = helper.get_screen_aspect_koef()
|
||||
|
||||
local revert_scale = 1
|
||||
if self._max_gui_upscale then
|
||||
revert_scale = self._max_gui_upscale / helper.get_gui_scale()
|
||||
revert_scale = math.min(revert_scale, 1)
|
||||
end
|
||||
gui.set_scale(self.node, vmath.vector3(revert_scale))
|
||||
|
||||
if self._fit_node then
|
||||
self.fit_size = gui.get_size(self._fit_node)
|
||||
self.fit_size.x = self.fit_size.x / x_koef
|
||||
self.fit_size.y = self.fit_size.y / y_koef
|
||||
end
|
||||
|
||||
x_koef = self.fit_size.x / self.origin_size.x * x_koef
|
||||
y_koef = self.fit_size.y / self.origin_size.y * y_koef
|
||||
|
||||
local new_size = vmath.vector3(self.origin_size)
|
||||
|
||||
if self.mode == const.LAYOUT_MODE.STRETCH then
|
||||
new_size.x = new_size.x * x_koef / revert_scale
|
||||
new_size.y = new_size.y * y_koef / revert_scale
|
||||
end
|
||||
|
||||
if self.mode == const.LAYOUT_MODE.STRETCH_X then
|
||||
new_size.x = new_size.x * x_koef / revert_scale
|
||||
end
|
||||
|
||||
if self.mode == const.LAYOUT_MODE.STRETCH_Y then
|
||||
new_size.y = new_size.y * y_koef / revert_scale
|
||||
end
|
||||
|
||||
-- Fit to the stretched container (node size or other defined)
|
||||
if self.mode == const.LAYOUT_MODE.ZOOM_MIN then
|
||||
new_size = new_size * math.min(x_koef, y_koef)
|
||||
end
|
||||
if self.mode == const.LAYOUT_MODE.ZOOM_MAX then
|
||||
new_size = new_size * math.max(x_koef, y_koef)
|
||||
end
|
||||
|
||||
if self._min_size then
|
||||
new_size.x = math.max(new_size.x, self._min_size.x)
|
||||
new_size.y = math.max(new_size.y, self._min_size.y)
|
||||
end
|
||||
if self._max_size then
|
||||
new_size.x = math.min(new_size.x, self._max_size.x)
|
||||
new_size.y = math.min(new_size.y, self._max_size.y)
|
||||
end
|
||||
self._current_size = new_size
|
||||
gui.set_size(self.node, new_size)
|
||||
|
||||
self.position.x = self.origin_position.x + self.origin_position.x * (x_koef - 1)
|
||||
self.position.y = self.origin_position.y + self.origin_position.y * (y_koef - 1)
|
||||
gui.set_position(self.node, self.position)
|
||||
|
||||
self.on_size_changed:trigger(self:get_context(), new_size)
|
||||
self:refresh_layout()
|
||||
end
|
||||
|
||||
|
||||
--- Set minimal size of layout node
|
||||
-- @tparam Layout self @{Layout}
|
||||
-- @tparam vector3 min_size
|
||||
-- @treturn Layout @{Layout}
|
||||
function Layout.set_min_size(self, min_size)
|
||||
self._min_size = min_size
|
||||
-- @tparam number|nil margin_x
|
||||
-- @tparam number|nil margin_y
|
||||
-- @treturn druid.layout @{Layout}
|
||||
function M.set_margin(self, margin_x, margin_y)
|
||||
self.margin.x = margin_x or self.margin.x
|
||||
self.margin.y = margin_y or self.margin.y
|
||||
self.is_dirty = true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set maximum size of layout node
|
||||
-- @tparam Layout self @{Layout}
|
||||
-- @tparam vector3 max_size
|
||||
-- @treturn Layout @{Layout}
|
||||
function Layout.set_max_size(self, max_size)
|
||||
self._max_size = max_size
|
||||
-- @tparam vector4 padding The vector4 with padding values, where x - left, y - top, z - right, w - bottom
|
||||
-- @treturn druid.layout @{Layout}
|
||||
function M.set_padding(self, padding)
|
||||
self.padding = padding
|
||||
self.is_dirty = true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set new origin position of layout node. You should apply this on node movement
|
||||
-- @tparam Layout self @{Layout}
|
||||
-- @tparam vector3 new_origin_position
|
||||
-- @treturn Layout @{Layout}
|
||||
function Layout.set_origin_position(self, new_origin_position)
|
||||
self.origin_position = new_origin_position or self.origin_position
|
||||
self:on_window_resized()
|
||||
-- @treturn druid.layout @{Layout}
|
||||
function M.set_dirty(self)
|
||||
self.is_dirty = true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set new origin size of layout node. You should apply this on node manual size change
|
||||
-- @tparam Layout self @{Layout}
|
||||
-- @tparam vector3 new_origin_size
|
||||
-- @treturn Layout @{Layout}
|
||||
function Layout.set_origin_size(self, new_origin_size)
|
||||
self.origin_size = new_origin_size or self.origin_size
|
||||
self:on_window_resized()
|
||||
-- @tparam boolean is_justify
|
||||
-- @treturn druid.layout @{Layout}
|
||||
function M.set_justify(self, is_justify)
|
||||
self.is_justify = is_justify
|
||||
self.is_dirty = true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set max gui upscale for FIT adjust mode (or side). It happens on bigger render gui screen
|
||||
-- @tparam Layout self @{Layout}
|
||||
-- @tparam number max_gui_upscale
|
||||
-- @treturn Layout @{Layout}
|
||||
function Layout.set_max_gui_upscale(self, max_gui_upscale)
|
||||
self._max_gui_upscale = max_gui_upscale
|
||||
self:on_window_resized()
|
||||
end
|
||||
-- @tparam string type The layout type: "horizontal", "vertical", "horizontal_wrap"
|
||||
-- @treturn druid.layout @{Layout}
|
||||
function M.set_type(self, type)
|
||||
self.type = type
|
||||
self.is_dirty = true
|
||||
|
||||
|
||||
--- Set size for layout node to fit inside it
|
||||
-- @tparam Layout self @{Layout}
|
||||
-- @tparam vector3 target_size
|
||||
-- @treturn Layout @{Layout}
|
||||
function Layout.fit_into_size(self, target_size)
|
||||
self.fit_size = target_size
|
||||
self:on_window_resized()
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set node for layout node to fit inside it. Pass nil to reset
|
||||
-- @tparam Layout self @{Layout}
|
||||
-- @tparam node|nil node
|
||||
-- @treturn Layout @{Layout}
|
||||
function Layout.fit_into_node(self, node)
|
||||
self._fit_node = node
|
||||
self:on_window_resized()
|
||||
-- @tparam boolean is_hug_width
|
||||
-- @tparam boolean is_hug_height
|
||||
-- @treturn druid.layout @{Layout}
|
||||
function M.set_hug_content(self, is_hug_width, is_hug_height)
|
||||
self.is_resize_width = is_hug_width or false
|
||||
self.is_resize_height = is_hug_height or false
|
||||
self.is_dirty = true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set current size for layout node to fit inside it
|
||||
-- @tparam Layout self @{Layout}
|
||||
-- @treturn Layout @{Layout}
|
||||
function Layout.fit_into_window(self)
|
||||
return self:fit_into_size(vmath.vector3(
|
||||
gui.get_width(),
|
||||
gui.get_height(),
|
||||
0))
|
||||
-- @tparam string|node node_or_node_id
|
||||
-- @treturn druid.layout @{Layout}
|
||||
function M.add(self, node_or_node_id)
|
||||
-- Acquire node from entity or by id
|
||||
local node = node_or_node_id
|
||||
if type(node_or_node_id) == "table" then
|
||||
assert(node_or_node_id.node, "The entity should have a node")
|
||||
node = node_or_node_id.node
|
||||
else
|
||||
-- @cast node_or_node_id string|node
|
||||
node = self:get_node(node_or_node_id)
|
||||
end
|
||||
|
||||
-- @cast node node
|
||||
table.insert(self.entities, node)
|
||||
gui.set_parent(node, self.node)
|
||||
|
||||
self.is_dirty = true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
return Layout
|
||||
-- @tparam Layout self @{Layout}
|
||||
-- @treturn druid.layout @{Layout}
|
||||
function M.refresh_layout(self)
|
||||
local layout_node = self.node
|
||||
|
||||
local entities = self.entities
|
||||
local type = self.type -- vertical, horizontal, horizontal_wrap
|
||||
local margin = self.margin -- {x: horizontal, y: vertical} in pixels, between elements
|
||||
local padding = self.padding -- {x: left, y: top, z: right, w: bottom} in pixels
|
||||
local is_justify = self.is_justify
|
||||
local size = gui.get_size(layout_node)
|
||||
local max_width = size.x - padding.x - padding.z
|
||||
local max_height = size.y - padding.y - padding.w
|
||||
local layout_pivot_offset = helper.get_pivot_offset(gui.get_pivot(layout_node)) -- {x: -0.5, y: -0.5} - is left bot, {x: 0.5, y: 0.5} - is right top
|
||||
|
||||
local rows_data = self:calculate_rows_data()
|
||||
local rows = rows_data.rows
|
||||
local row_index = 1
|
||||
local row = rows[row_index]
|
||||
|
||||
-- Current x and Current y is a top left corner of the node
|
||||
local current_x = -row.width * (0.5 + layout_pivot_offset.x)
|
||||
local current_y = rows_data.total_height * (0.5 - layout_pivot_offset.y)
|
||||
|
||||
if is_justify then
|
||||
if (type == "horizontal" or type == "horizontal_wrap") and row.count > 1 then
|
||||
current_x = -max_width * (0.5 + layout_pivot_offset.x)
|
||||
end
|
||||
if type == "vertical" then
|
||||
current_y = max_height * (0.5 - layout_pivot_offset.y)
|
||||
end
|
||||
end
|
||||
|
||||
for index = 1, #entities do
|
||||
local node = entities[index]
|
||||
local node_width = rows_data.nodes_width[node]
|
||||
local node_height = rows_data.nodes_height[node]
|
||||
local pivot_offset = helper.get_pivot_offset(gui.get_pivot(node))
|
||||
|
||||
if node_width > 0 and node_height > 0 then
|
||||
-- Calculate position for current node
|
||||
local position_x, position_y
|
||||
|
||||
if type == "horizontal" then
|
||||
position_x = current_x + node_width * (0.5 + pivot_offset.x)
|
||||
position_y = current_y - row.height * (0.5 - pivot_offset.y)
|
||||
|
||||
local node_margin = margin.x
|
||||
if is_justify and row.count > 1 then
|
||||
node_margin = (max_width - row.width) / (row.count - 1) + margin.x
|
||||
end
|
||||
current_x = current_x + node_width + node_margin
|
||||
end
|
||||
|
||||
if type == "vertical" then
|
||||
position_x = current_x + row.width * (0.5 - pivot_offset.x)
|
||||
position_y = current_y - node_height * (0.5 + pivot_offset.y)
|
||||
|
||||
local node_margin = margin.y
|
||||
if is_justify then
|
||||
node_margin = (max_height - rows_data.total_height) / (#rows - 1) + margin.y
|
||||
end
|
||||
|
||||
current_y = current_y - node_height - node_margin
|
||||
end
|
||||
|
||||
if type == "horizontal_wrap" then
|
||||
local width = row.width
|
||||
if is_justify and row.count > 0 then
|
||||
width = math.max(row.width, max_width)
|
||||
end
|
||||
local new_row_width = width * (0.5 - layout_pivot_offset.x)
|
||||
|
||||
-- Compare with eps due the float loss and element flickering
|
||||
if current_x + node_width - new_row_width > 0.0001 then
|
||||
if row_index < #rows then
|
||||
row_index = row_index + 1
|
||||
row = rows[row_index]
|
||||
end
|
||||
|
||||
current_x = -row.width * (0.5 + layout_pivot_offset.x)
|
||||
current_y = current_y - row.height - margin.y
|
||||
if is_justify and row.count > 1 then
|
||||
current_x = -max_width * (0.5 + layout_pivot_offset.x)
|
||||
end
|
||||
end
|
||||
|
||||
position_x = current_x + node_width * (0.5 + pivot_offset.x)
|
||||
position_y = current_y - row.height * (0.5 - pivot_offset.y)
|
||||
|
||||
local node_margin = margin.x
|
||||
if is_justify and row.count > 1 then
|
||||
node_margin = (max_width - row.width) / (row.count - 1) + margin.x
|
||||
end
|
||||
current_x = current_x + node_width + node_margin
|
||||
end
|
||||
|
||||
do -- Padding offset
|
||||
if layout_pivot_offset.x == -0.5 then
|
||||
position_x = position_x + padding.x
|
||||
end
|
||||
if layout_pivot_offset.y == 0.5 then
|
||||
position_y = position_y - padding.y
|
||||
end
|
||||
if layout_pivot_offset.x == 0.5 then
|
||||
position_x = position_x - padding.z
|
||||
end
|
||||
if layout_pivot_offset.y == -0.5 then
|
||||
position_y = position_y + padding.w
|
||||
end
|
||||
end
|
||||
|
||||
self:set_node_position(node, position_x, position_y)
|
||||
end
|
||||
end
|
||||
|
||||
if self.is_resize_width or self.is_resize_height then
|
||||
if self.is_resize_width then
|
||||
size.x = rows_data.total_width + padding.x + padding.z
|
||||
end
|
||||
if self.is_resize_height then
|
||||
size.y = rows_data.total_height + padding.y + padding.w
|
||||
end
|
||||
gui.set_size(layout_node, size)
|
||||
end
|
||||
|
||||
self.is_dirty = false
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
-- @tparam Layout self @{Layout}
|
||||
-- @treturn druid.layout @{Layout}
|
||||
function M.clear_layout(self)
|
||||
for index = #self.entities, 1, -1 do
|
||||
self.entities[index] = nil
|
||||
end
|
||||
|
||||
self.is_dirty = true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
-- @private
|
||||
-- @tparam node node
|
||||
-- @treturn number, number
|
||||
function M.get_node_size(node)
|
||||
if not gui.is_enabled(node, false) then
|
||||
return 0, 0
|
||||
end
|
||||
|
||||
local scale = gui.get_scale(node)
|
||||
|
||||
-- If node has text - get text size instead of node size
|
||||
if gui.get_text(node) then
|
||||
local text_metrics = helper.get_text_metrics_from_node(node)
|
||||
return text_metrics.width * scale.x, text_metrics.height * scale.y
|
||||
end
|
||||
|
||||
local size = gui.get_size(node)
|
||||
return size.x * scale.x, size.y * scale.y
|
||||
end
|
||||
|
||||
|
||||
-- @private
|
||||
-- @tparam Layout self @{Layout}
|
||||
-- Calculate rows data for layout. Contains total width, height and rows info (width, height, count of elements in row)
|
||||
-- @treturn druid.layout.rows_data
|
||||
function M.calculate_rows_data(self)
|
||||
local entities = self.entities
|
||||
local margin = self.margin
|
||||
local type = self.type
|
||||
local padding = self.padding
|
||||
|
||||
local size = gui.get_size(self.node)
|
||||
local max_width = size.x - padding.x - padding.z
|
||||
|
||||
-- Collect rows info about width, height and count of elements in row
|
||||
local current_row = { width = 0, height = 0, count = 0 }
|
||||
local rows_data = {
|
||||
total_width = 0,
|
||||
total_height = 0,
|
||||
nodes_width = {},
|
||||
nodes_height = {},
|
||||
rows = { current_row }
|
||||
}
|
||||
|
||||
for index = 1, #entities do
|
||||
local node = entities[index]
|
||||
local node_width = rows_data.nodes_width[node]
|
||||
local node_height = rows_data.nodes_height[node]
|
||||
|
||||
-- Get node size if it's not calculated yet
|
||||
if not node_width or not node_height then
|
||||
node_width, node_height = M.get_node_size(node)
|
||||
rows_data.nodes_width[node] = node_width
|
||||
rows_data.nodes_height[node] = node_height
|
||||
end
|
||||
|
||||
if node_width > 0 and node_height > 0 then
|
||||
if type == "horizontal" then
|
||||
current_row.width = current_row.width + node_width + margin.x
|
||||
current_row.height = math.max(current_row.height, node_height)
|
||||
current_row.count = current_row.count + 1
|
||||
end
|
||||
|
||||
if type == "vertical" then
|
||||
if current_row.count > 0 then
|
||||
current_row = { width = 0, height = 0, count = 0 }
|
||||
table.insert(rows_data.rows, current_row)
|
||||
end
|
||||
|
||||
current_row.width = math.max(current_row.width, node_width + margin.x)
|
||||
current_row.height = node_height
|
||||
current_row.count = current_row.count + 1
|
||||
end
|
||||
|
||||
if type == "horizontal_wrap" then
|
||||
if current_row.width + node_width > max_width and current_row.count > 0 then
|
||||
current_row = { width = 0, height = 0, count = 0 }
|
||||
table.insert(rows_data.rows, current_row)
|
||||
end
|
||||
|
||||
current_row.width = current_row.width + node_width + margin.x
|
||||
current_row.height = math.max(current_row.height, node_height)
|
||||
current_row.count = current_row.count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove last margin of each row
|
||||
-- Calculate total width and height
|
||||
local rows_count = #rows_data.rows
|
||||
for index = 1, rows_count do
|
||||
local row = rows_data.rows[index]
|
||||
if row.width > 0 then
|
||||
row.width = row.width - margin.x
|
||||
end
|
||||
|
||||
rows_data.total_width = math.max(rows_data.total_width, row.width)
|
||||
rows_data.total_height = rows_data.total_height + row.height
|
||||
end
|
||||
|
||||
rows_data.total_height = rows_data.total_height + margin.y * (rows_count - 1)
|
||||
return rows_data
|
||||
end
|
||||
|
||||
-- @private
|
||||
-- @tparam node node
|
||||
-- @tparam number x
|
||||
-- @tparam number y
|
||||
-- @treturn node
|
||||
function M:set_node_position(node, x, y)
|
||||
local position = gui.get_position(node)
|
||||
position.x = x
|
||||
position.y = y
|
||||
gui.set_position(node, position)
|
||||
|
||||
return node
|
||||
end
|
||||
|
||||
return M
|
||||
|
@@ -151,6 +151,8 @@ function Slider.on_input(self, action_id, action)
|
||||
self.value = (self.target_pos.y - self.start_pos.y) / self.dist.y
|
||||
end
|
||||
|
||||
self.value = math.abs(self.value)
|
||||
|
||||
if self.steps then
|
||||
local closest_dist = 1000
|
||||
local closest = nil
|
||||
|
@@ -42,15 +42,15 @@ M["button"] = {
|
||||
end,
|
||||
|
||||
on_click_disabled = function(self, node)
|
||||
settings.play_sound(M.button.BTN_SOUND_DISABLED)
|
||||
local start_pos = self.start_pos
|
||||
gui.animate(node, "position.x", start_pos.x - 3, gui.EASING_OUTSINE, 0.05, 0, function()
|
||||
gui.animate(node, "position.x", start_pos.x + 3, gui.EASING_OUTSINE, 0.1, 0, function()
|
||||
gui.animate(node, "position.x", start_pos.x, gui.EASING_OUTSINE, 0.05)
|
||||
end)
|
||||
end)
|
||||
end,
|
||||
|
||||
on_set_enabled = function(self, node, state)
|
||||
if state then
|
||||
gui.set_color(node, M.button.ENABLED_COLOR)
|
||||
else
|
||||
gui.set_color(node, M.button.DISABLED_COLOR)
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
|
@@ -247,7 +247,7 @@ function DruidInstance.initialize(self, context, style)
|
||||
end
|
||||
|
||||
|
||||
--- Create new component.
|
||||
-- Create new component.
|
||||
-- @tparam DruidInstance self
|
||||
-- @tparam BaseComponent component Component module
|
||||
-- @tparam any ... Other component params to pass it to component:init function
|
||||
@@ -776,9 +776,8 @@ end
|
||||
-- @tparam DruidInstance self
|
||||
-- @tparam string|node node The_node id or gui.get_node(node_id).
|
||||
-- @tparam string mode The layout mode
|
||||
-- @tparam function|nil on_size_changed_callback The callback on window resize
|
||||
-- @treturn Layout @{Layout} component
|
||||
function DruidInstance.new_layout(self, node, mode, on_size_changed_callback)
|
||||
function DruidInstance.new_layout(self, node, mode)
|
||||
return helper.require_component_message("layout")
|
||||
end
|
||||
|
||||
|
@@ -688,7 +688,8 @@ local function matcherGenerator(regex, plain)
|
||||
local sum = 0
|
||||
local bc, ec = utf8sub(str, 1, 1), utf8sub(str, 2, 2)
|
||||
local skip = len(bc) + len(ec)
|
||||
bc, ec = utf8unicode(bc), utf8unicode(ec)
|
||||
bc = utf8unicode(bc) --[[@as string]]
|
||||
ec = utf8unicode(ec) --[[@as string]]
|
||||
return function(cC)
|
||||
if cC == ec and sum > 0 then
|
||||
sum = sum - 1
|
||||
|
@@ -3,18 +3,13 @@ local component = require("druid.component")
|
||||
---@class component_name : druid.base_component
|
||||
local Component = component.create("component_name")
|
||||
|
||||
local SCHEME = {
|
||||
ROOT = "root",
|
||||
BUTTON = "button",
|
||||
}
|
||||
|
||||
|
||||
-- Component constructor. Template name and nodes are optional. Pass it if you use it in your component
|
||||
function Component:init(template, nodes)
|
||||
self.druid = self:get_druid(template, nodes)
|
||||
self.root = self:get_node(SCHEME.ROOT)
|
||||
self.root = self:get_node("root")
|
||||
|
||||
self.button = self.druid:new_button(SCHEME.BUTTON, function() end)
|
||||
self.button = self.druid:new_button("button", function() end)
|
||||
end
|
||||
|
||||
|
||||
|
@@ -3,13 +3,6 @@ local component = require("druid.component")
|
||||
---@class component_name : druid.base_component
|
||||
local Component = component.create("component_name")
|
||||
|
||||
-- Scheme of component gui nodes
|
||||
local SCHEME = {
|
||||
ROOT = "root",
|
||||
BUTTON = "button",
|
||||
}
|
||||
|
||||
|
||||
-- Component constructor. Template name and nodes are optional. Pass it if you use it in your component
|
||||
function Component:init(template, nodes)
|
||||
-- If your component is gui template, pass the template name and set it
|
||||
@@ -18,7 +11,7 @@ function Component:init(template, nodes)
|
||||
self.druid = self:get_druid(template, nodes)
|
||||
|
||||
-- self:get_node will auto process component template and nodes
|
||||
self.root = self:get_node(SCHEME.ROOT)
|
||||
self.root = self:get_node("root")
|
||||
|
||||
end
|
||||
|
||||
|
Reference in New Issue
Block a user