Update annotations P.1

This commit is contained in:
Insality
2024-10-29 20:31:45 +02:00
parent a46f38734e
commit c85d66fdca
40 changed files with 1458 additions and 3941 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,65 +1,24 @@
-- Copyright (c) 2023 Maksim Tuprikov <insality@gmail.com>. This code is licensed under MIT license
--- Component with event on back and backspace button.
-- <b># Overview #</b>
--
-- Back Handler is recommended to put in every game window to close it
-- or in main screen to call settings window.
--
-- <b># Notes #</b>
--
-- • Back Handler inheritance @{BaseComponent}, you can use all of its methods in addition to those described here.
--
-- • Back Handler react on release action ACTION_BACK or ACTION_BACKSPACE
-- @usage
-- local callback = function(self, params) ... end
--
-- local params = {}
-- local back_handler = self.druid:new_back_handler(callback, [params])
-- @module BackHandler
-- @within BaseComponent
-- @alias druid.back_handler
--- The @{DruidEvent} Event on back handler action.
--
-- Trigger on input action ACTION_BACK or ACTION_BACKSPACE
-- @usage
-- -- Subscribe additional callbacks:
-- back_handler.on_back:subscribe(callback)
-- @tfield DruidEvent on_back @{DruidEvent}
--- Custom args to pass in the callback
-- @usage
-- -- Replace params on runtime:
-- back_handler.params = { ... }
-- @tfield any|nil params
---
local Event = require("druid.event")
local event = require("druid.event")
local const = require("druid.const")
local component = require("druid.component")
local BackHandler = component.create("back_handler")
---@class druid.back_handler: druid.base_component
---@field on_back druid.event Trigger on back handler action, fun(self, params)
---@field params any|nil Custom args to pass in the callback
local M = component.create("back_handler")
--- The @{BackHandler} constructor
-- @tparam BackHandler self @{BackHandler}
-- @tparam function callback @The callback(self, custom_args) to call on back event
-- @tparam any|nil custom_args Button events custom arguments
-- @local
function BackHandler.init(self, callback, custom_args)
self.params = custom_args
self.on_back = Event(callback)
---@param callback function|nil
---@param params any|nil
function M:init(callback, params)
self.params = params
self.on_back = event.create(callback)
end
--- Component input handler
-- @tparam BackHandler self @{BackHandler}
-- @tparam string action_id on_input action id
-- @tparam table action on_input action
-- @local
function BackHandler.on_input(self, action_id, action)
---@param action_id string
---@param action table
function M:on_input(action_id, action)
if not action.released then
return false
end
@@ -73,4 +32,4 @@ function BackHandler.on_input(self, action_id, action)
end
return BackHandler
return M

View File

@@ -1,50 +1,22 @@
-- Copyright (c) 2023 Maksim Tuprikov <insality@gmail.com>. This code is licensed under MIT license
--- Component to consume input in special zone defined by GUI node.
-- <b># Overview #</b>
--
-- <b># Notes #</b>
--
-- Blocker consume input if `gui.pick_node` works on it.
--
-- • Blocker inheritance @{BaseComponent}, you can use all of its methods in addition to those described here.
--
-- • Blocker initial enabled state is `gui.is_enabled(node, true)`
--
-- • The Blocker node should be enabled to capture the input
-- @usage
-- local node = gui.get_node("blocker_node")
-- local blocker = self.druid:new_blocker(node)
-- @module Blocker
-- @within BaseComponent
-- @alias druid.blocker
---Blocker node
-- @tfield node node
---
local const = require("druid.const")
local component = require("druid.component")
local Blocker = component.create("blocker")
---@class druid.blocker: druid.base_component
---@field node node
---@field private _is_enabled boolean
local M = component.create("blocker")
--- The @{Blocker} constructor
-- @tparam Blocker self @{Blocker}
-- @tparam node node Gui node
function Blocker.init(self, node)
---@param node node
function M:init(node)
self.node = self:get_node(node)
self._is_enabled = gui.is_enabled(self.node, true)
end
--- Component input handler
-- @tparam Blocker self @{Blocker}
-- @tparam string action_id on_input action id
-- @tparam table action on_input action
-- @local
function Blocker.on_input(self, action_id, action)
---@param action_id string
---@param action table
function M:on_input(action_id, action)
if action_id ~= const.ACTION_TOUCH and
action_id ~= const.ACTION_MULTITOUCH and
action_id ~= nil then
@@ -67,22 +39,21 @@ function Blocker.on_input(self, action_id, action)
end
--- Set enabled blocker component state.
--
-- Don't change node enabled state itself.
-- @tparam Blocker self @{Blocker}
-- @tparam boolean|nil state Enabled state
function Blocker.set_enabled(self, state)
---Set blocker enabled state
---@param state boolean
---@return druid.blocker self
function M:set_enabled(state)
self._is_enabled = state
return self
end
--- Return blocker enabled state
-- @tparam Blocker self @{Blocker}
-- @treturn boolean @True, if blocker is enabled
function Blocker.is_enabled(self)
---Get blocker enabled state
---@return boolean
function M:is_enabled()
return self._is_enabled
end
return Blocker
return M

View File

@@ -35,16 +35,16 @@
-- @alias druid.button
--- The @{DruidEvent}: Event on successful release action over button.
--- The DruidEvent: Event on successful release action over button.
-- @usage
-- -- Custom args passed in Button constructor
-- button.on_click:subscribe(function(self, custom_args, button_instance)
-- print("On button click!")
-- end)
-- @tfield DruidEvent on_click @{DruidEvent}
-- @tfield DruidEvent on_click DruidEvent
--- The @{DruidEvent}: Event on repeated action over button.
--- The DruidEvent: Event on repeated action over button.
--
-- This callback will be triggered if user hold the button. The repeat rate pick from `input.repeat_interval` in game.project
-- @usage
@@ -52,10 +52,10 @@
-- button.on_repeated_click:subscribe(function(self, custom_args, button_instance, click_count)
-- print("On repeated Button click!")
-- end)
-- @tfield DruidEvent on_repeated_click @{DruidEvent}
-- @tfield DruidEvent on_repeated_click DruidEvent
--- The @{DruidEvent}: Event on long tap action over button.
--- The DruidEvent: Event on long tap action over button.
--
-- This callback will be triggered if user pressed the button and hold the some amount of time.
-- The amount of time picked from button style param: LONGTAP_TIME
@@ -64,10 +64,10 @@
-- button.on_long_click:subscribe(function(self, custom_args, button_instance, hold_time)
-- print("On long Button click!")
-- end)
-- @tfield DruidEvent on_long_click @{DruidEvent}
-- @tfield DruidEvent on_long_click DruidEvent
--- The @{DruidEvent}: Event on double tap action over button.
--- The DruidEvent: Event on double tap action over button.
--
-- If secondary click was too fast after previous one, the double
-- click will be called instead usual click (if on_double_click subscriber exists)
@@ -76,10 +76,10 @@
-- button.on_double_click:subscribe(function(self, custom_args, button_instance, click_amount)
-- print("On double Button click!")
-- end)
-- @tfield DruidEvent on_double_click @{DruidEvent}
-- @tfield DruidEvent on_double_click DruidEvent
--- The @{DruidEvent}: Event calls every frame before on_long_click event.
--- The DruidEvent: Event calls every frame before on_long_click event.
--
-- If long_click subscriber exists, the on_hold_callback will be called before long_click trigger.
--
@@ -89,10 +89,10 @@
-- button.on_double_click:subscribe(function(self, custom_args, button_instance, time)
-- print("On hold Button callback!")
-- end)
-- @tfield DruidEvent on_hold_callback @{DruidEvent}
-- @tfield DruidEvent on_hold_callback DruidEvent
--- The @{DruidEvent}: Event calls if click event was outside of button.
--- The DruidEvent: Event calls if click event was outside of button.
--
-- This event will be triggered for each button what was not clicked on user click action
--
@@ -102,16 +102,16 @@
-- button.on_click_outside:subscribe(function(self, custom_args, button_instance)
-- print("On click Button outside!")
-- end)
-- @tfield DruidEvent on_click_outside @{DruidEvent}
-- @tfield DruidEvent on_click_outside DruidEvent
--- The @{DruidEvent}: Event triggered if button was pressed by user.
--- The DruidEvent: Event triggered if button was pressed by user.
-- @usage
-- -- Custom args passed in Button constructor
-- button.on_pressed:subscribe(function(self, custom_args, button_instance)
-- print("On Button pressed!")
-- end)
-- @tfield DruidEvent on_pressed @{DruidEvent}
-- @tfield DruidEvent on_pressed DruidEvent
--- Button trigger node
-- @tfield node node
@@ -128,8 +128,8 @@
---Custom args for any Button event. Setup in Button constructor
-- @tfield any params
--- The @{Hover}: Button Hover component
-- @tfield Hover hover @{Hover}
--- The Hover: Button Hover component
-- @tfield Hover hover Hover
--- Additional button click area, defined by another GUI node
-- @tfield node|nil click_zone
@@ -141,7 +141,26 @@ local const = require("druid.const")
local helper = require("druid.helper")
local component = require("druid.component")
local Button = component.create("button")
---@class druid.button: druid.base_component
---@field on_click druid.event
---@field on_pressed druid.event
---@field on_repeated_click druid.event
---@field on_long_click druid.event
---@field on_double_click druid.event
---@field on_hold_callback druid.event
---@field on_click_outside druid.event
---@field node node
---@field node_id hash
---@field anim_node node
---@field params any
---@field hover druid.hover
---@field click_zone node
---@field start_scale vector3
---@field start_pos vector3
---@field disabled boolean
---@field key_trigger hash
---@field style table
local M = component.create("button")
local function is_input_match(self, action_id)
@@ -271,7 +290,7 @@ end
-- @tfield function on_hover function(self, node, hover_state)
-- @tfield function on_mouse_hover function(self, node, hover_state)
-- @tfield function on_set_enabled function(self, node, enabled_state)
function Button.on_style_change(self, style)
function M:on_style_change(style)
self.style = {}
self.style.LONGTAP_TIME = style.LONGTAP_TIME or 0.4
self.style.AUTOHOLD_TRIGGER = style.AUTOHOLD_TRIGGER or 0.8
@@ -285,22 +304,21 @@ function Button.on_style_change(self, style)
end
--- The @{Button} constructor
-- @tparam Button self @{Button}
-- @tparam string|node node The node_id or gui.get_node(node_id)
-- @tparam function callback On click button callback
-- @tparam any|nil custom_args Button events custom arguments
-- @tparam string|node|nil anim_node Node to animate instead of trigger node.
function Button.init(self, node, callback, custom_args, anim_node)
---Button constructor
---@param node_or_node_id node|string Node name or GUI Node itself.
---@param callback fun()|nil Callback on button click
---@param custom_args any|nil Custom args for any Button event
---@param anim_node node|string|nil Node to animate instead of trigger node.
function M:init(node_or_node_id, callback, custom_args, anim_node)
self.druid = self:get_druid()
self.node = self:get_node(node)
self.node = self:get_node(node_or_node_id)
self.node_id = gui.get_id(self.node)
self.anim_node = anim_node and self:get_node(anim_node) or self.node
self.start_scale = gui.get_scale(self.anim_node)
self.start_pos = gui.get_position(self.anim_node)
self.params = custom_args
self.hover = self.druid:new_hover(node, on_button_hover)
self.hover = self.druid:new_hover(node_or_node_id, on_button_hover)
self.hover.on_mouse_hover:subscribe(on_button_mouse_hover)
self.click_zone = nil
self.is_repeated_started = false
@@ -325,7 +343,7 @@ function Button.init(self, node, callback, custom_args, anim_node)
end
function Button.on_late_init(self)
function M:on_late_init()
if not self.click_zone and const.IS_STENCIL_CHECK then
local stencil_node = helper.get_closest_stencil_node(self.node)
if stencil_node then
@@ -335,7 +353,7 @@ function Button.on_late_init(self)
end
function Button.on_input(self, action_id, action)
function M:on_input(action_id, action)
if not is_input_match(self, action_id) then
return false
end
@@ -416,14 +434,14 @@ function Button.on_input(self, action_id, action)
end
function Button.on_input_interrupt(self)
function M:on_input_interrupt()
self.can_action = false
self.hover:set_hover(false)
self.hover:set_mouse_hover(false)
end
function Button.on_message_input(self, node_id, message)
function M:on_message_input(node_id, message)
if node_id ~= self.node_id or self.disabled or not gui.is_enabled(self.node) then
return false
end
@@ -451,13 +469,13 @@ end
--- Set button enabled state.
-- The style.on_set_enabled will be triggered.
-- Disabled button is not clickable.
-- @tparam Button self @{Button}
-- @tparam Button self Button
-- @tparam boolean|nil state Enabled state
-- @treturn Button Current button instance
-- @usage
-- button:set_enabled(false)
-- button:set_enabled(true)
function Button.set_enabled(self, state)
function M:set_enabled(state)
self.disabled = not state
self.hover:set_enabled(state)
self.style.on_set_enabled(self, self.node, state)
@@ -469,11 +487,11 @@ end
--- Get button enabled state.
--
-- By default all Buttons is enabled on creating.
-- @tparam Button self @{Button}
-- @tparam Button self Button
-- @treturn boolean @True, if button is enabled now, False overwise
-- @usage
-- local is_enabled = button:is_enabled()
function Button.is_enabled(self)
function M:is_enabled()
return not self.disabled
end
@@ -482,12 +500,12 @@ end
-- Useful to restrict click outside out stencil node or scrollable content.
--
-- This functions calls automatically if you don't disable it in game.project: druid.no_stencil_check
-- @tparam Button self @{Button}
-- @tparam Button self Button
-- @tparam node|string|nil zone Gui node
-- @treturn Button Current button instance
-- @usage
-- button:set_click_zone("stencil_node")
function Button.set_click_zone(self, zone)
function M:set_click_zone(zone)
self.click_zone = self:get_node(zone)
self.hover:set_click_zone(zone)
@@ -496,12 +514,12 @@ end
--- Set key name to trigger this button by keyboard.
-- @tparam Button self @{Button}
-- @tparam Button self Button
-- @tparam hash|string key The action_id of the input key
-- @treturn Button Current button instance
-- @usage
-- button:set_key_trigger("key_space")
function Button.set_key_trigger(self, key)
function M:set_key_trigger(key)
self.key_trigger = hash(key)
return self
@@ -513,7 +531,7 @@ end
-- @treturn hash The action_id of the input key
-- @usage
-- local key_hash = button:get_key_trigger()
function Button.get_key_trigger(self)
function M:get_key_trigger()
return self.key_trigger
end
@@ -523,7 +541,7 @@ end
-- @tparam function|nil check_function Should return true or false. If true - button can be pressed.
-- @tparam function|nil failure_callback Function will be called on button click, if check function return false
-- @treturn Button Current button instance
function Button.set_check_function(self, check_function, failure_callback)
function M:set_check_function(check_function, failure_callback)
self._check_function = check_function
self._failure_callback = failure_callback
end
@@ -540,10 +558,10 @@ end
-- @treturn Button Current button instance
-- @usage
-- button:set_web_user_interaction(true)
function Button.set_web_user_interaction(self, is_web_mode)
function M:set_web_user_interaction(is_web_mode)
self._is_html5_mode = not not (is_web_mode and html5)
return self
end
return Button
return M

View File

@@ -14,19 +14,19 @@
-- @tfield node node
--- Event on touch start callback(self)
-- @tfield DruidEvent on_touch_start @{DruidEvent}
-- @tfield DruidEvent on_touch_start DruidEvent
--- Event on touch end callback(self)
-- @tfield DruidEvent on_touch_end @{DruidEvent}
-- @tfield DruidEvent on_touch_end DruidEvent
--- Event on drag start callback(self, touch)
-- @tfield DruidEvent on_drag_start @{DruidEvent}
-- @tfield DruidEvent on_drag_start DruidEvent
--- on drag progress callback(self, dx, dy, total_x, total_y, touch)
-- @tfield DruidEvent on_drag Event @{DruidEvent}
-- @tfield DruidEvent on_drag Event DruidEvent
--- Event on drag end callback(self, total_x, total_y, touch)
-- @tfield DruidEvent on_drag_end @{DruidEvent}
-- @tfield DruidEvent on_drag_end DruidEvent
--- Is component now touching
-- @tfield boolean is_touch
@@ -62,7 +62,31 @@ local const = require("druid.const")
local helper = require("druid.helper")
local component = require("druid.component")
local Drag = component.create("drag", const.PRIORITY_INPUT_HIGH)
---@class druid.drag: druid.base_component
---@field node node
---@field on_touch_start druid.event
---@field on_touch_end druid.event
---@field on_drag_start druid.event
---@field on_drag druid.event
---@field on_drag_end druid.event
---@field style table
---@field click_zone node
---@field is_touch boolean
---@field is_drag boolean
---@field can_x boolean
---@field can_y boolean
---@field dx number
---@field dy number
---@field touch_id number
---@field x number
---@field y number
---@field screen_x number
---@field screen_y number
---@field touch_start_pos vector3
---@field private _is_enabled boolean
---@field private _x_koef number
---@field private _y_koef number
local M = component.create("drag", const.PRIORITY_INPUT_HIGH)
local function start_touch(self, touch)
@@ -177,19 +201,18 @@ end
-- @table style
-- @tfield number|nil DRAG_DEADZONE Distance in pixels to start dragging. Default: 10
-- @tfield boolean|nil NO_USE_SCREEN_KOEF If screen aspect ratio affects on drag values. Default: false
function Drag.on_style_change(self, style)
function M:on_style_change(style)
self.style = {}
self.style.DRAG_DEADZONE = style.DRAG_DEADZONE or 10
self.style.NO_USE_SCREEN_KOEF = style.NO_USE_SCREEN_KOEF or false
end
--- The @{Drag} constructor
-- @tparam Drag self @{Drag}
-- @tparam node node GUI node to detect dragging
-- @tparam function on_drag_callback Callback for on_drag_event(self, dx, dy)
function Drag.init(self, node, on_drag_callback)
self.node = self:get_node(node)
---Drag constructor
---@param node_or_node_id node|string
---@param on_drag_callback function
function M:init(node_or_node_id, on_drag_callback)
self.node = self:get_node(node_or_node_id)
self.dx = 0
self.dy = 0
@@ -219,7 +242,7 @@ function Drag.init(self, node, on_drag_callback)
end
function Drag.on_late_init(self)
function M:on_late_init()
if not self.click_zone and const.IS_STENCIL_CHECK then
local stencil_node = helper.get_closest_stencil_node(self.node)
if stencil_node then
@@ -229,7 +252,7 @@ function Drag.on_late_init(self)
end
function Drag.on_window_resized(self)
function M:on_window_resized()
local x_koef, y_koef = helper.get_screen_aspect_koef()
self._x_koef = x_koef
self._y_koef = y_koef
@@ -237,14 +260,17 @@ function Drag.on_window_resized(self)
end
function Drag.on_input_interrupt(self)
function M:on_input_interrupt()
if self.is_drag or self.is_touch then
end_touch(self)
end
end
function Drag.on_input(self, action_id, action)
---@local
---@param action_id string
---@param action table
function M:on_input(action_id, action)
if action_id ~= const.ACTION_TOUCH and action_id ~= const.ACTION_MULTITOUCH then
return false
end
@@ -321,29 +347,31 @@ function Drag.on_input(self, action_id, action)
end
--- Strict drag click area. Useful for
-- restrict events outside stencil node
-- @tparam Drag self @{Drag}
-- @tparam node|string|nil node Gui node
function Drag.set_click_zone(self, node)
---Set Drag click zone
---@param node node|string|nil
---@return druid.drag self
function M:set_click_zone(node)
self.click_zone = self:get_node(node)
return self
end
--- Set Drag input enabled or disabled
-- @tparam Drag self @{Drag}
-- @tparam boolean|nil is_enabled
function Drag.set_enabled(self, is_enabled)
---Set Drag component enabled state.
---@param is_enabled boolean
---@return druid.drag self
function M:set_enabled(is_enabled)
self._is_enabled = is_enabled
return self
end
--- Check if Drag component is enabled
-- @tparam Drag self @{Drag}
-- @treturn boolean
function Drag.is_enabled(self)
---Check if Drag component is enabled
---@return boolean
function M:is_enabled()
return self._is_enabled
end
return Drag
return M

View File

@@ -9,10 +9,10 @@
-- @tfield node node
--- On hover callback(self, state, hover_instance)
-- @tfield DruidEvent on_hover @{DruidEvent}
-- @tfield DruidEvent on_hover DruidEvent
--- On mouse hover callback(self, state, hover_instance)
-- @tfield DruidEvent on_mouse_hover @{DruidEvent}
-- @tfield DruidEvent on_mouse_hover DruidEvent
---
@@ -21,15 +21,25 @@ local const = require("druid.const")
local helper = require("druid.helper")
local component = require("druid.component")
local Hover = component.create("hover")
---@class druid.hover: druid.base_component
---@field node node
---@field on_hover druid.event
---@field on_mouse_hover druid.event
---@field style table
---@field click_zone node
---@field private _is_hovered boolean
---@field private _is_mouse_hovered boolean
---@field private _is_enabled boolean
---@field private _is_mobile boolean
local M = component.create("hover")
--- The @{Hover} constructor
-- @tparam Hover self @{Hover}
--- The Hover constructor
-- @tparam Hover self Hover
-- @tparam node node Gui node
-- @tparam function on_hover_callback Hover callback
-- @tparam function on_mouse_hover On mouse hover callback
function Hover.init(self, node, on_hover_callback, on_mouse_hover)
function M:init(node, on_hover_callback, on_mouse_hover)
self.node = self:get_node(node)
self._is_hovered = false
@@ -42,7 +52,7 @@ function Hover.init(self, node, on_hover_callback, on_mouse_hover)
end
function Hover.on_late_init(self)
function M:on_late_init()
if not self.click_zone and const.IS_STENCIL_CHECK then
local stencil_node = helper.get_closest_stencil_node(self.node)
if stencil_node then
@@ -58,14 +68,14 @@ end
-- @table style
-- @tfield[opt] string ON_HOVER_CURSOR Mouse hover style on node hover
-- @tfield[opt] string ON_MOUSE_HOVER_CURSOR Mouse hover style on node mouse hover
function Hover.on_style_change(self, style)
function M:on_style_change(style)
self.style = {}
self.style.ON_HOVER_CURSOR = style.ON_HOVER_CURSOR or nil
self.style.ON_MOUSE_HOVER_CURSOR = style.ON_MOUSE_HOVER_CURSOR or nil
end
function Hover.on_input(self, action_id, action)
function M:on_input(action_id, action)
if action_id ~= const.ACTION_TOUCH and action_id ~= nil then
return false
end
@@ -99,15 +109,15 @@ function Hover.on_input(self, action_id, action)
end
function Hover.on_input_interrupt(self)
function M:on_input_interrupt()
self:set_hover(false)
end
--- Set hover state
-- @tparam Hover self @{Hover}
-- @tparam Hover self Hover
-- @tparam boolean|nil state The hover state
function Hover.set_hover(self, state)
function M:set_hover(state)
if self._is_hovered == state then
return
end
@@ -122,17 +132,17 @@ end
--- Return current hover state. True if touch action was on the node at current time
-- @tparam Hover self @{Hover}
-- @tparam Hover self Hover
-- @treturn boolean The current hovered state
function Hover.is_hovered(self)
function M:is_hovered()
return self._is_hovered
end
--- Set mouse hover state
-- @tparam Hover self @{Hover}
-- @tparam Hover self Hover
-- @tparam boolean|nil state The mouse hover state
function Hover.set_mouse_hover(self, state)
function M:set_mouse_hover(state)
if self._is_mouse_hovered == state then
return
end
@@ -147,18 +157,18 @@ end
--- Return current hover state. True if nil action_id (usually desktop mouse) was on the node at current time
-- @tparam Hover self @{Hover}
-- @tparam Hover self Hover
-- @treturn boolean The current hovered state
function Hover.is_mouse_hovered(self)
function M:is_mouse_hovered()
return self._is_mouse_hovered
end
--- Strict hover click area. Useful for
-- no click events outside stencil node
-- @tparam Hover self @{Hover}
-- @tparam Hover self Hover
-- @tparam node|string|nil zone Gui node
function Hover.set_click_zone(self, zone)
function M:set_click_zone(zone)
self.click_zone = self:get_node(zone)
end
@@ -166,9 +176,9 @@ end
--- Set enable state of hover component.
-- If hover is not enabled, it will not generate
-- any hover events
-- @tparam Hover self @{Hover}
-- @tparam Hover self Hover
-- @tparam boolean|nil state The hover enabled state
function Hover.set_enabled(self, state)
function M:set_enabled(state)
self._is_enabled = state
if not state then
@@ -183,16 +193,17 @@ end
--- Return current hover enabled state
-- @tparam Hover self @{Hover}
-- @tparam Hover self Hover
-- @treturn boolean The hover enabled state
function Hover.is_enabled(self)
function M:is_enabled()
return self._is_enabled
end
-- Internal cursor stack
local cursor_stack = {}
function Hover:_set_cursor(priority, cursor)
---@local
function M:_set_cursor(priority, cursor)
if not defos then
return
end
@@ -217,4 +228,4 @@ function Hover:_set_cursor(priority, cursor)
end
return Hover
return M

View File

@@ -39,13 +39,13 @@
--- On scroll move callback(self, position)
-- @tfield DruidEvent on_scroll @{DruidEvent}
-- @tfield DruidEvent on_scroll DruidEvent
--- On scroll_to function callback(self, target, is_instant)
-- @tfield DruidEvent on_scroll_to @{DruidEvent}
-- @tfield DruidEvent on_scroll_to DruidEvent
--- On scroll_to_index function callback(self, index, point)
-- @tfield DruidEvent on_point_scroll @{DruidEvent}
-- @tfield DruidEvent on_point_scroll DruidEvent
--- Scroll view node
-- @tfield node view_node
@@ -75,7 +75,7 @@
-- @tfield vector3 available_size
--- Drag Druid component
-- @tfield Drag drag @{Drag}
-- @tfield Drag drag Drag
--- Current index of points of interests
-- @tfield number|nil selected
@@ -90,7 +90,16 @@ local const = require("druid.const")
local helper = require("druid.helper")
local component = require("druid.component")
local Scroll = component.create("scroll")
---@class druid.scroll: druid.base_component
---@field on_scroll druid.event
---@field on_scroll_to druid.event
---@field on_point_scroll druid.event
---@field view_node node
---@field view_border vector4
---@field content_node node
---@field view_size vector3
---@field position vector3
local M = component.create("scroll")
local function inverse_lerp(min, max, current)
@@ -138,7 +147,7 @@ end
-- @tfield boolean|nil WHEEL_SCROLL_SPEED The scroll speed via mouse wheel scroll or touchpad. Set to 0 to disable wheel scrolling. Default: 0
-- @tfield boolean|nil WHEEL_SCROLL_INVERTED If true, invert direction for touchpad and mouse wheel scroll. Default: false
-- @tfield boolean|nil WHEEL_SCROLL_BY_INERTION If true, wheel will add inertion to scroll. Direct set position otherwise.. Default: false
function Scroll.on_style_change(self, style)
function M:on_style_change(style)
self.style = {}
self.style.EXTRA_STRETCH_SIZE = style.EXTRA_STRETCH_SIZE or 0
self.style.ANIM_SPEED = style.ANIM_SPEED or 0.2
@@ -161,11 +170,11 @@ function Scroll.on_style_change(self, style)
end
--- The @{Scroll} constructor
-- @tparam Scroll self @{Scroll}
--- The Scroll constructor
-- @tparam Scroll self Scroll
-- @tparam string|node view_node GUI view scroll node
-- @tparam string|node content_node GUI content scroll node
function Scroll.init(self, view_node, content_node)
function M:init(view_node, content_node)
self.druid = self:get_druid()
self.view_node = self:get_node(view_node)
@@ -203,7 +212,7 @@ function Scroll.init(self, view_node, content_node)
end
function Scroll.on_late_init(self)
function M:on_late_init()
if not self.click_zone and const.IS_STENCIL_CHECK then
local stencil_node = helper.get_closest_stencil_node(self.node)
if stencil_node then
@@ -213,12 +222,12 @@ function Scroll.on_late_init(self)
end
function Scroll.on_layout_change(self)
function M:on_layout_change()
gui.set_position(self.content_node, self.position)
end
function Scroll.update(self, dt)
function M:update(dt)
if self.is_animate then
self.position.x = gui.get(self.content_node, "position.x")
self.position.y = gui.get(self.content_node, "position.y")
@@ -233,23 +242,23 @@ function Scroll.update(self, dt)
end
function Scroll.on_input(self, action_id, action)
function M:on_input(action_id, action)
return self:_process_scroll_wheel(action_id, action)
end
function Scroll.on_remove(self)
function M:on_remove()
self:bind_grid(nil)
end
--- Start scroll to target point.
-- @tparam Scroll self @{Scroll}
-- @tparam Scroll self Scroll
-- @tparam vector3 point Target point
-- @tparam boolean|nil is_instant Instant scroll flag
-- @usage scroll:scroll_to(vmath.vector3(0, 50, 0))
-- @usage scroll:scroll_to(vmath.vector3(0), true)
function Scroll.scroll_to(self, point, is_instant)
function M:scroll_to(point, is_instant)
local b = self.available_pos
local target = vmath.vector3(
self._is_horizontal_scroll and -point.x or self.target_position.x,
@@ -278,10 +287,10 @@ end
--- Scroll to item in scroll by point index.
-- @tparam Scroll self @{Scroll}
-- @tparam Scroll self Scroll
-- @tparam number index Point index
-- @tparam boolean|nil skip_cb If true, skip the point callback
function Scroll.scroll_to_index(self, index, skip_cb)
function M:scroll_to_index(index, skip_cb)
if not self.points then
return
end
@@ -301,11 +310,11 @@ end
--- Start scroll to target scroll percent
-- @tparam Scroll self @{Scroll}
-- @tparam Scroll self Scroll
-- @tparam vector3 percent target percent
-- @tparam boolean|nil is_instant instant scroll flag
-- @usage scroll:scroll_to_percent(vmath.vector3(0.5, 0, 0))
function Scroll.scroll_to_percent(self, percent, is_instant)
function M:scroll_to_percent(percent, is_instant)
local border = self.available_pos
local pos = vmath.vector3(
@@ -327,9 +336,9 @@ end
--- Return current scroll progress status.
-- Values will be in [0..1] interval
-- @tparam Scroll self @{Scroll}
-- @tparam Scroll self Scroll
-- @treturn vector3 New vector with scroll progress values
function Scroll.get_percent(self)
function M:get_percent()
local x_perc = 1 - inverse_lerp(self.available_pos.x, self.available_pos.z, self.position.x)
local y_perc = inverse_lerp(self.available_pos.w, self.available_pos.y, self.position.y)
@@ -339,11 +348,11 @@ end
--- Set scroll content size.
-- It will change content gui node size
-- @tparam Scroll self @{Scroll}
-- @tparam Scroll self Scroll
-- @tparam vector3 size The new size for content node
-- @tparam vector3|nil offset Offset value to set, where content is starts
-- @treturn druid.scroll Current scroll instance
function Scroll.set_size(self, size, offset)
function M:set_size(size, offset)
if offset then
self._offset = offset
end
@@ -355,10 +364,10 @@ end
--- Set new scroll view size in case the node size was changed.
-- @tparam Scroll self @{Scroll}
-- @tparam Scroll self Scroll
-- @tparam vector3 size The new size for view node
-- @treturn druid.scroll Current scroll instance
function Scroll.set_view_size(self, size)
function M:set_view_size(size)
gui.set_size(self.view_node, size)
self.view_size = size
self.view_border = helper.get_border(self.view_node)
@@ -369,8 +378,8 @@ end
--- Refresh scroll view size
-- @tparam Scroll self @{Scroll}
function Scroll.update_view_size(self)
-- @tparam Scroll self Scroll
function M:update_view_size()
self.view_size = helper.get_scaled_size(self.view_node)
self.view_border = helper.get_border(self.view_node)
self:_update_size()
@@ -382,10 +391,10 @@ end
--- Enable or disable scroll inert.
-- If disabled, scroll through points (if exist)
-- If no points, just simple drag without inertion
-- @tparam Scroll self @{Scroll}
-- @tparam Scroll self Scroll
-- @tparam boolean|nil state Inert scroll state
-- @treturn druid.scroll Current scroll instance
function Scroll.set_inert(self, state)
function M:set_inert(state)
self._is_inert = state
return self
@@ -393,19 +402,19 @@ end
--- Return if scroll have inertion.
-- @tparam Scroll self @{Scroll}
-- @tparam Scroll self Scroll
-- @treturn boolean @If scroll have inertion
function Scroll.is_inert(self)
function M:is_inert()
return self._is_inert
end
--- Set extra size for scroll stretching.
-- Set 0 to disable stretching effect
-- @tparam Scroll self @{Scroll}
-- @tparam Scroll self Scroll
-- @tparam number|nil stretch_size Size in pixels of additional scroll area
-- @treturn druid.scroll Current scroll instance
function Scroll.set_extra_stretch_size(self, stretch_size)
function M:set_extra_stretch_size(stretch_size)
self.style.EXTRA_STRETCH_SIZE = stretch_size or 0
self:_update_size()
@@ -414,19 +423,19 @@ end
--- Return vector of scroll size with width and height.
-- @tparam Scroll self @{Scroll}
-- @tparam Scroll self Scroll
-- @treturn vector3 Available scroll size
function Scroll.get_scroll_size(self)
function M:get_scroll_size()
return self.available_size
end
--- Set points of interest.
-- Scroll will always centered on closer points
-- @tparam Scroll self @{Scroll}
-- @tparam Scroll self Scroll
-- @tparam table points Array of vector3 points
-- @treturn druid.scroll Current scroll instance
function Scroll.set_points(self, points)
function M:set_points(points)
self.points = points
table.sort(self.points, function(a, b)
@@ -440,10 +449,10 @@ end
--- Lock or unlock horizontal scroll
-- @tparam Scroll self @{Scroll}
-- @tparam Scroll self Scroll
-- @tparam boolean|nil state True, if horizontal scroll is enabled
-- @treturn druid.scroll Current scroll instance
function Scroll.set_horizontal_scroll(self, state)
function M:set_horizontal_scroll(state)
self._is_horizontal_scroll = state
self.drag.can_x = self.available_size.x > 0 and state
return self
@@ -451,10 +460,10 @@ end
--- Lock or unlock vertical scroll
-- @tparam Scroll self @{Scroll}
-- @tparam Scroll self Scroll
-- @tparam boolean|nil state True, if vertical scroll is enabled
-- @treturn druid.scroll Current scroll instance
function Scroll.set_vertical_scroll(self, state)
function M:set_vertical_scroll(state)
self._is_vertical_scroll = state
self.drag.can_y = self.available_size.y > 0 and state
return self
@@ -463,10 +472,10 @@ end
--- Check node if it visible now on scroll.
-- Extra border is not affected. Return true for elements in extra scroll zone
-- @tparam Scroll self @{Scroll}
-- @tparam Scroll self Scroll
-- @tparam node node The node to check
-- @treturn boolean True if node in visible scroll area
function Scroll.is_node_in_view(self, node)
function M:is_node_in_view(node)
local node_offset_for_view = gui.get_position(node)
local parent = gui.get_parent(node)
local is_parent_of_view = false
@@ -504,10 +513,10 @@ end
--- Bind the grid component (Static or Dynamic) to recalculate
-- scroll size on grid changes
-- @tparam Scroll self @{Scroll}
-- @tparam Scroll self Scroll
-- @tparam StaticGrid grid Druid grid component
-- @treturn druid.scroll Current scroll instance
function Scroll.bind_grid(self, grid)
function M:bind_grid(grid)
if self._grid_on_change then
self._grid_on_change:unsubscribe(self._grid_on_change_callback)
@@ -535,12 +544,12 @@ end
-- restrict events outside stencil node
-- @tparam Drag self
-- @tparam node|string node Gui node
function Scroll.set_click_zone(self, node)
function M:set_click_zone(node)
self.drag:set_click_zone(node)
end
function Scroll._on_scroll_drag(self, dx, dy)
function M:_on_scroll_drag(dx, dy)
local t = self.target_position
local b = self.available_pos
local eb = self.available_pos_extra
@@ -581,7 +590,7 @@ function Scroll._on_scroll_drag(self, dx, dy)
end
function Scroll._check_soft_zone(self)
function M:_check_soft_zone()
local target = self.target_position
local border = self.available_pos
local speed = self.style.BACK_SPEED
@@ -610,7 +619,7 @@ end
-- Cancel animation on other animation or input touch
function Scroll._cancel_animate(self)
function M:_cancel_animate()
self.inertion.x = 0
self.inertion.y = 0
@@ -624,7 +633,7 @@ function Scroll._cancel_animate(self)
end
function Scroll._set_scroll_position(self, position_x, position_y)
function M:_set_scroll_position(position_x, position_y)
local available_extra = self.available_pos_extra
position_x = helper.clamp(position_x, available_extra.x, available_extra.z)
position_y = helper.clamp(position_y, available_extra.w, available_extra.y)
@@ -643,7 +652,7 @@ end
-- if no inert, scroll to next point by scroll direction
-- if inert, find next point by scroll director
-- @local
function Scroll._check_points(self)
function M:_check_points()
if not self.points then
return
end
@@ -699,7 +708,7 @@ function Scroll._check_points(self)
end
function Scroll._check_threshold(self)
function M:_check_threshold()
local is_stopped = false
if self.drag.can_x and math.abs(self.inertion.x) < self.style.INERT_THRESHOLD then
@@ -717,7 +726,7 @@ function Scroll._check_threshold(self)
end
function Scroll._update_free_scroll(self, dt)
function M:_update_free_scroll(dt)
if self.is_animate then
return
end
@@ -742,7 +751,7 @@ function Scroll._update_free_scroll(self, dt)
end
function Scroll._update_hand_scroll(self, dt)
function M:_update_hand_scroll(dt)
if self.is_animate then
self:_cancel_animate()
end
@@ -757,7 +766,7 @@ function Scroll._update_hand_scroll(self, dt)
end
function Scroll._on_touch_start(self)
function M:_on_touch_start()
self.inertion.x = 0
self.inertion.y = 0
self.target_position.x = self.position.x
@@ -765,12 +774,12 @@ function Scroll._on_touch_start(self)
end
function Scroll._on_touch_end(self)
function M:_on_touch_end()
self:_check_threshold()
end
function Scroll._update_size(self)
function M:_update_size()
local content_border = helper.get_border(self.content_node)
local content_size = helper.get_scaled_size(self.content_node)
@@ -808,7 +817,7 @@ function Scroll._update_size(self)
end
function Scroll._process_scroll_wheel(self, action_id, action)
function M:_process_scroll_wheel(action_id, action)
if not self._is_mouse_hover or self.style.WHEEL_SCROLL_SPEED == 0 then
return false
end
@@ -845,9 +854,9 @@ function Scroll._process_scroll_wheel(self, action_id, action)
end
function Scroll._on_mouse_hover(self, state)
function M:_on_mouse_hover(state)
self._is_mouse_hover = state
end
return Scroll
return M

View File

@@ -37,19 +37,19 @@
-- @alias druid.static_grid
--- On item add callback(self, node, index)
-- @tfield DruidEvent on_add_item @{DruidEvent}
-- @tfield DruidEvent on_add_item DruidEvent
--- On item remove callback(self, index)
-- @tfield DruidEvent on_remove_item @{DruidEvent}
-- @tfield DruidEvent on_remove_item DruidEvent
--- On item add, remove or change in_row callback(self, index|nil)
-- @tfield DruidEvent on_change_items @{DruidEvent}
-- @tfield DruidEvent on_change_items DruidEvent
--- On grid clear callback(self)
-- @tfield DruidEvent on_clear @{DruidEvent}
-- @tfield DruidEvent on_clear DruidEvent
--- On update item positions callback(self)
-- @tfield DruidEvent on_update_positions @{DruidEvent}
-- @tfield DruidEvent on_update_positions DruidEvent
--- Parent gui node
-- @tfield node parent
@@ -82,7 +82,23 @@ local Event = require("druid.event")
local helper = require("druid.helper")
local component = require("druid.component")
local StaticGrid = component.create("static_grid")
---@class druid.grid: druid.base_component
---@field on_add_item druid.event
---@field on_remove_item druid.event
---@field on_change_items druid.event
---@field on_clear druid.event
---@field on_update_positions druid.event
---@field parent node
---@field nodes node[]
---@field first_index number
---@field last_index number
---@field anchor vector3
---@field pivot vector3
---@field node_size vector3
---@field border vector4
---@field in_row number
---@field style table
local M = component.create("static_grid")
local function _extend_border(border, pos, size, pivot)
@@ -104,23 +120,23 @@ end
-- @table style
-- @tfield boolean|nil IS_DYNAMIC_NODE_POSES If true, always center grid content as grid pivot sets. Default: false
-- @tfield boolean|nil IS_ALIGN_LAST_ROW If true, always align last row of the grid as grid pivot sets. Default: false
function StaticGrid.on_style_change(self, style)
function M:on_style_change(style)
self.style = {}
self.style.IS_DYNAMIC_NODE_POSES = style.IS_DYNAMIC_NODE_POSES or false
self.style.IS_ALIGN_LAST_ROW = style.IS_ALIGN_LAST_ROW or false
end
--- The @{StaticGrid} constructor
-- @tparam StaticGrid self @{StaticGrid}
--- The StaticGrid constructor
-- @tparam StaticGrid self StaticGrid
-- @tparam string|node parent The GUI Node container, where grid's items will be placed
-- @tparam node element Element prefab. Need to get it size
-- @tparam number|nil in_row How many nodes in row can be placed. By default 1
function StaticGrid.init(self, parent, element, in_row)
function M:init(parent, element, in_row)
self.parent = self:get_node(parent)
self.nodes = {}
self.pivot = helper.get_pivot_offset(gui.get_pivot(self.parent))
self.pivot = helper.get_pivot_offset(self.parent)
self.anchor = vmath.vector3(0.5 + self.pivot.x, 0.5 - self.pivot.y, 0)
self.in_row = in_row or 1
@@ -149,10 +165,10 @@ end
local _temp_pos = vmath.vector3(0)
--- Return pos for grid node index
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @tparam number index The grid element index
-- @treturn vector3 @Node position
function StaticGrid.get_pos(self, index)
function M:get_pos(index)
local row = math.ceil(index / self.in_row) - 1
local col = (index - row * self.in_row) - 1
@@ -167,10 +183,10 @@ end
--- Return index for grid pos
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @tparam vector3 pos The node position in the grid
-- @treturn number The node index
function StaticGrid.get_index(self, pos)
function M:get_index(pos)
-- Offset to left-top corner from node pivot
local node_offset_x = self.node_size.x * (-0.5 + self.node_pivot.x)
local node_offset_y = self.node_size.y * (0.5 - self.node_pivot.y)
@@ -187,10 +203,10 @@ end
--- Return grid index by node
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @tparam node node The gui node in the grid
-- @treturn number The node index
function StaticGrid.get_index_by_node(self, node)
function M:get_index_by_node(node)
for index, grid_node in pairs(self.nodes) do
if node == grid_node then
return index
@@ -201,28 +217,28 @@ function StaticGrid.get_index_by_node(self, node)
end
function StaticGrid.on_layout_change(self)
function M:on_layout_change()
self:_update(true)
end
--- Set grid anchor. Default anchor is equal to anchor of grid parent node
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @tparam vector3 anchor Anchor
function StaticGrid.set_anchor(self, anchor)
function M:set_anchor(anchor)
self.anchor = anchor
self:_update()
end
--- Update grid content
-- @tparam StaticGrid self @{StaticGrid}
function StaticGrid.refresh(self)
-- @tparam StaticGrid self StaticGrid
function M:refresh()
self:_update(true)
end
function StaticGrid.set_pivot(self, pivot)
function M:set_pivot(pivot)
local prev_pivot = helper.get_pivot_offset(gui.get_pivot(self.parent))
self.pivot = helper.get_pivot_offset(pivot)
@@ -254,12 +270,12 @@ end
--- Add new item to the grid
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @tparam node item GUI node
-- @tparam number|nil index The item position. By default add as last item
-- @tparam number|nil shift_policy How shift nodes, if required. Default: const.SHIFT.RIGHT
-- @tparam boolean|nil is_instant If true, update node positions instantly
function StaticGrid.add(self, item, index, shift_policy, is_instant)
function M:add(item, index, shift_policy, is_instant)
index = index or ((self.last_index or 0) + 1)
helper.insert_with_shift(self.nodes, item, index, shift_policy)
@@ -279,10 +295,10 @@ end
--- Set new items to the grid. All previous items will be removed
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @tparam node[] nodes The new grid nodes
-- @tparam[opt=false] boolean is_instant If true, update node positions instantly
function StaticGrid.set_items(self, nodes, is_instant)
function M:set_items(nodes, is_instant)
self.nodes = nodes
for index = 1, #nodes do
local item = nodes[index]
@@ -296,12 +312,12 @@ end
--- Remove the item from the grid. Note that gui node will be not deleted
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @tparam number index The grid node index to remove
-- @tparam number|nil shift_policy How shift nodes, if required. Default: const.SHIFT.RIGHT
-- @tparam boolean|nil is_instant If true, update node positions instantly
-- @treturn node The deleted gui node from grid
function StaticGrid.remove(self, index, shift_policy, is_instant)
function M:remove(index, shift_policy, is_instant)
assert(self.nodes[index], "No grid item at given index " .. index)
local remove_node = self.nodes[index]
@@ -317,9 +333,9 @@ end
--- Return grid content size
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @treturn vector3 The grid content size
function StaticGrid.get_size(self)
function M:get_size()
return vmath.vector3(
self.border.z - self.border.x,
self.border.y - self.border.w,
@@ -327,7 +343,7 @@ function StaticGrid.get_size(self)
end
function StaticGrid.get_size_for(self, count)
function M:get_size_for(count)
if not count or count == 0 then
return vmath.vector3(0)
end
@@ -350,17 +366,17 @@ end
--- Return grid content borders
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @treturn vector3 The grid content borders
function StaticGrid.get_borders(self)
function M:get_borders()
return self.border
end
--- Return array of all node positions
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @treturn vector3[] All grid node positions
function StaticGrid.get_all_pos(self)
function M:get_all_pos()
local result = {}
for i, node in pairs(self.nodes) do
table.insert(result, gui.get_position(node))
@@ -372,10 +388,10 @@ end
--- Change set position function for grid nodes. It will call on
-- update poses on grid elements. Default: gui.set_position
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @tparam function callback Function on node set position
-- @treturn druid.static_grid Current grid instance
function StaticGrid.set_position_function(self, callback)
function M:set_position_function(callback)
self._set_position_function = callback or gui.set_position
return self
@@ -384,9 +400,9 @@ end
--- Clear grid nodes array. GUI nodes will be not deleted!
-- If you want to delete GUI nodes, use static_grid.nodes array before grid:clear
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @treturn druid.static_grid Current grid instance
function StaticGrid.clear(self)
function M:clear()
self.border.x = 0
self.border.y = 0
self.border.w = 0
@@ -403,9 +419,9 @@ end
--- Return StaticGrid offset, where StaticGrid content starts.
-- @tparam StaticGrid self @{StaticGrid} The StaticGrid instance
-- @tparam StaticGrid self StaticGrid The StaticGrid instance
-- @treturn vector3 The StaticGrid offset
function StaticGrid:get_offset()
function M:get_offset()
local borders = self:get_borders()
local size = self:get_size()
@@ -419,10 +435,10 @@ end
--- Set new in_row elements for grid
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @tparam number in_row The new in_row value
-- @treturn druid.static_grid Current grid instance
function StaticGrid.set_in_row(self, in_row)
function M:set_in_row(in_row)
self.in_row = in_row
self._grid_horizonal_offset = self.node_size.x * (self.in_row - 1) * self.anchor.x
self._zero_offset = vmath.vector3(
@@ -438,11 +454,11 @@ end
--- Set new node size for grid
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @tparam[opt] number width The new node width
-- @tparam[opt] number height The new node height
-- @treturn druid.static_grid Current grid instance
function StaticGrid.set_item_size(self, width, height)
function M:set_item_size(width, height)
if width then
self.node_size.x = width
end
@@ -463,20 +479,20 @@ end
--- Sort grid nodes by custom comparator function
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @tparam function comparator The comparator function. (a, b) -> boolean
-- @treturn druid.static_grid Current grid instance
function StaticGrid.sort_nodes(self, comparator)
function M:sort_nodes(comparator)
table.sort(self.nodes, comparator)
self:_update(true)
end
--- Update grid inner state
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @tparam boolean|nil is_instant If true, node position update instantly, otherwise with set_position_function callback
-- @local
function StaticGrid._update(self, is_instant)
function M:_update(is_instant)
self:_update_indexes()
self:_update_borders()
self:_update_pos(is_instant)
@@ -484,9 +500,9 @@ end
--- Update first and last indexes of grid nodes
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @local
function StaticGrid._update_indexes(self)
function M:_update_indexes()
self.first_index = nil
self.last_index = nil
for index in pairs(self.nodes) do
@@ -500,9 +516,9 @@ end
--- Update grid content borders, recalculate min and max values
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @local
function StaticGrid._update_borders(self)
function M:_update_borders()
if not self.first_index then
self.border = vmath.vector4(0)
return
@@ -519,10 +535,10 @@ end
--- Update grid nodes position
-- @tparam StaticGrid self @{StaticGrid}
-- @tparam StaticGrid self StaticGrid
-- @tparam boolean|nil is_instant If true, node position update instantly, otherwise with set_position_function callback
-- @local
function StaticGrid._update_pos(self, is_instant)
function M:_update_pos(is_instant)
local zero_offset = self:_get_zero_offset()
for i, node in pairs(self.nodes) do
@@ -545,7 +561,7 @@ end
-- parent pivot node (0:0) with adjusting of node sizes and anchoring
-- @treturn vector3 The offset vector
-- @local
function StaticGrid:_get_zero_offset()
function M:_get_zero_offset()
if not self.style.IS_DYNAMIC_NODE_POSES then
return const.VECTOR_ZERO
end
@@ -562,7 +578,7 @@ end
--- Return offset x for last row in grid. Used to align this row accorting to grid's anchor
-- @treturn number The offset x value
-- @local
function StaticGrid:_get_zero_offset_x(row_index)
function M:_get_zero_offset_x(row_index)
if not self.style.IS_DYNAMIC_NODE_POSES or not self.style.IS_ALIGN_LAST_ROW then
return self._zero_offset.x
end
@@ -580,4 +596,4 @@ function StaticGrid:_get_zero_offset_x(row_index)
end
return StaticGrid
return M

View File

@@ -36,13 +36,13 @@
-- @alias druid.text
--- On set text callback(self, text)
-- @tfield DruidEvent on_set_text @{DruidEvent}
-- @tfield DruidEvent on_set_text DruidEvent
--- On adjust text size callback(self, new_scale, text_metrics)
-- @tfield DruidEvent on_update_text_scale @{DruidEvent}
-- @tfield DruidEvent on_update_text_scale DruidEvent
--- On change pivot callback(self, pivot)
-- @tfield DruidEvent on_set_pivot @{DruidEvent}
-- @tfield DruidEvent on_set_pivot DruidEvent
--- Text node
-- @tfield node node
@@ -83,7 +83,16 @@ local utf8_lua = require("druid.system.utf8")
local component = require("druid.component")
local utf8 = utf8 or utf8_lua --[[@as utf8]]
local Text = component.create("text")
---@class druid.text: druid.base_component
---@field node node
---@field on_set_text druid.event
---@field on_update_text_scale druid.event
---@field on_set_pivot druid.event
---@field style table
---@field private start_pivot number
---@field private start_scale vector3
---@field private scale vector3
local M = component.create("text")
local function update_text_size(self)
if self.scale.x == 0 or self.scale.y == 0 then
@@ -269,7 +278,7 @@ end
-- @tfield string|nil DEFAULT_ADJUST The default adjust type for any text component. Default: DOWNSCALE
-- @tfield string|nil ADJUST_STEPS Amount of iterations for text adjust by height. Default: 20
-- @tfield string|nil ADJUST_SCALE_DELTA Scale step on each height adjust step. Default: 0.02
function Text.on_style_change(self, style)
function M:on_style_change(style)
self.style = {}
self.style.TRIM_POSTFIX = style.TRIM_POSTFIX or "..."
self.style.DEFAULT_ADJUST = style.DEFAULT_ADJUST or const.TEXT_ADJUST.DOWNSCALE
@@ -278,12 +287,12 @@ function Text.on_style_change(self, style)
end
--- The @{Text} constructor
-- @tparam Text self @{Text}
--- The Text constructor
-- @tparam Text self Text
-- @tparam string|node node Node name or GUI Text Node itself
-- @tparam string|nil value Initial text. Default value is node text from GUI scene. Default: nil
-- @tparam string|nil adjust_type Adjust type for text. By default is DOWNSCALE. Look const.TEXT_ADJUST for reference. Default: DOWNSCALE
function Text.init(self, node, value, adjust_type)
function M:init(node, value, adjust_type)
self.node = self:get_node(node)
self.pos = gui.get_position(self.node)
self.node_id = gui.get_id(self.node)
@@ -309,12 +318,12 @@ function Text.init(self, node, value, adjust_type)
end
function Text.on_layout_change(self)
function M:on_layout_change()
self:set_to(self.last_value)
end
function Text.on_message_input(self, node_id, message)
function M:on_message_input(node_id, message)
if node_id ~= self.node_id then
return false
end
@@ -326,11 +335,11 @@ end
--- Calculate text width with font with respect to trailing space
-- @tparam Text self @{Text}
-- @tparam Text self Text
-- @tparam string text|nil
-- @treturn number Width
-- @treturn number Height
function Text.get_text_size(self, text)
function M:get_text_size(text)
text = text or self.last_value
local font_name = gui.get_font(self.node)
local font = gui.get_font_resource(font_name)
@@ -351,10 +360,10 @@ end
--- Get chars count by width
-- @tparam Text self @{Text}
-- @tparam Text self Text
-- @tparam number width
-- @treturn number Chars count
function Text.get_text_index_by_width(self, width)
function M:get_text_index_by_width(width)
local text = self.last_value
local font_name = gui.get_font(self.node)
local font = gui.get_font_resource(font_name)
@@ -385,10 +394,10 @@ end
--- Set text to text field
-- @tparam Text self @{Text}
-- @tparam Text self Text
-- @tparam string set_to Text for node
-- @treturn Text Current text instance
function Text.set_to(self, set_to)
function M:set_to(set_to)
set_to = set_to or ""
self.last_value = set_to
@@ -403,10 +412,10 @@ end
--- Set text area size
-- @tparam Text self @{Text}
-- @tparam Text self Text
-- @tparam vector3 size The new text area size
-- @treturn Text Current text instance
function Text.set_size(self, size)
function M:set_size(size)
self.start_size = size
self.text_area = vmath.vector3(size)
self.text_area.x = self.text_area.x * self.start_scale.x
@@ -416,10 +425,10 @@ end
--- Set color
-- @tparam Text self @{Text}
-- @tparam Text self Text
-- @tparam vector4 color Color for node
-- @treturn Text Current text instance
function Text.set_color(self, color)
function M:set_color(color)
self.color = color
gui.set_color(self.node, color)
@@ -428,10 +437,10 @@ end
--- Set alpha
-- @tparam Text self @{Text}
-- @tparam Text self Text
-- @tparam number alpha Alpha for node
-- @treturn Text Current text instance
function Text.set_alpha(self, alpha)
function M:set_alpha(alpha)
self.color.w = alpha
gui.set_color(self.node, self.color)
@@ -440,10 +449,10 @@ end
--- Set scale
-- @tparam Text self @{Text}
-- @tparam Text self Text
-- @tparam vector3 scale Scale for node
-- @treturn Text Current text instance
function Text.set_scale(self, scale)
function M:set_scale(scale)
self.last_scale = scale
gui.set_scale(self.node, scale)
@@ -452,10 +461,10 @@ end
--- Set text pivot. Text will re-anchor inside text area
-- @tparam Text self @{Text}
-- @tparam Text self Text
-- @tparam number pivot The gui.PIVOT_* constant
-- @treturn Text Current text instance
function Text.set_pivot(self, pivot)
function M:set_pivot(pivot)
local prev_pivot = gui.get_pivot(self.node)
local prev_offset = const.PIVOTS[prev_pivot]
@@ -478,19 +487,19 @@ end
--- Return true, if text with line break
-- @tparam Text self @{Text}
-- @tparam Text self Text
-- @treturn boolean Is text node with line break
function Text.is_multiline(self)
function M:is_multiline()
return gui.get_line_break(self.node)
end
--- Set text adjust, refresh the current text visuals, if needed
-- @tparam Text self @{Text}
-- @tparam Text self Text
-- @tparam string|nil adjust_type See const.TEXT_ADJUST. If pass nil - use current adjust type
-- @tparam number|nil minimal_scale If pass nil - not use minimal scale
-- @treturn Text Current text instance
function Text.set_text_adjust(self, adjust_type, minimal_scale)
function M:set_text_adjust(adjust_type, minimal_scale)
self.adjust_type = adjust_type
self._minimal_scale = minimal_scale
self:set_to(self.last_value)
@@ -500,10 +509,10 @@ end
--- Set minimal scale for DOWNSCALE_LIMITED or SCALE_THEN_SCROLL adjust types
-- @tparam Text self @{Text}
-- @tparam Text self Text
-- @tparam number minimal_scale If pass nil - not use minimal scale
-- @treturn Text Current text instance
function Text.set_minimal_scale(self, minimal_scale)
function M:set_minimal_scale(minimal_scale)
self._minimal_scale = minimal_scale
return self
@@ -512,9 +521,9 @@ end
--- Return current text adjust type
-- @treturn number The current text adjust type
function Text.get_text_adjust(self, adjust_type)
function M:get_text_adjust(adjust_type)
return self.adjust_type
end
return Text
return M

View File

@@ -1,83 +1,99 @@
-- Copyright (c) 2021 Maksim Tuprikov <insality@gmail.com>. This code is licensed under MIT license
--- Basic class for all Druid components.
-- To create you custom component, use static function `component.create`
-- @usage
-- -- Create your component:
-- local component = require("druid.component")
--
-- local AwesomeComponent = component.create("awesome_component")
--
-- function AwesomeComponent:init(template, nodes)
-- self:set_template(template)
-- self:set_nodes(nodes)
-- self.druid = self:get_druid()
-- end
--
-- return AwesomeComponent
-- @module BaseComponent
-- @alias druid.base_component
---Base component class for all Druid components.
local const = require("druid.const")
local helper = require("druid.helper")
local BaseComponent = {}
---@class druid.base_component.meta
---@field template string
---@field context table
---@field nodes table
---@field style table
---@field druid druid_instance
---@field input_enabled boolean
---@field children table
---@field parent druid.base_component
---@field instance_class table
---@class druid.base_component.component
---@field name string
---@field input_priority number
---@field default_input_priority number
---@field _is_input_priority_changed boolean
---@field _uid number
---@class druid.base_component
---@field druid druid_instance Druid instance to create inner components
---@field protected init fun(self:druid.base_component, ...)|nil
---@field protected update fun(self:druid.base_component, dt:number)|nil
---@field protected on_remove fun(self:druid.base_component)|nil
---@field protected on_input fun(self:druid.base_component, action_id:number, action:table)|nil
---@field protected on_message fun(self:druid.base_component, message_id:hash, message:table, sender:userdata)|nil
---@field protected on_late_init fun(self:druid.base_component)|nil
---@field protected on_focus_lost fun(self:druid.base_component)|nil
---@field protected on_focus_gained fun(self:druid.base_component)|nil
---@field protected on_style_change fun(self:druid.base_component, style: table)|nil
---@field protected on_layout_change fun(self:druid.base_component)|nil
---@field protected on_window_resized fun(self:druid.base_component, width:number, height:number)|nil
---@field protected on_language_change fun(self:druid.base_component, language:string)|nil
---@field private _component druid.base_component.component
---@field private _meta druid.base_component.meta
local M = {}
local INTERESTS = {} -- Cache interests per component class in runtime
local IS_AUTO_TEMPLATE = not (sys.get_config_int("druid.no_auto_template", 0) == 1)
-- Component Interests
BaseComponent.ON_INPUT = const.ON_INPUT
BaseComponent.ON_UPDATE = const.ON_UPDATE
BaseComponent.ON_MESSAGE = const.ON_MESSAGE
BaseComponent.ON_LATE_INIT = const.ON_LATE_INIT
BaseComponent.ON_FOCUS_LOST = const.ON_FOCUS_LOST
BaseComponent.ON_FOCUS_GAINED = const.ON_FOCUS_GAINED
BaseComponent.ON_LAYOUT_CHANGE = const.ON_LAYOUT_CHANGE
BaseComponent.ON_MESSAGE_INPUT = const.ON_MESSAGE_INPUT
BaseComponent.ON_WINDOW_RESIZED = const.ON_WINDOW_RESIZED
BaseComponent.ON_LANGUAGE_CHANGE = const.ON_LANGUAGE_CHANGE
M.ON_INPUT = const.ON_INPUT
M.ON_UPDATE = const.ON_UPDATE
M.ON_MESSAGE = const.ON_MESSAGE
M.ON_LATE_INIT = const.ON_LATE_INIT
M.ON_FOCUS_LOST = const.ON_FOCUS_LOST
M.ON_FOCUS_GAINED = const.ON_FOCUS_GAINED
M.ON_LAYOUT_CHANGE = const.ON_LAYOUT_CHANGE
M.ON_MESSAGE_INPUT = const.ON_MESSAGE_INPUT
M.ON_WINDOW_RESIZED = const.ON_WINDOW_RESIZED
M.ON_LANGUAGE_CHANGE = const.ON_LANGUAGE_CHANGE
BaseComponent.ALL_INTERESTS = {
BaseComponent.ON_INPUT,
BaseComponent.ON_UPDATE,
BaseComponent.ON_MESSAGE,
BaseComponent.ON_LATE_INIT,
BaseComponent.ON_FOCUS_LOST,
BaseComponent.ON_FOCUS_GAINED,
BaseComponent.ON_LAYOUT_CHANGE,
BaseComponent.ON_MESSAGE_INPUT,
BaseComponent.ON_WINDOW_RESIZED,
BaseComponent.ON_LANGUAGE_CHANGE,
M.ALL_INTERESTS = {
M.ON_INPUT,
M.ON_UPDATE,
M.ON_MESSAGE,
M.ON_LATE_INIT,
M.ON_FOCUS_LOST,
M.ON_FOCUS_GAINED,
M.ON_LAYOUT_CHANGE,
M.ON_MESSAGE_INPUT,
M.ON_WINDOW_RESIZED,
M.ON_LANGUAGE_CHANGE,
}
-- Mapping from on_message method to specific method name
BaseComponent.SPECIFIC_UI_MESSAGES = {
[hash("layout_changed")] = BaseComponent.ON_LAYOUT_CHANGE, -- The message_id from Defold
[hash(BaseComponent.ON_FOCUS_LOST)] = BaseComponent.ON_FOCUS_LOST,
[hash(BaseComponent.ON_FOCUS_GAINED)] = BaseComponent.ON_FOCUS_GAINED,
[hash(BaseComponent.ON_WINDOW_RESIZED)] = BaseComponent.ON_WINDOW_RESIZED,
[hash(BaseComponent.ON_MESSAGE_INPUT)] = BaseComponent.ON_MESSAGE_INPUT,
[hash(BaseComponent.ON_LANGUAGE_CHANGE)] = BaseComponent.ON_LANGUAGE_CHANGE,
M.SPECIFIC_UI_MESSAGES = {
[hash("layout_changed")] = M.ON_LAYOUT_CHANGE, -- The message_id from Defold
[hash(M.ON_FOCUS_LOST)] = M.ON_FOCUS_LOST,
[hash(M.ON_FOCUS_GAINED)] = M.ON_FOCUS_GAINED,
[hash(M.ON_WINDOW_RESIZED)] = M.ON_WINDOW_RESIZED,
[hash(M.ON_MESSAGE_INPUT)] = M.ON_MESSAGE_INPUT,
[hash(M.ON_LANGUAGE_CHANGE)] = M.ON_LANGUAGE_CHANGE,
}
local uid = 0
function BaseComponent.create_uid()
---@private
function M.create_uid()
uid = uid + 1
return uid
end
--- Set current component style table.
--
-- Invoke `on_style_change` on component, if exist. Component should handle
-- their style changing and store all style params
-- @tparam BaseComponent self @{BaseComponent}
-- @tparam table|nil druid_style Druid style module
-- @treturn BaseComponent @{BaseComponent}
-- @local
function BaseComponent.set_style(self, druid_style)
---Set component style. Pass nil to clear style
---@generic T
---@param self T
---@param druid_style table|nil
---@return T self The component itself for chaining
function M.set_style(self, druid_style)
---@cast self druid.base_component
self._meta.style = druid_style or {}
local component_style = self._meta.style[self._component.name] or {}
@@ -89,15 +105,16 @@ function BaseComponent.set_style(self, druid_style)
end
--- Set component template name.
--
-- Use on all your custom components with GUI layouts used as templates.
-- It will check parent template name to build full template name in self:get_node()
-- @tparam BaseComponent self @{BaseComponent}
-- @tparam string template BaseComponent template name
-- @treturn BaseComponent @{BaseComponent}
-- @local
function BaseComponent.set_template(self, template)
---Set component template name. Pass nil to clear template.
---This template id used to access nodes inside the template on GUI scene.
---Parent template will be added automatically if exist.
---@generic T
---@param self T
---@param template string|nil
---@return T self The component itself for chaining
function M.set_template(self, template)
---@cast self druid.base_component
template = template or ""
local parent = self:get_parent_component()
@@ -116,101 +133,49 @@ function BaseComponent.set_template(self, template)
end
--- Get current component template name.
-- @tparam BaseComponent self @{BaseComponent}
-- @treturn string Component full template name
function BaseComponent.get_template(self)
---Get full template name.
---@return string
function M:get_template()
return self._meta.template
end
--- Set current component nodes.
-- Use if your component nodes was cloned with `gui.clone_tree` and you got the node tree.
-- @tparam BaseComponent self @{BaseComponent}
-- @tparam table nodes BaseComponent nodes table
-- @treturn BaseComponent @{BaseComponent}
-- @usage
-- local nodes = gui.clone_tree(self.prefab)
-- ... In your component:
-- self:set_nodes(nodes)
-- @local
function BaseComponent.set_nodes(self, nodes)
---Set current component nodes, returned from `gui.clone_tree` function.
---@param nodes table<hash, node>
---@return druid.base_component
function M.set_nodes(self, nodes)
self._meta.nodes = nodes
-- When we use gui.clone_tree in inner template (template inside other template)
-- this nodes have no id. We have table: hash(correct_id) : hash("")
-- It's wrong and we use this hack to fix this
if nodes then
for id, node in pairs(nodes) do
gui.set_id(node, id)
end
end
return self
end
--- Context used as first arg in all Druid events
--
-- Context is usually self of gui_script.
-- @tparam BaseComponent self @{BaseComponent}
-- @treturn table BaseComponent context
function BaseComponent.get_context(self)
---Return current component context
---@return any context Usually it's self of script but can be any other Druid component
function M.get_context(self)
return self._meta.context
end
--- Increase input priority in input stack
-- @tparam BaseComponent self @{BaseComponent}
-- @local
function BaseComponent.increase_input_priority(self)
helper.deprecated("The component:increase_input_priority is deprecated. Please use component:set_input_priority(druid_const.PRIORITY_INPUT_MAX) instead")
end
--- Get component node by name.
--
-- If component has nodes, node_or_name should be string
-- It autopick node by template name or from nodes by gui.clone_tree
-- if they was setup via component:set_nodes, component:set_template.
-- If node is not found, the exception will fired
-- @tparam BaseComponent self @{BaseComponent}
-- @tparam string|node node_or_name Node name or node itself
-- @treturn node Gui node
function BaseComponent.get_node(self, node_or_name)
if type(node_or_name) ~= "string" then
---Get component node by node_id. Respect to current template and nodes.
---@param node_id string|node
---@return node
function M.get_node(self, node_id)
if type(node_id) ~= "string" then
-- Assume it's already node from gui.get_node
return node_or_name
return node_id
end
local template_name = self:get_template()
local nodes = self:__get_nodes()
if #template_name > 0 then
template_name = template_name .. "/"
end
local node
if nodes then
node = nodes[template_name .. node_or_name]
else
node = gui.get_node(template_name .. node_or_name)
end
if not node then
assert(node, "No component with name: " .. (template_name or "") .. (node_or_name or ""))
end
return node
local nodes = self:get_nodes()
return helper.get_node(node_id, template_name, nodes)
end
--- Get Druid instance for inner component creation.
-- @tparam BaseComponent self @{BaseComponent}
-- @tparam string|nil template The template name
-- @tparam table|nil nodes The nodes table
-- @treturn DruidInstance Druid instance with component context
function BaseComponent.get_druid(self, template, nodes)
---Get Druid instance for inner component creation.
---@param template string|nil
---@param nodes table<hash, node>|nil
---@return druid_instance
function M:get_druid(template, nodes)
local context = { _context = self }
local druid_instance = setmetatable(context, { __index = self._meta.druid })
@@ -226,39 +191,33 @@ function BaseComponent.get_druid(self, template, nodes)
end
--- Return component name
-- @tparam BaseComponent self @{BaseComponent}
-- @treturn string The component name
function BaseComponent.get_name(self)
return self._component.name .. BaseComponent.create_uid()
---Get component name
---@return string name The component name + uid
function M.get_name(self)
return self._component.name .. M.create_uid()
end
--- Return parent component name
-- @tparam BaseComponent self @{BaseComponent}
-- @treturn string|nil The parent component name if exist or bil
function BaseComponent.get_parent_name(self)
---Get parent component name
---@return string|nil parent_name The parent component name if exist or nil
function M.get_parent_name(self)
local parent = self:get_parent_component()
return parent and parent:get_name()
end
--- Return component input priority
-- @tparam BaseComponent self @{BaseComponent}
-- @treturn number The component input priority
function BaseComponent.get_input_priority(self)
---Get component input priority, the bigger number processed first. Default value: 10
---@return number
function M.get_input_priority(self)
return self._component.input_priority
end
--- Set component input priority
--
-- Default value: 10
-- @tparam BaseComponent self @{BaseComponent}
-- @tparam number value The new input priority value
-- @tparam boolean|nil is_temporary If true, the reset input priority will return to previous value
-- @treturn number The component input priority
function BaseComponent.set_input_priority(self, value, is_temporary)
---Set component input priority, the bigger number processed first. Default value: 10
---@param value number
---@param is_temporary boolean|nil If true, the reset input priority will return to previous value
---@return druid.base_component self The component itself for chaining
function M.set_input_priority(self, value, is_temporary)
assert(value)
if self._component.input_priority == value then
@@ -281,32 +240,27 @@ function BaseComponent.set_input_priority(self, value, is_temporary)
end
--- Reset component input priority to default value
-- @tparam BaseComponent self @{BaseComponent}
-- @treturn number The component input priority
function BaseComponent.reset_input_priority(self)
---Reset component input priority to it's default value, that was set in `create` function or `set_input_priority`
---@return druid.base_component self The component itself for chaining
function M.reset_input_priority(self)
self:set_input_priority(self._component.default_input_priority)
return self
end
--- Return component UID.
--
-- UID generated in component creation order.
-- @tparam BaseComponent self @{BaseComponent}
-- @treturn number The component uid
function BaseComponent.get_uid(self)
---Get component UID, unique identifier created in component creation order.
---@return number uid The component uid
function M.get_uid(self)
return self._component._uid
end
--- Set component input state. By default it enabled
--
-- If input is disabled, the component will not receive input events
-- @tparam BaseComponent self @{BaseComponent}
-- @tparam boolean|nil state The component input state
-- @treturn BaseComponent BaseComponent itself
function BaseComponent.set_input_enabled(self, state)
---Set component input state. By default it's enabled.
---If input is disabled, the component will not receive input events.
---Recursive for all children components.
---@param state boolean
---@return druid.base_component self The component itself for chaining
function M.set_input_enabled(self, state)
self._meta.input_enabled = state
for index = 1, #self._meta.children do
@@ -317,23 +271,22 @@ function BaseComponent.set_input_enabled(self, state)
end
--- Return the parent component if exist
-- @tparam BaseComponent self @{BaseComponent}
-- @treturn BaseComponent|nil The druid component instance or nil
function BaseComponent.get_parent_component(self)
---Get parent component
---@return druid.base_component|nil parent The parent component if exist or nil
function M.get_parent_component(self)
return self._meta.parent
end
--- Setup component context and his style table
-- @tparam BaseComponent self @{BaseComponent}
-- @tparam BaseComponent self BaseComponent
-- @tparam table druid_instance The parent druid instance
-- @tparam table context Druid context. Usually it is self of script
-- @tparam table style Druid style module
-- @tparam table instance_class The component instance class
-- @treturn component BaseComponent itself
-- @local
function BaseComponent.setup_component(self, druid_instance, context, style, instance_class)
---@private
function M:setup_component(druid_instance, context, style, instance_class)
self._meta = {
template = "",
context = context,
@@ -357,62 +310,35 @@ function BaseComponent.setup_component(self, druid_instance, context, style, ins
end
--- Print log information if debug mode is enabled
-- @tparam BaseComponent self @{BaseComponent}
-- @tparam string message
-- @tparam table context
-- @local
function BaseComponent.log_message(self, message, context)
if not self._component.is_debug then
return
end
print("[" .. self:get_name() .. "]:", message, helper.table_to_string(context))
end
--- Set debug logs for component enabled or disabled
-- @tparam BaseComponent self @{BaseComponent}
-- @tparam boolean|nil is_debug
-- @local
function BaseComponent.set_debug(self, is_debug)
self._component.is_debug = is_debug
end
--- Return true, if input priority was changed
-- @tparam BaseComponent self @{BaseComponent}
-- @local
function BaseComponent._is_input_priority_changed(self)
-- @tparam BaseComponent self BaseComponent
---@private
function M._is_input_priority_changed(self)
return self._component._is_input_priority_changed
end
--- Reset is_input_priority_changed field
-- @tparam BaseComponent self @{BaseComponent}
-- @local
function BaseComponent._reset_input_priority_changed(self)
-- @tparam BaseComponent self BaseComponent
---@private
function M._reset_input_priority_changed(self)
self._component._is_input_priority_changed = false
end
function BaseComponent.__tostring(self)
return self._component.name
end
--- Get current component interests
-- @tparam BaseComponent self @{BaseComponent}
-- @tparam BaseComponent self BaseComponent
-- @treturn table List of component interests
-- @local
function BaseComponent.__get_interests(self)
---@private
function M.__get_interests(self)
local instance_class = self._meta.instance_class
if INTERESTS[instance_class] then
return INTERESTS[instance_class]
end
local interests = {}
for index = 1, #BaseComponent.ALL_INTERESTS do
local interest = BaseComponent.ALL_INTERESTS[index]
for index = 1, #M.ALL_INTERESTS do
local interest = M.ALL_INTERESTS[index]
if self[interest] and type(self[interest]) == "function" then
table.insert(interests, interest)
end
@@ -423,34 +349,31 @@ function BaseComponent.__get_interests(self)
end
--- Get current component nodes
-- @tparam BaseComponent self @{BaseComponent}
-- @treturn table BaseComponent nodes table
-- @local
function BaseComponent.__get_nodes(self)
---Get current component nodes
---@return table<hash, node>
function M.get_nodes(self)
local nodes = self._meta.nodes
local parent = self:get_parent_component()
if parent then
nodes = nodes or parent:__get_nodes()
nodes = nodes or parent:get_nodes()
end
return nodes
end
--- Add child to component children list
-- @tparam BaseComponent self @{BaseComponent}
-- @tparam component child The druid component instance
-- @local
function BaseComponent.__add_child(self, child)
---Add child to component children list
---@param child druid.base_component The druid component instance
---@private
function M:__add_child(child)
table.insert(self._meta.children, child)
end
--- Remove child from component children list
-- @tparam BaseComponent self @{BaseComponent}
-- @tparam BaseComponent self BaseComponent
-- @tparam component child The druid component instance
-- @local
function BaseComponent.__remove_child(self, child)
---@private
function M.__remove_child(self, child)
for i = #self._meta.children, 1, -1 do
if self._meta.children[i] == child then
table.remove(self._meta.children, i)
@@ -461,9 +384,9 @@ end
--- Return all children components, recursive
-- @tparam BaseComponent self @{BaseComponent}
-- @tparam BaseComponent self BaseComponent
-- @treturn table Array of childrens if the Druid component instance
function BaseComponent.get_childrens(self)
function M.get_childrens(self)
local childrens = {}
for i = 1, #self._meta.children do
@@ -477,23 +400,21 @@ function BaseComponent.get_childrens(self)
end
--- Create new component. It will inheritance from basic Druid component.
-- @function BaseComponent.create
-- @tparam string name BaseComponent name
-- @tparam number|nil input_priority The input priority. The bigger number processed first
-- @local
function BaseComponent.create(name, input_priority)
---Сreate a new component class, which will inherit from the base Druid component.
---@param name string|nil The name of the component
---@param input_priority number|nil The input priority. The bigger number processed first. Default value: 10
---@return druid.base_component
function M.create(name, input_priority)
local new_class = setmetatable({}, {
__index = BaseComponent,
__index = M,
__call = function(cls, ...)
local self = setmetatable({
_component = {
name = name,
name = name or "Druid Component",
input_priority = input_priority or const.PRIORITY_INPUT,
default_input_priority = input_priority or const.PRIORITY_INPUT,
is_debug = false,
_is_input_priority_changed = true, -- Default true for sort once time after GUI init
_uid = BaseComponent.create_uid()
_uid = M.create_uid()
}
}, {
__index = cls
@@ -506,4 +427,4 @@ function BaseComponent.create(name, input_priority)
end
return BaseComponent
return M

View File

@@ -1,10 +1,4 @@
-- Copyright (c) 2021 Maksim Tuprikov <insality@gmail.com>. This code is licensed under MIT license
--- Druid constants
-- @local
-- @module DruidConst
-- @alias druid_const
---@class druid.system.const
local M = {}
M.ACTION_TEXT = hash(sys.get_config_string("druid.input_text", "text"))
@@ -25,7 +19,6 @@ M.ACTION_LCMD = hash(sys.get_config_string("druid.input_key_lsuper", "key_lsuper
M.IS_STENCIL_CHECK = not (sys.get_config_int("druid.no_stencil_check", 0) == 1)
M.ON_INPUT = "on_input"
M.ON_UPDATE = "update"
M.ON_MESSAGE = "on_message"
@@ -123,10 +116,6 @@ M.SWIPE = {
RIGHT = "right",
}
M.ERRORS = {
GRID_DYNAMIC_ANCHOR = "The pivot of dynamic grid node should be West, East, South or North"
}
M.EMPTY_FUNCTION = function() end
return M

View File

@@ -1,11 +1,11 @@
script: ""
fonts {
name: "game"
font: "/example/assets/fonts/game.font"
name: "text_bold"
font: "/example/assets/fonts/text_bold.font"
}
textures {
name: "kenney"
texture: "/example/assets/images/kenney.atlas"
name: "druid"
texture: "/example/assets/druid.atlas"
}
background_color {
x: 0.0
@@ -33,8 +33,8 @@ nodes {
w: 1.0
}
size {
x: 1.0
y: 1.0
x: 500.0
y: 80.0
z: 0.0
w: 1.0
}
@@ -46,7 +46,7 @@ nodes {
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: "kenney/empty"
texture: ""
id: "root"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
@@ -65,7 +65,11 @@ nodes {
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_AUTO
size_mode: SIZE_MODE_MANUAL
custom_type: 0
enabled: true
visible: false
material: ""
}
nodes {
position {
@@ -87,8 +91,8 @@ nodes {
w: 1.0
}
size {
x: 190.0
y: 45.0
x: 500.0
y: 80.0
z: 0.0
w: 1.0
}
@@ -100,7 +104,7 @@ nodes {
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: "kenney/progress_back"
texture: "druid/rect_round2_width1"
id: "button"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
@@ -110,17 +114,21 @@ nodes {
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
x: 4.0
y: 4.0
z: 4.0
w: 4.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_AUTO
size_mode: SIZE_MODE_MANUAL
custom_type: 0
enabled: true
visible: true
material: ""
}
nodes {
position {
@@ -136,27 +144,27 @@ nodes {
w: 1.0
}
scale {
x: 0.5
y: 0.5
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 300.0
x: 480.0
y: 60.0
z: 0.0
w: 1.0
}
color {
x: 0.9490196
y: 0.9490196
z: 0.9490196
x: 0.31
y: 0.318
z: 0.322
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "Placeholder"
font: "game"
font: "text_bold"
id: "placeholder_text"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
@@ -179,11 +187,15 @@ nodes {
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
outline_alpha: 0.0
shadow_alpha: 0.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
material: ""
}
nodes {
position {
@@ -199,27 +211,27 @@ nodes {
w: 1.0
}
scale {
x: 0.6
y: 0.6
z: 1.0
w: 1.0
}
size {
x: 300.0
y: 60.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 480.0
y: 60.0
z: 0.0
w: 1.0
}
color {
x: 0.722
y: 0.741
z: 0.761
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "User input"
font: "game"
font: "text_bold"
id: "input_text"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
@@ -242,15 +254,19 @@ nodes {
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
outline_alpha: 0.0
shadow_alpha: 0.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
material: ""
}
nodes {
position {
x: 67.0
x: 118.0
y: 0.0
z: 0.0
w: 1.0
@@ -262,26 +278,26 @@ nodes {
w: 1.0
}
scale {
x: 0.6
y: 0.6
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 1.0
y: 1.0
x: 16.0
y: 50.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
x: 0.631
y: 0.843
z: 0.961
w: 1.0
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: "kenney/empty"
texture: "druid/ui_circle_16"
id: "cursor_node"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
@@ -291,22 +307,26 @@ nodes {
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
x: 8.0
y: 8.0
z: 8.0
w: 8.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
alpha: 0.5
template_node_child: false
size_mode: SIZE_MODE_AUTO
size_mode: SIZE_MODE_MANUAL
custom_type: 0
enabled: true
visible: true
material: ""
}
nodes {
position {
x: 0.0
y: 2.0
y: 4.0
z: 0.0
w: 1.0
}
@@ -317,8 +337,8 @@ nodes {
w: 1.0
}
scale {
x: 1.0
y: 1.0
x: 1.2
y: 1.2
z: 1.0
w: 1.0
}
@@ -329,15 +349,15 @@ nodes {
w: 1.0
}
color {
x: 0.2
y: 0.2
z: 0.2
x: 0.722
y: 0.741
z: 0.761
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "|"
font: "game"
font: "text_bold"
id: "cursor_text"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
@@ -358,13 +378,17 @@ nodes {
line_break: false
parent: "cursor_node"
layer: ""
inherit_alpha: true
inherit_alpha: false
alpha: 1.0
outline_alpha: 0.0
shadow_alpha: 0.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
material: ""
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT

View File

@@ -6,13 +6,13 @@
-- @alias druid.rich_input
--- The component druid instance
-- @tfield DruidInstance druid @{DruidInstance}
-- @tfield DruidInstance druid DruidInstance
--- Root node
-- @tfield node root
--- On input field text change callback(self, input_text)
-- @tfield Input input @{Input}
-- @tfield Input input Input
--- On input field text change to empty string callback(self, input_text)
-- @tfield node cursor
@@ -44,16 +44,23 @@ local utf8_lua = require("druid.system.utf8")
local utf8 = utf8 or utf8_lua
local input = require("druid.extended.input")
local RichInput = component.create("druid.rich_input")
local SCHEME = {
ROOT = "root",
BUTTON = "button",
PLACEHOLDER = "placeholder_text",
INPUT = "input_text",
CURSOR = "cursor_node",
CURSOR_TEXT = "cursor_text",
}
---@class druid.rich_input: druid.base_component
---@field root node
---@field input druid.input
---@field cursor node
---@field cursor_text node
---@field cursor_position vector3
local M = component.create("druid.rich_input")
--local SCHEME = {
-- ROOT = "root",
-- BUTTON = "button",
-- PLACEHOLDER = "placeholder_text",
-- INPUT = "input_text",
-- CURSOR = "cursor_node",
-- CURSOR_TEXT = "cursor_text",
--}
local DOUBLE_CLICK_TIME = 0.35
@@ -189,13 +196,11 @@ local function on_drag_callback(self, dx, dy, x, y, touch)
end
--- The @{RichInput} constructor
-- @tparam RichInput self @{RichInput}
-- @tparam string template The template string name
-- @tparam table nodes Nodes table from gui.clone_tree
function RichInput.init(self, template, nodes)
---@param template string The template string name
---@param nodes table Nodes table from gui.clone_tree
function M:init(template, nodes)
self.druid = self:get_druid(template, nodes)
self.root = self:get_node(SCHEME.ROOT)
self.root = self:get_node("root")
self._last_touch_info = {
cursor_index = nil,
@@ -204,20 +209,20 @@ function RichInput.init(self, template, nodes)
self.is_lshift = false
self.is_lctrl = false
self.input = self.druid:new(input, self:get_node(SCHEME.BUTTON), self:get_node(SCHEME.INPUT))
self.input = self.druid:new(input, "button", "input_text")
self.is_button_input_enabled = gui.is_enabled(self.input.button.node)
self.cursor = self:get_node(SCHEME.CURSOR)
self.cursor = self:get_node("cursor_node")
self.cursor_position = gui.get_position(self.cursor)
self.cursor_text = self:get_node(SCHEME.CURSOR_TEXT)
self.cursor_text = self:get_node("cursor_text")
self.drag = self.druid:new_drag(self:get_node(SCHEME.BUTTON), on_drag_callback)
self.drag = self.druid:new_drag("button", on_drag_callback)
self.drag.on_touch_start:subscribe(on_touch_start_callback)
self.drag:set_input_priority(const.PRIORITY_INPUT_MAX + 1)
self.drag:set_enabled(false)
self.input:set_text("")
self.placeholder = self.druid:new_text(self:get_node(SCHEME.PLACEHOLDER))
self.placeholder = self.druid:new_text("placeholder_text")
self.text_position = gui.get_position(self.input.text.node)
self.input.on_input_text:subscribe(update_text)
@@ -230,7 +235,7 @@ function RichInput.init(self, template, nodes)
end
function RichInput.on_input(self, action_id, action)
function M:on_input(action_id, action)
if action_id == const.ACTION_LSHIFT then
if action.pressed then
self.is_lshift = true
@@ -258,26 +263,26 @@ end
--- Set placeholder text
-- @tparam RichInput self @{RichInput}
-- @tparam RichInput self RichInput
-- @tparam string placeholder_text The placeholder text
function RichInput.set_placeholder(self, placeholder_text)
function M:set_placeholder(placeholder_text)
self.placeholder:set_to(placeholder_text)
return self
end
--- Select input field
-- @tparam RichInput self @{RichInput}
function RichInput.select(self)
-- @tparam RichInput self RichInput
function M:select()
self.input:select()
end
--- Set input field text
-- @tparam RichInput self @{RichInput}
-- @tparam RichInput self RichInput
-- @treturn druid.input Current input instance
-- @tparam string text The input text
function RichInput.set_text(self, text)
function M:set_text(text)
self.input:set_text(text)
gui.set_enabled(self.placeholder.node, true and #self.input:get_text() == 0)
@@ -286,10 +291,10 @@ end
--- Set input field font
-- @tparam RichInput self @{RichInput}
-- @tparam RichInput self RichInput
-- @tparam hash font The font hash
-- @treturn druid.input Current input instance
function RichInput.set_font(self, font)
function M:set_font(font)
gui.set_font(self.input.text.node, font)
gui.set_font(self.placeholder.node, font)
@@ -298,8 +303,8 @@ end
--- Set input field text
-- @tparam RichInput self @{RichInput}
function RichInput.get_text(self)
-- @tparam RichInput self RichInput
function M:get_text()
return self.input:get_text()
end
@@ -307,14 +312,14 @@ end
--- Set allowed charaters for input field.
-- See: https://defold.com/ref/stable/string/
-- ex: [%a%d] for alpha and numeric
-- @tparam RichInput self @{RichInput}
-- @tparam RichInput self RichInput
-- @tparam string characters Regulax exp. for validate user input
-- @treturn RichInput Current instance
function RichInput.set_allowed_characters(self, characters)
function M:set_allowed_characters(characters)
self.input:set_allowed_characters(characters)
return self
end
return RichInput
return M

View File

@@ -63,7 +63,7 @@
-- @alias druid.rich_text
--- The component druid instance
-- @tfield DruidInstance druid @{DruidInstance}
-- @tfield DruidInstance druid DruidInstance
--- The root node of the Rich Text
-- @tfield node root
@@ -76,14 +76,19 @@
local component = require("druid.component")
local rich_text = require("druid.custom.rich_text.module.rt")
local RichText = component.create("rich_text")
---@class druid.rich_text: druid.base_component
---@field root node
---@field text_prefab node
---@field private _last_value string
---@field private _settings table
local M = component.create("rich_text")
--- The @{RichText} constructor
-- @tparam RichText self @{RichText}
--- The RichText constructor
-- @tparam RichText self RichText
-- @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)
function M:init(text_node, value)
self.root = self:get_node(text_node)
self.text_prefab = self.root
@@ -98,7 +103,7 @@ function RichText.init(self, text_node, value)
end
function RichText.on_layout_change(self)
function M:on_layout_change()
if self._last_value then
self:set_text(self._last_value)
end
@@ -112,7 +117,7 @@ end
-- @tfield table|nil COLORS Rich Text color aliases. Default: {}
-- @tfield number|nil ADJUST_STEPS Amount steps of attemps text adjust by height. Default: 20
-- @tfield number|nil ADJUST_SCALE_DELTA Scale step on each height adjust step. Default: 0.02
function RichText.on_style_change(self, style)
function M:on_style_change(style)
self.style = {}
self.style.COLORS = style.COLORS or {}
self.style.ADJUST_STEPS = style.ADJUST_STEPS or 20
@@ -121,7 +126,7 @@ end
--- Set text for Rich Text
-- @tparam RichText self @{RichText}
-- @tparam RichText self RichText
-- @tparam string|nil text The text to set
-- @treturn druid.rich_text.word[] words
-- @treturn druid.rich_text.lines_metrics line_metrics
@@ -168,7 +173,7 @@ end
-- <img=texture:image/>
-- <img=texture:image,size/>
-- <img=texture:image,width,height/>
function RichText.set_text(self, text)
function M:set_text(text)
text = text or ""
self:clear()
self._last_value = text
@@ -184,14 +189,14 @@ end
--- Get current text
-- @tparam RichText self @{RichText}
-- @tparam RichText self RichText
-- @treturn string text
function RichText.get_text(self)
function M:get_text()
return self._last_value
end
function RichText:on_remove()
function M:on_remove()
gui.set_scale(self.root, self._default_scale)
gui.set_size(self.root, self._default_size)
self:clear()
@@ -199,7 +204,7 @@ end
--- Clear all created words.
function RichText:clear()
function M:clear()
if self._words then
rich_text.remove(self._words)
self._words = nil
@@ -209,10 +214,10 @@ end
--- Get all words, which has a passed tag.
-- @tparam RichText self @{RichText}
-- @tparam RichText self RichText
-- @tparam string tag
-- @treturn druid.rich_text.word[] words
function RichText.tagged(self, tag)
function M:tagged(tag)
if not self._words then
return
end
@@ -222,29 +227,29 @@ end
---Split a word into it's characters
-- @tparam RichText self @{RichText}
-- @tparam RichText self RichText
-- @tparam druid.rich_text.word word
-- @treturn druid.rich_text.word[] characters
function RichText.characters(self, word)
function M:characters(word)
return rich_text.characters(word)
end
--- Get all current words.
-- @treturn table druid.rich_text.word[]
function RichText:get_words()
function M:get_words()
return self._words
end
--- Get current line metrics
--- @treturn druid.rich_text.lines_metrics
function RichText:get_line_metric()
function M:get_line_metric()
return self._line_metrics
end
function RichText:_create_settings()
function M:_create_settings()
local root_size = gui.get_size(self.root)
local scale = gui.get_scale(self.root)
@@ -280,4 +285,4 @@ function RichText:_create_settings()
end
return RichText
return M

View File

@@ -13,7 +13,7 @@
--
-- • Each Druid instance maintains the self context from the constructor and passes it to each Druid callback.
--
-- See next: @{DruidInstance}
-- See next: DruidInstance
--
-- @usage
-- local druid = require("druid.druid")
@@ -52,15 +52,15 @@ local druid_instance = require("druid.system.druid_instance")
local default_style = require("druid.styles.default.style")
---@class druid
local M = {}
local _instances = {}
local druid_instances = {}
local function clean_deleted_druid_instances()
for i = #_instances, 1, -1 do
if _instances[i]._deleted then
table.remove(_instances, i)
for i = #druid_instances, 1, -1 do
if druid_instances[i]._deleted then
table.remove(druid_instances, i)
end
end
end
@@ -68,45 +68,27 @@ end
local function get_druid_instances()
clean_deleted_druid_instances()
return _instances
return druid_instances
end
--- Register a new external Druid component.
--
-- You can register your own components to make new alias: the druid:new_{name} function.
-- For example, if you want to register a component called "my_component", you can create it using druid:new_my_component(...).
-- This can be useful if you have your own "basic" components that you don't want to re-create each time.
-- @function druid.register
-- @tparam string name module name
-- @tparam table module lua table with component
-- @usage
-- local my_component = require("path.to.my.component")
-- druid.register("my_component", my_component)
-- ...
-- local druid = druid.new(self)
-- local component_instance = self.druid:new_my_component(...)
---Register a new external Druid component.
---You can register your own components to make new alias: the druid:new_{name} function.
---For example, if you want to register a component called "my_component", you can create it using druid:new_my_component(...).
---This can be useful if you have your own "basic" components that you don't want to re-create each time.
---@param name string Module name
---@param module table Lua table with component
function M.register(name, module)
druid_instance["new_" .. name] = function(self, ...)
return druid_instance.new(self, module, ...)
end
return druid_instance["new_" .. name]
end
--- Create a new Druid instance for creating GUI components.
--
-- @function druid.new
-- @tparam table context The Druid context. Usually, this is the self of the gui_script. It is passed into all Druid callbacks.
-- @tparam table|nil style The Druid style table to override style parameters for this Druid instance.
-- @treturn druid_instance The Druid instance @{DruidInstance}.
-- @usage
-- local druid = require("druid.druid")
--
-- function init(self)
-- self.druid = druid.new(self)
-- end
---Create a new Druid instance for creating GUI components.
---@param context table The Druid context. Usually, this is the self of the gui_script. It is passed into all Druid callbacks.
---@param style table|nil The Druid style table to override style parameters for this Druid instance.
---@return druid_instance druid_instance The new Druid instance
function M.new(context, style)
clean_deleted_druid_instances()
@@ -117,65 +99,35 @@ function M.new(context, style)
local new_instance = setmetatable({}, { __index = druid_instance })
new_instance:initialize(context, style)
table.insert(_instances, new_instance)
table.insert(druid_instances, new_instance)
return new_instance
end
--- Set your own default style for all Druid instances.
--
-- To create your own style file, copy the default style file and make changes to it.
-- Register the new style before creating your Druid instances.
-- @function druid.set_default_style
-- @tparam table style Druid style module
-- @usage
-- local my_style = require("path.to.my.style")
-- druid.set_default_style(my_style)
---Set the default style for all Druid instances.
---@param style table Default style
function M.set_default_style(style)
settings.default_style = style or {}
end
--- Set the text function for the LangText component.
--
-- The Druid locale component will call this function to get translated text.
-- After setting the text function, all existing locale components will be updated.
-- @function druid.set_text_function
-- @tparam function callback Get localized text function
-- @usage
-- druid.set_text_function(function(text_id)
-- return lang_data[text_id] -- Replace with your real function
-- end)
---Set the text function for the LangText component.
---@param callback fun(text_id: string): string Get localized text function
function M.set_text_function(callback)
settings.get_text = callback or const.EMPTY_FUNCTION
M.on_language_change()
end
--- Set the Druid sound function to play UI sounds if used.
--
-- Set a function to play a sound given a sound_id. This function is used for button clicks to play the "click" sound.
-- It can also be used to play sounds in your custom components (see the default Druid style file for an example).
-- @function druid.set_sound_function
-- @tparam function callback Sound play callback
-- @usage
-- druid.set_sound_function(function(sound_id)
-- sound.play(sound_id) -- Replace with your real function
-- end)
---Set the sound function to able components to play sounds.
---@param callback fun(sound_id: string) Sound play callback
function M.set_sound_function(callback)
settings.play_sound = callback or const.EMPTY_FUNCTION
end
--- Set the window callback to enable on_focus_gain and on_focus_lost functions.
--
-- This is used to trigger the on_focus_lost and on_focus_gain functions in Druid components.
-- @function druid.on_window_callback
-- @tparam string event Event param from window listener
-- @usage
-- window.set_listener(function(_, event)
-- druid.on_window_callback(event)
-- end)
---Set the window callback to enable Druid window events.
---@param event constant Event param from window listener
function M.on_window_callback(event)
local instances = get_druid_instances()
@@ -195,12 +147,7 @@ function M.on_window_callback(event)
end
--- Call this function when the game language changes.
--
-- This function will translate all current LangText components.
-- @function druid.on_language_change
-- @usage
-- druid.on_language_change()
---Call this function when the game language changes.
function M.on_language_change()
local instances = get_druid_instances()

View File

@@ -48,10 +48,6 @@ def process_component(node_name, component_name):
component_define += "\n--TODO: Replace prefab_name with grid element prefab"
component_define += "\n\tself.{0} = self.druid:new_static_grid(\"{1}\", \"prefab_name\", 1)".format(node_name, node_name)
if node_name.startswith("dynamic_grid"):
component_annotations += "\n---@field {0} druid.dynamic_grid".format(node_name)
component_define += "\n\tself.{0} = self.druid:new_dynamic_grid(\"{1}\")".format(node_name, node_name)
if node_name.startswith("scroll_view"):
field_name = node_name.replace("_view", "")
content_name = node_name.replace("_view", "_content")

View File

@@ -8,6 +8,7 @@
-- @module DruidEvent
-- @alias druid.event
---@class druid.event
local M = {}
M.COUNTER = 0
@@ -39,11 +40,11 @@ end
--- Check is event subscribed.
-- @tparam DruidEvent self @{DruidEvent}
-- @tparam DruidEvent self DruidEvent
-- @tparam function callback Callback itself
-- @tparam any|nil callback_context Additional context as first param to callback call
-- @treturn boolean, number|nil @Is event subscribed, return index of callback in event as second param
function M.is_subscribed(self, callback, callback_context)
function M:is_subscribed(callback, callback_context)
if #self == 0 then
return false, nil
end
@@ -60,7 +61,7 @@ end
--- Subscribe callback on event
-- @tparam DruidEvent self @{DruidEvent}
-- @tparam DruidEvent self DruidEvent
-- @tparam function callback Callback itself
-- @tparam any|nil callback_context Additional context as first param to callback call, usually it's self
-- @treturn boolean True if callback was subscribed
@@ -71,7 +72,7 @@ end
-- ...
-- local button = self.druid:new_button("button", callback)
-- button.on_long_click:subscribe(on_long_callback, self)
function M.subscribe(self, callback, callback_context)
function M:subscribe(callback, callback_context)
assert(type(self) == "table", "You should subscribe to event with : syntax")
assert(callback, "A function must be passed to subscribe to an event")
@@ -85,7 +86,7 @@ end
--- Unsubscribe callback on event
-- @tparam DruidEvent self @{DruidEvent}
-- @tparam DruidEvent self DruidEvent
-- @tparam function callback Callback itself
-- @tparam any|nil callback_context Additional context as first param to callback call
-- @usage
@@ -94,7 +95,7 @@ end
-- end
-- ...
-- button.on_long_click:unsubscribe(on_long_callback, self)
function M.unsubscribe(self, callback, callback_context)
function M:unsubscribe(callback, callback_context)
assert(callback, "A function must be passed to subscribe to an event")
local _, event_index = self:is_subscribed(callback, callback_context)
@@ -108,17 +109,17 @@ end
--- Return true, if event have at lease one handler
-- @tparam DruidEvent self @{DruidEvent}
-- @tparam DruidEvent self DruidEvent
-- @treturn boolean True if event have handlers
-- @usage
-- local is_long_click_handler_exists = button.on_long_click:is_exist()
function M.is_exist(self)
function M:is_exist()
return #self > 0
end
--- Return true, if event not have handler
--- @tparam DruidEvent self @{DruidEvent}
--- @tparam DruidEvent self DruidEvent
--- @treturn boolean True if event not have handlers
--- @usage
--- local is_long_click_handler_not_exists = button.on_long_click:is_empty()
@@ -128,10 +129,10 @@ end
--- Clear the all event handlers
-- @tparam DruidEvent self @{DruidEvent}
-- @tparam DruidEvent self DruidEvent
-- @usage
-- button.on_long_click:clear()
function M.clear(self)
function M:clear()
for index = #self, 1, -1 do
self[index] = nil
end
@@ -139,14 +140,14 @@ end
--- Trigger the event and call all subscribed callbacks
-- @tparam DruidEvent self @{DruidEvent}
-- @tparam DruidEvent self DruidEvent
-- @tparam any ... All event params
-- @usage
-- local Event = require("druid.event")
-- ...
-- local event = Event()
-- event:trigger("Param1", "Param2")
function M.trigger(self, ...)
function M:trigger(...)
if #self == 0 then
return
end

View File

@@ -10,10 +10,10 @@
--- The Druid scroll component
-- @tfield Scroll scroll @{Scroll}
-- @tfield Scroll scroll Scroll
--- The Druid Grid component
-- @tfield StaticGrid grid @{StaticGrid}, @{DynamicGrid}
-- @tfield StaticGrid grid StaticGrid}, @{DynamicGrid
--- The current progress of scroll posititon
-- @tfield number scroll_progress
@@ -25,13 +25,13 @@
-- @tfield number last_index
--- Event triggered when scroll progress is changed; event(self, progress_value)
-- @tfield DruidEvent on_scroll_progress_change @{DruidEvent}
-- @tfield DruidEvent on_scroll_progress_change DruidEvent
---On DataList visual element created Event callback(self, index, node, instance)
-- @tfield DruidEvent on_element_add @{DruidEvent}
-- @tfield DruidEvent on_element_add DruidEvent
---On DataList visual element created Event callback(self, index)
-- @tfield DruidEvent on_element_remove @{DruidEvent}
-- @tfield DruidEvent on_element_remove DruidEvent
---
@@ -40,15 +40,27 @@ local helper = require("druid.helper")
local component = require("druid.component")
local Event = require("druid.event")
local DataList = component.create("data_list")
---@class druid.data_list: druid.base_component
---@field scroll druid.scroll
---@field grid druid.grid
---@field on_scroll_progress_change druid.event
---@field on_element_add druid.event
---@field on_element_remove druid.event
---@field private _create_function function
---@field private _is_use_cache boolean
---@field private _cache table
---@field private _data table
---@field private _data_visual table
---@field private top_index number
local M = component.create("data_list")
--- The @{DataList} constructor
-- @tparam DataList self @{DataList}
-- @tparam Scroll scroll The @{Scroll} instance for Data List component
-- @tparam StaticGrid grid The @{StaticGrid} or @{DynamicGrid} instance for Data List component
--- The DataList constructor
-- @tparam DataList self DataList
-- @tparam Scroll scroll The Scroll instance for Data List component
-- @tparam StaticGrid grid The StaticGrid} or @{DynamicGrid instance for Data List component
-- @tparam function create_function The create function callback(self, data, index, data_list). Function should return (node, [component])
function DataList.init(self, scroll, grid, create_function)
function M:init(scroll, grid, create_function)
self.scroll = scroll
self.grid = grid
if self.grid.style then
@@ -75,28 +87,28 @@ end
--- Druid System on_remove function
-- @tparam DataList self @{DataList}
function DataList.on_remove(self)
-- @tparam DataList self DataList
function M:on_remove()
self:clear()
self.scroll.on_scroll:unsubscribe(self._refresh, self)
end
--- Set refresh function for DataList component
-- @tparam DataList self @{DataList}
-- @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)
function M:set_use_cache(is_use_cache)
self._is_use_cache = is_use_cache
return self
end
--- Set new data set for DataList component
-- @tparam DataList self @{DataList}
-- @tparam DataList self DataList
-- @tparam table data The new data array
-- @treturn druid.data_list Current DataList instance
function DataList.set_data(self, data)
function M:set_data(data)
self._data = data or {}
self:_refresh()
@@ -105,19 +117,19 @@ end
--- Return current data from DataList component
-- @tparam DataList self @{DataList}
-- @tparam DataList self DataList
-- @treturn table The current data array
function DataList.get_data(self)
function M:get_data()
return self._data
end
--- Add element to DataList. Currenly untested
-- @tparam DataList self @{DataList}
-- @tparam DataList self DataList
-- @tparam table data
-- @tparam number|nil index
-- @tparam number|nil shift_policy The constant from const.SHIFT.*
function DataList.add(self, data, index, shift_policy)
function M:add(data, index, shift_policy)
index = index or #self._data + 1
shift_policy = shift_policy or const.SHIFT.RIGHT
@@ -127,20 +139,20 @@ end
--- Remove element from DataList. Currenly untested
-- @tparam DataList self @{DataList}
-- @tparam DataList self DataList
-- @tparam number|nil index
-- @tparam number|nil shift_policy The constant from const.SHIFT.*
function DataList.remove(self, index, shift_policy)
function M:remove(index, shift_policy)
helper.remove_with_shift(self._data, index, shift_policy)
self:_refresh()
end
--- Remove element from DataList by data value. Currenly untested
-- @tparam DataList self @{DataList}
-- @tparam DataList self DataList
-- @tparam table data
-- @tparam number|nil shift_policy The constant from const.SHIFT.*
function DataList.remove_by_data(self, data, shift_policy)
function M:remove_by_data(data, shift_policy)
local index = helper.contains(self._data, data)
if index then
helper.remove_with_shift(self._data, index, shift_policy)
@@ -150,17 +162,17 @@ end
--- Clear the DataList and refresh visuals
-- @tparam DataList self @{DataList}
function DataList.clear(self)
-- @tparam DataList self DataList
function M:clear()
self._data = {}
self:_refresh()
end
--- Return index for data value
-- @tparam DataList self @{DataList}
-- @tparam DataList self DataList
-- @tparam table data
function DataList.get_index(self, data)
function M:get_index(data)
for index, value in pairs(self._data) do
if value == data then
return index
@@ -172,9 +184,9 @@ end
--- Return all currenly created nodes in DataList
-- @tparam DataList self @{DataList}
-- @tparam DataList self DataList
-- @treturn node[] List of created nodes
function DataList.get_created_nodes(self)
function M:get_created_nodes()
local nodes = {}
for index, data in pairs(self._data_visual) do
@@ -186,9 +198,9 @@ end
--- Return all currenly created components in DataList
-- @tparam DataList self @{DataList}
-- @tparam DataList self DataList
-- @treturn druid.base_component[] List of created nodes
function DataList.get_created_components(self)
function M:get_created_components()
local components = {}
for index, data in pairs(self._data_visual) do
@@ -200,19 +212,19 @@ end
--- Instant scroll to element with passed index
-- @tparam DataList self @{DataList}
-- @tparam DataList self DataList
-- @tparam number index
function DataList.scroll_to_index(self, index)
function M:scroll_to_index(index)
local pos = self.grid:get_pos(index)
self.scroll:scroll_to(pos)
end
--- Add element at passed index using cache or create new
-- @tparam DataList self @{DataList}
-- @tparam DataList self DataList
-- @tparam number index
-- @local
function DataList._add_at(self, index)
function M:_add_at(index)
if self._data_visual[index] then
self:_remove_at(index)
end
@@ -243,10 +255,10 @@ end
--- Remove element from passed index and add it to cache if applicable
-- @tparam DataList self @{DataList}
-- @tparam DataList self DataList
-- @tparam number index
-- @local
function DataList._remove_at(self, index)
function M:_remove_at(index)
self.grid:remove(index, const.SHIFT.NO_SHIFT)
local visual_data = self._data_visual[index]
@@ -274,9 +286,9 @@ end
--- Refresh all elements in DataList
-- @tparam DataList self @{DataList}
-- @tparam DataList self DataList
-- @local
function DataList._refresh(self)
function M:_refresh()
self.scroll:set_size(self.grid:get_size_for(#self._data))
local start_pos = -self.scroll.position --[[@as vector3]]
@@ -313,4 +325,4 @@ function DataList._refresh(self)
end
return DataList
return M

View File

@@ -1,427 +0,0 @@
-- Copyright (c) 2021 Maksim Tuprikov <insality@gmail.com>. This code is licensed under MIT license
--- Component to handle placing components in row
--
-- <a href="https://insality.github.io/druid/druid/index.html?example=general_grid" target="_blank"><b>Example Link</b></a>
-- @module DynamicGrid
-- @within BaseComponent
-- @alias druid.dynamic_grid
--- On item add callback(self, node, index)
-- @tfield DruidEvent on_add_item @{DruidEvent}
--- On item remove callback(self, index)
-- @tfield DruidEvent on_remove_item @{DruidEvent}
--- On item add or remove callback(self, index)
-- @tfield DruidEvent on_change_items @{DruidEvent}
--- On grid clear callback(self)
-- @tfield DruidEvent on_clear @{DruidEvent}
--- On update item positions callback(self)
-- @tfield DruidEvent on_update_positions @{DruidEvent}
--- Parent gui node
-- @tfield node parent
--- List of all grid elements. Contains from node, pos, size, pivot
-- @tfield node[] nodes
--- The first index of node in grid
-- @tfield number first_index
--- The last index of node in grid
-- @tfield number last_index
--- Item size
-- @tfield vector3 node_size
--- The size of item content
-- @tfield vector4 border
---
local const = require("druid.const")
local Event = require("druid.event")
local helper = require("druid.helper")
local component = require("druid.component")
local DynamicGrid = component.create("dynamic_grid")
local SIDE_VECTORS = {
LEFT = vmath.vector3(-1, 0, 0),
RIGHT = vmath.vector3(1, 0, 0),
TOP = vmath.vector3(0, -1, 0),
BOT = vmath.vector3(0, 1, 0),
}
local AVAILABLE_PIVOTS = {
gui.PIVOT_N,
gui.PIVOT_S,
gui.PIVOT_W,
gui.PIVOT_E,
}
--- The @{DynamicGrid} constructor
-- @tparam DynamicGrid self @{DynamicGrid}
-- @tparam node parent The gui node parent, where items will be placed
function DynamicGrid.init(self, parent)
self.parent = self:get_node(parent)
local parent_pivot = gui.get_pivot(self.parent)
self.pivot = helper.get_pivot_offset(parent_pivot)
assert(helper.contains(AVAILABLE_PIVOTS, parent_pivot), const.ERRORS.GRID_DYNAMIC_ANCHOR)
self.side = ((parent_pivot == gui.PIVOT_W or parent_pivot == gui.PIVOT_E)
and const.SIDE.X or const.SIDE.Y)
self.nodes = {}
self.border = vmath.vector4(0) -- Current grid content size
self.on_add_item = Event()
self.on_remove_item = Event()
self.on_change_items = Event()
self.on_clear = Event()
self.on_update_positions = Event()
self._set_position_function = gui.set_position
end
function DynamicGrid.on_layout_change(self)
self:_update(true)
end
--- Return pos for grid node index
-- @tparam DynamicGrid self @{DynamicGrid}
-- @tparam number index The grid element index
-- @tparam node node The node to be placed
-- @tparam number|nil origin_index Index of nearby node
-- @treturn vector3 node position
function DynamicGrid.get_pos(self, index, node, origin_index)
local origin_node = self.nodes[origin_index]
-- If anchor node is not exist, check around nodes
if not origin_node then
if self.nodes[index + 1] then
origin_index = index + 1
end
if self.nodes[index - 1] then
origin_index = index - 1
end
origin_node = self.nodes[origin_index]
end
if not origin_node then
assert(not self.first_index, "Dynamic Grid can't have gaps between nodes. Error on grid:add")
-- If not origin node, so it should be first element in the grid
local size = helper.get_scaled_size(node)
local pivot = const.PIVOTS[gui.get_pivot(node)]
return vmath.vector3(
size.x * pivot.x - size.x * self.pivot.x,
size.y * pivot.y - size.y * self.pivot.y,
0)
end
if origin_node then
-- Other nodes spawn from other side of the origin node
local is_forward = origin_index < index
local delta = is_forward and 1 or -1
return self:_get_next_node_pos(index - delta, node, self:_get_side_vector(self.side, is_forward))
end
end
--- Add new node to the grid
-- @tparam DynamicGrid self @{DynamicGrid}
-- @tparam node node Gui node
-- @tparam number|nil index The node position. By default add as last node
-- @tparam number|nil shift_policy How shift nodes, if required. Default: const.SHIFT.RIGHT
-- @tparam boolean|nil is_instant If true, update node positions instantly
function DynamicGrid.add(self, node, index, shift_policy, is_instant)
shift_policy = shift_policy or const.SHIFT.RIGHT
local delta = shift_policy -- -1 or 1 or 0
-- By default add node at end
index = index or ((self.last_index or 0) + 1)
-- If node exist at index place, shifting them
local is_shift = self.nodes[index] and shift_policy ~= const.SHIFT.NO_SHIFT
if is_shift then
-- We need to iterate from index to start or end grid, depends of shift side
local start_index = shift_policy == const.SHIFT.LEFT and self.first_index or self.last_index
for i = start_index, index, -delta do
self.nodes[i + delta] = self.nodes[i]
end
end
self:_add_node(node, index, index - delta)
-- After shifting we should recalc node poses
if is_shift then
-- We need to iterate from placed node to start or end grid, depends of shift side
local target_index = shift_policy == const.SHIFT.LEFT and self.first_index or self.last_index
for i = index + delta, target_index + delta, delta do
local move_node = self.nodes[i]
move_node.pos = self:get_pos(i, move_node.node, i - delta)
end
end
-- Sync grid data
self:_update(is_instant)
self.on_add_item:trigger(self:get_context(), node, index)
self.on_change_items:trigger(self:get_context(), index)
end
--- Remove the item from the grid. Note that gui node will be not deleted
-- @tparam DynamicGrid self @{DynamicGrid}
-- @tparam number index The grid node index to remove
-- @tparam number|nil shift_policy How shift nodes, if required. Default: const.SHIFT.RIGHT
-- @tparam boolean|nil is_instant If true, update node positions instantly
-- @treturn node The deleted gui node from grid
function DynamicGrid.remove(self, index, shift_policy, is_instant)
shift_policy = shift_policy or const.SHIFT.RIGHT
local delta = shift_policy -- -1 or 1 or 0
assert(self.nodes[index], "No grid item at given index " .. index)
-- Just set nil for delete node data
local removed_node = self.nodes[index].node
self.nodes[index] = nil
-- After delete node, we should shift nodes and recalc their poses, depends from is_shift_left
if shift_policy ~= const.SHIFT.NO_SHIFT then
local target_index = shift_policy == const.SHIFT.LEFT and self.first_index or self.last_index
for i = index, target_index, delta do
self.nodes[i] = self.nodes[i + delta]
if self.nodes[i] then
self.nodes[i].pos = self:get_pos(i, self.nodes[i].node, i - delta)
end
end
end
-- Sync grid data
self:_update(is_instant)
self.on_remove_item:trigger(self:get_context(), index)
self.on_change_items:trigger(self:get_context(), index)
return removed_node
end
--- Return grid content size
-- @tparam DynamicGrid self @{DynamicGrid}
-- @tparam vector3 border
-- @treturn vector3 The grid content size
function DynamicGrid.get_size(self, border)
border = border or self.border
return vmath.vector3(
border.z - border.x,
border.y - border.w,
0)
end
--- Return DynamicGrid offset, where DynamicGrid content starts.
-- @tparam DynamicGrid self @{DynamicGrid} The DynamicGrid instance
-- @treturn vector3 The DynamicGrid offset
function DynamicGrid.get_offset(self)
local size = self:get_size()
local borders = self:get_borders()
local offset = vmath.vector3(
(borders.z + borders.x)/2 + size.x * self.pivot.x,
(borders.y + borders.w)/2 + size.y * self.pivot.y,
0)
return offset
end
--- Return grid content borders
-- @tparam DynamicGrid self @{DynamicGrid}
-- @treturn vector3 The grid content borders
function DynamicGrid.get_borders(self)
return self.border
end
--- Return grid index by node
-- @tparam DynamicGrid self @{DynamicGrid}
-- @tparam node node The gui node in the grid
-- @treturn number The node index
function DynamicGrid.get_index_by_node(self, node)
for index, node_info in pairs(self.nodes) do
if node == node_info.node then
return index
end
end
return nil
end
--- Return array of all node positions
-- @tparam DynamicGrid self @{DynamicGrid}
-- @treturn vector3[] All grid node positions
function DynamicGrid.get_all_pos(self)
local result = {}
for i, node in pairs(self.nodes) do
table.insert(result, gui.get_position(node.node))
end
return result
end
--- Change set position function for grid nodes. It will call on
-- update poses on grid elements. Default: gui.set_position
-- @tparam DynamicGrid self @{DynamicGrid}
-- @tparam function callback Function on node set position
-- @treturn druid.dynamic_grid Current grid instance
function DynamicGrid.set_position_function(self, callback)
self._set_position_function = callback or gui.set_position
return self
end
--- Clear grid nodes array. GUI nodes will be not deleted!
-- If you want to delete GUI nodes, use dynamic_grid.nodes array before grid:clear
-- @tparam DynamicGrid self @{DynamicGrid}
-- @treturn druid.dynamic_grid Current grid instance
function DynamicGrid.clear(self)
self.nodes = {}
self:_update()
self.on_clear:trigger(self:get_context())
return self
end
function DynamicGrid._add_node(self, node, index, origin_index)
self.nodes[index] = {
node = node,
pos = self:get_pos(index, node, origin_index),
size = helper.get_scaled_size(node),
pivot = const.PIVOTS[gui.get_pivot(node)]
}
-- Add new item instantly in new pos
gui.set_parent(node, self.parent)
gui.set_position(node, self.nodes[index].pos)
end
--- Update grid inner state
-- @tparam DynamicGrid self @{DynamicGrid}
-- @tparam boolean|nil is_instant If true, node position update instantly, otherwise with set_position_function callback
-- @local
function DynamicGrid._update(self, is_instant)
self:_update_indexes()
self:_update_borders()
self:_update_pos(is_instant)
end
--- Update first and last indexes of grid nodes
-- @tparam DynamicGrid self @{DynamicGrid}
-- @local
function DynamicGrid._update_indexes(self)
self.first_index = nil
self.last_index = nil
for index in pairs(self.nodes) do
self.first_index = self.first_index or index
self.last_index = self.last_index or index
self.first_index = math.min(self.first_index, index)
self.last_index = math.max(self.last_index, index)
end
end
--- Update grid content borders, recalculate min and max values
-- @tparam DynamicGrid self @{DynamicGrid}
-- @local
function DynamicGrid._update_borders(self)
if not self.first_index then
self.border = vmath.vector4(0)
return
end
self.border = vmath.vector4(math.huge, -math.huge, -math.huge, math.huge)
for index, node in pairs(self.nodes) do
local pos = node.pos
local size = node.size
local pivot = node.pivot
local left = pos.x - size.x/2 - (size.x * pivot.x)
local right = pos.x + size.x/2 - (size.x * pivot.x)
local top = pos.y + size.y/2 - (size.y * pivot.y)
local bottom = pos.y - size.y/2 - (size.y * pivot.y)
self.border.x = math.min(self.border.x, left)
self.border.y = math.max(self.border.y, top)
self.border.z = math.max(self.border.z, right)
self.border.w = math.min(self.border.w, bottom)
end
end
--- Update grid nodes position
-- @tparam DynamicGrid self @{DynamicGrid}
-- @tparam boolean|nil is_instant If true, node position update instantly, otherwise with set_position_function callback
-- @local
function DynamicGrid._update_pos(self, is_instant)
for index, node in pairs(self.nodes) do
if is_instant then
gui.set_position(node.node, node.pos)
else
self._set_position_function(node.node, node.pos)
end
end
self.on_update_positions:trigger(self:get_context())
end
function DynamicGrid._get_next_node_pos(self, origin_node_index, new_node, place_side)
local node = self.nodes[origin_node_index]
local new_node_size = helper.get_scaled_size(new_node)
local new_pivot = const.PIVOTS[gui.get_pivot(new_node)]
local dist_x = (node.size.x/2 + new_node_size.x/2) * place_side.x
local dist_y = (node.size.y/2 + new_node_size.y/2) * place_side.y
local node_center_x = node.pos.x - node.size.x * node.pivot.x
local node_center_y = node.pos.y - node.size.y * node.pivot.y
return vmath.vector3(
node_center_x + dist_x + new_node_size.x * new_pivot.x,
node_center_y - dist_y + new_node_size.y * new_pivot.y,
0
)
end
--- Return side vector to correct node shifting
function DynamicGrid._get_side_vector(self, side, is_forward)
if side == const.SIDE.X then
return is_forward and SIDE_VECTORS.RIGHT or SIDE_VECTORS.LEFT
end
if side == const.SIDE.Y then
return is_forward and SIDE_VECTORS.BOT or SIDE_VECTORS.TOP
end
end
return DynamicGrid

View File

@@ -8,10 +8,10 @@
-- @alias druid.hotkey
--- On hotkey released callback(self, argument)
-- @tfield DruidEvent on_hotkey_pressed @{DruidEvent}
-- @tfield DruidEvent on_hotkey_pressed DruidEvent
--- On hotkey released callback(self, argument)
-- @tfield DruidEvent on_hotkey_released @{DruidEvent}
-- @tfield DruidEvent on_hotkey_released DruidEvent
--- Visual node
-- @tfield node node
@@ -20,7 +20,7 @@
-- @tfield node|nil click_node
--- Button component from click_node
-- @tfield Button button @{Button}
-- @tfield Button button Button
---
@@ -28,15 +28,21 @@ local helper = require("druid.helper")
local component = require("druid.component")
local Event = require("druid.event")
local Hotkey = component.create("hotkey")
---@class druid.hotkey: druid.base_component
---@field on_hotkey_pressed druid.event
---@field on_hotkey_released druid.event
---@field style table
---@field private _hotkeys table
---@field private _modificators table
local M = component.create("hotkey")
--- The @{Hotkey} constructor
-- @tparam Hotkey self @{Hotkey}
--- The Hotkey constructor
-- @tparam Hotkey self Hotkey
-- @tparam string[]|string keys The keys to be pressed for trigger callback. Should contains one key and any modificator keys
-- @tparam function callback The callback function
-- @tparam any|nil callback_argument The argument to pass into the callback function
function Hotkey.init(self, keys, callback, callback_argument)
function M:init(keys, callback, callback_argument)
self.druid = self:get_druid()
self._hotkeys = {}
@@ -56,7 +62,7 @@ end
-- or create your own style
-- @table style
-- @tfield string[] MODIFICATORS The list of action_id as hotkey modificators
function Hotkey.on_style_change(self, style)
function M:on_style_change(style)
self.style = {}
self.style.MODIFICATORS = style.MODIFICATORS or {}
@@ -67,11 +73,11 @@ end
--- Add hotkey for component callback
-- @tparam Hotkey self @{Hotkey}
-- @tparam Hotkey self Hotkey
-- @tparam string[]|hash[]|string|hash keys that have to be pressed before key pressed to activate
-- @tparam any|nil callback_argument The argument to pass into the callback function
-- @treturn Hotkey Current instance
function Hotkey.add_hotkey(self, keys, callback_argument)
function M:add_hotkey(keys, callback_argument)
keys = keys or {}
if type(keys) == "string" then
keys = { keys }
@@ -110,14 +116,14 @@ function Hotkey.add_hotkey(self, keys, callback_argument)
end
function Hotkey.on_focus_gained(self)
function M:on_focus_gained()
for k, v in pairs(self._modificators) do
self._modificators[k] = false
end
end
function Hotkey.on_input(self, action_id, action)
function M:on_input(action_id, action)
if not action_id or #self._hotkeys == 0 then
return false
end
@@ -168,13 +174,13 @@ end
--- If true, the callback will be triggered on action.repeated
-- @tparam Hotkey self @{Hotkey}
-- @tparam Hotkey self Hotkey
-- @tparam bool is_enabled_repeated The flag value
-- @treturn Hotkey
function Hotkey.set_repeat(self, is_enabled_repeated)
function M:set_repeat(is_enabled_repeated)
self._is_process_repeated = is_enabled_repeated
return self
end
return Hotkey
return M

View File

@@ -10,25 +10,25 @@
-- @alias druid.input
--- On input field select callback(self, input_instance)
-- @tfield DruidEvent on_input_select @{DruidEvent}
-- @tfield DruidEvent on_input_select DruidEvent
--- On input field unselect callback(self, input_text, input_instance)
-- @tfield DruidEvent on_input_unselect @{DruidEvent}
-- @tfield DruidEvent on_input_unselect DruidEvent
--- On input field text change callback(self, input_text)
-- @tfield DruidEvent on_input_text @{DruidEvent}
-- @tfield DruidEvent on_input_text DruidEvent
--- On input field text change to empty string callback(self, input_text)
-- @tfield DruidEvent on_input_empty @{DruidEvent}
-- @tfield DruidEvent on_input_empty DruidEvent
--- On input field text change to max length string callback(self, input_text)
-- @tfield DruidEvent on_input_full @{DruidEvent}
-- @tfield DruidEvent on_input_full DruidEvent
--- On trying user input with not allowed character callback(self, params, input_text)
-- @tfield DruidEvent on_input_wrong @{DruidEvent}
-- @tfield DruidEvent on_input_wrong DruidEvent
--- On cursor position change callback(self, cursor_index, start_index, end_index)
-- @tfield DruidEvent on_select_cursor_change @{DruidEvent}
-- @tfield DruidEvent on_select_cursor_change DruidEvent
--- The cursor index. The index of letter cursor after. Leftmost cursor - 0
-- @tfield number cursor_index
@@ -40,7 +40,7 @@
-- @tfield number end_index
--- Text component
-- @tfield Text text @{Text}
-- @tfield Text text Text
--- Current input value
-- @tfield string value
@@ -61,7 +61,7 @@
-- @tfield number marked_text_width
--- Button component
-- @tfield Button button @{Button}
-- @tfield Button button Button
--- Is current input selected now
-- @tfield boolean is_selected
@@ -87,9 +87,19 @@ local component = require("druid.component")
local utf8_lua = require("druid.system.utf8")
local utf8 = utf8 or utf8_lua
local Input = component.create("input")
---@class druid.input: druid.base_component
---@field on_input_select druid.event
---@field on_input_unselect druid.event
---@field on_input_text druid.event
---@field on_input_empty druid.event
---@field on_input_full druid.event
---@field on_input_wrong druid.event
---@field on_select_cursor_change druid.event
---@field style table
---@field text druid.text
local M = component.create("input")
Input.ALLOWED_ACTIONS = {
M.ALLOWED_ACTIONS = {
[const.ACTION_TOUCH] = true,
[const.ACTION_TEXT] = true,
[const.ACTION_MARKED_TEXT] = true,
@@ -132,7 +142,7 @@ end
-- @tfield function on_select (self, button_node) Callback on input field selecting
-- @tfield function on_unselect (self, button_node) Callback on input field unselecting
-- @tfield function on_input_wrong (self, button_node) Callback on wrong user input
function Input.on_style_change(self, style)
function M:on_style_change(style)
self.style = {}
self.style.IS_LONGTAP_ERASE = style.IS_LONGTAP_ERASE or false
@@ -145,12 +155,12 @@ function Input.on_style_change(self, style)
end
--- The @{Input} constructor
-- @tparam Input self @{Input}
--- The Input constructor
-- @tparam Input self Input
-- @tparam node click_node Node to enabled input component
-- @tparam node|Text text_node Text node what will be changed on user input. You can pass text component instead of text node name @{Text}
-- @tparam node|Text text_node Text node what will be changed on user input. You can pass text component instead of text node name Text
-- @tparam number|nil keyboard_type Gui keyboard type for input field
function Input.init(self, click_node, text_node, keyboard_type)
function M:init(click_node, text_node, keyboard_type)
self.druid = self:get_druid()
if type(text_node) == "table" then
@@ -201,8 +211,8 @@ function Input.init(self, click_node, text_node, keyboard_type)
end
function Input.on_input(self, action_id, action)
if not (action_id == nil or Input.ALLOWED_ACTIONS[action_id]) then
function M:on_input(action_id, action)
if not (action_id == nil or M.ALLOWED_ACTIONS[action_id]) then
return false
end
@@ -299,17 +309,17 @@ function Input.on_input(self, action_id, action)
end
function Input.on_focus_lost(self)
function M:on_focus_lost()
self:unselect()
end
function Input.on_input_interrupt(self)
function M:on_input_interrupt()
--self:unselect()
end
function Input.get_text_selected(self)
function M:get_text_selected()
if self.start_index == self.end_index then
return self.value
end
@@ -318,10 +328,10 @@ function Input.get_text_selected(self)
end
--- Replace selected text with new text
-- @tparam Input self @{Input}
-- @tparam Input self Input
-- @tparam string text The text to replace selected text
-- @treturn string New input text
function Input.get_text_selected_replaced(self, text)
function M:get_text_selected_replaced(text)
local left_part = utf8.sub(self.value, 1, self.start_index)
local right_part = utf8.sub(self.value, self.end_index + 1, utf8.len(self.value))
local result = left_part .. text .. right_part
@@ -336,9 +346,9 @@ end
--- Set text for input field
-- @tparam Input self @{Input}
-- @tparam Input self Input
-- @tparam string input_text The string to apply for input field
function Input.set_text(self, input_text)
function M:set_text(input_text)
input_text = tostring(input_text or "")
-- Case when update with marked text
@@ -385,8 +395,8 @@ end
--- Select input field. It will show the keyboard and trigger on_select events
-- @tparam Input self @{Input}
function Input.select(self)
-- @tparam Input self Input
function M:select()
gui.reset_keyboard()
self.marked_value = ""
if not self.is_selected then
@@ -410,8 +420,8 @@ end
--- Remove selection from input. It will hide the keyboard and trigger on_unselect events
-- @tparam Input self @{Input}
function Input.unselect(self)
-- @tparam Input self Input
function M:unselect()
gui.reset_keyboard()
self.marked_value = ""
self.value = self.current_value
@@ -429,9 +439,9 @@ end
--- Return current input field text
-- @tparam Input self @{Input}
-- @tparam Input self Input
-- @treturn string The current input field text
function Input.get_text(self)
function M:get_text()
if self.marked_value ~= "" then
return self.value .. self.marked_value
end
@@ -442,10 +452,10 @@ end
--- Set maximum length for input field.
-- Pass nil to make input field unliminted (by default)
-- @tparam Input self @{Input}
-- @tparam Input self Input
-- @tparam number max_length Maximum length for input text field
-- @treturn druid.input Current input instance
function Input.set_max_length(self, max_length)
function M:set_max_length(max_length)
self.max_length = max_length
return self
end
@@ -454,19 +464,19 @@ end
--- Set allowed charaters for input field.
-- See: https://defold.com/ref/stable/string/
-- ex: [%a%d] for alpha and numeric
-- @tparam Input self @{Input}
-- @tparam Input self Input
-- @tparam string characters Regulax exp. for validate user input
-- @treturn druid.input Current input instance
function Input.set_allowed_characters(self, characters)
function M:set_allowed_characters(characters)
self.allowed_characters = characters
return self
end
--- Reset current input selection and return previous value
-- @tparam Input self @{Input}
-- @tparam Input self Input
-- @treturn druid.input Current input instance
function Input.reset_changes(self)
function M:reset_changes()
self:set_text(self.previous_value)
self:unselect()
return self
@@ -474,12 +484,12 @@ end
--- Set cursor position in input field
-- @tparam Input self @{Input}
-- @tparam Input self Input
-- @tparam number|nil cursor_index Cursor index for cursor position, if nil - will be set to the end of the text
-- @tparam number|nil start_index Start index for cursor position, if nil - will be set to the end of the text
-- @tparam number|nil end_index End index for cursor position, if nil - will be set to the start_index
-- @treturn druid.input Current input instance
function Input.select_cursor(self, cursor_index, start_index, end_index)
function M:select_cursor(cursor_index, start_index, end_index)
local len = utf8.len(self.value)
self.cursor_index = cursor_index or len
@@ -497,11 +507,11 @@ end
--- Change cursor position by delta
-- @tparam Input self @{Input}
-- @tparam Input self Input
-- @tparam number delta side for cursor position, -1 for left, 1 for right
-- @tparam boolean is_add_to_selection (Shift key)
-- @tparam boolean is_move_to_end (Ctrl key)
function Input.move_selection(self, delta, is_add_to_selection, is_move_to_end)
function M:move_selection(delta, is_add_to_selection, is_move_to_end)
local len = utf8.len(self.value)
local cursor_index = self.cursor_index
local start_index, end_index -- if nil, the selection will be 0 at cursor position
@@ -559,4 +569,4 @@ function Input.move_selection(self, delta, is_add_to_selection, is_move_to_end)
end
return Input
return M

View File

@@ -18,10 +18,10 @@
-- @alias druid.lang_text
--- On change text callback
-- @tfield DruidEvent on_change @{DruidEvent}
-- @tfield DruidEvent on_change DruidEvent
--- The text component
-- @tfield Text text @{Text}
-- @tfield Text text Text
--- Text node
-- @tfield node node
@@ -32,15 +32,21 @@ local Event = require("druid.event")
local settings = require("druid.system.settings")
local component = require("druid.component")
local LangText = component.create("lang_text")
---@class druid.lang_text: druid.base_component
---@field text druid.text
---@field node node
---@field on_change druid.event
---@field private last_locale_args table
---@field private last_locale string
local M = component.create("lang_text")
--- The @{LangText} constructor
-- @tparam LangText self @{LangText}
--- The LangText constructor
-- @tparam LangText self LangText
-- @tparam string|node node The node_id or gui.get_node(node_id)
-- @tparam string|nil locale_id Default locale id or text from node as default
-- @tparam string|nil adjust_type Adjust type for text. By default is DOWNSCALE. Look const.TEXT_ADJUST for reference
function LangText.init(self, node, locale_id, adjust_type)
function M:init(node, locale_id, adjust_type)
self.druid = self:get_druid()
self.text = self.druid:new_text(node, locale_id, adjust_type)
self.node = self.text.node
@@ -55,7 +61,7 @@ function LangText.init(self, node, locale_id, adjust_type)
end
function LangText.on_language_change(self)
function M:on_language_change()
if self.last_locale then
self:translate(self.last_locale, unpack(self.last_locale_args))
end
@@ -63,10 +69,10 @@ end
--- Setup raw text to lang_text component
-- @tparam LangText self @{LangText}
-- @tparam LangText self LangText
-- @tparam string text Text for text node
-- @treturn LangText Current instance
function LangText.set_to(self, text)
function M:set_to(text)
self.last_locale = false
self.text:set_to(text)
self.on_change:trigger()
@@ -76,7 +82,7 @@ end
--- Translate the text by locale_id
-- @tparam LangText self @{LangText}
-- @tparam LangText self LangText
-- @tparam string locale_id Locale id
-- @tparam string|nil a Optional param to string.format
-- @tparam string|nil b Optional param to string.format
@@ -86,7 +92,7 @@ end
-- @tparam string|nil f Optional param to string.format
-- @tparam string|nil g Optional param to string.format
-- @treturn LangText Current instance
function LangText.translate(self, locale_id, a, b, c, d, e, f, g)
function M:translate(locale_id, a, b, c, d, e, f, g)
self.last_locale_args = { a, b, c, d, e, f, g }
self.last_locale = locale_id or self.last_locale
self.text:set_to(settings.get_text(self.last_locale, a, b, c, d, e, f, g) or "")
@@ -96,7 +102,7 @@ end
--- Format string with new text params on localized text
-- @tparam LangText self @{LangText}
-- @tparam LangText self LangText
-- @tparam string|nil a Optional param to string.format
-- @tparam string|nil b Optional param to string.format
-- @tparam string|nil c Optional param to string.format
@@ -105,11 +111,11 @@ end
-- @tparam string|nil f Optional param to string.format
-- @tparam string|nil g Optional param to string.format
-- @treturn LangText Current instance
function LangText.format(self, a, b, c, d, e, f, g)
function M:format(a, b, c, d, e, f, g)
self.last_locale_args = { a, b, c, d, e, f, g }
self.text:set_to(settings.get_text(self.last_locale, a, b, c, d, e, f, g) or "")
return self
end
return LangText
return M

View File

@@ -1,45 +1,41 @@
-- Copyright (c) 2024 Maksim Tuprikov <insality@gmail.com>. This code is licensed under MIT license
--- Layout management on node
--
-- <a href="https://insality.github.io/druid/druid/index.html?example=general_layout" target="_blank"><b>Example Link</b></a>
-- @module Layout
-- @within BaseComponent
-- @alias druid.layout
--- Layout node
-- @tfield node node
--- Current layout mode
-- @tfield string mode
---
local helper = require("druid.helper")
local component = require("druid.component")
-- @class druid.layout.row_data
-- @tfield width number
-- @tfield height number
-- @tfield count number
---@alias druid.layout.mode
---| "horizontal" Elements are placed horizontally
---| "vertical" Elements are placed vertically
---| "horizontal_wrap" Elements are placed horizontally, but if the row width is greater than the parent width, the next row is created
-- @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.row_data
---@field width number
---@field height number
---@field count number
-- @class druid.layout: druid.base_component
---@class druid.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.layout.row_data[]>
---@class druid.layout: druid.base_component
---@field node node
---@field is_dirty boolean
---@field entities node[]
---@field margin {x: number, y: number}
---@field padding vector4
---@field type string
---@field is_resize_width boolean
---@field is_resize_height boolean
---@field is_justify boolean
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(self, node, layout_type)
self.node = self:get_node(node)
---Layout component constructor
---@local
---@param node_or_node_id node|string
---@param layout_type druid.layout.mode
function M:init(node_or_node_id, layout_type)
self.node = self:get_node(node_or_node_id)
self.is_dirty = true
self.entities = {}
@@ -51,6 +47,8 @@ function M.init(self, node, layout_type)
self.is_justify = false
end
---@local
function M:update()
if not self.is_dirty then
return
@@ -60,11 +58,10 @@ function M:update()
end
-- @tparam Layout self @{Layout}
-- @tparam number|nil margin_x
-- @tparam number|nil margin_y
-- @treturn druid.layout @{Layout}
function M.set_margin(self, margin_x, margin_y)
---@param margin_x number|nil
---@param margin_y number|nil
---@return druid.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
@@ -73,10 +70,9 @@ function M.set_margin(self, margin_x, margin_y)
end
-- @tparam Layout self @{Layout}
-- @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)
---@param padding vector4 The vector4 with padding values, where x - left, y - top, z - right, w - bottom
---@return druid.layout
function M:set_padding(padding)
self.padding = padding
self.is_dirty = true
@@ -84,19 +80,17 @@ function M.set_padding(self, padding)
end
-- @tparam Layout self @{Layout}
-- @treturn druid.layout @{Layout}
function M.set_dirty(self)
---@return druid.layout
function M:set_dirty()
self.is_dirty = true
return self
end
-- @tparam Layout self @{Layout}
-- @tparam boolean is_justify
-- @treturn druid.layout @{Layout}
function M.set_justify(self, is_justify)
---@param is_justify boolean
---@return druid.layout
function M:set_justify(is_justify)
self.is_justify = is_justify
self.is_dirty = true
@@ -104,10 +98,9 @@ function M.set_justify(self, is_justify)
end
-- @tparam Layout self @{Layout}
-- @tparam string type The layout type: "horizontal", "vertical", "horizontal_wrap"
-- @treturn druid.layout @{Layout}
function M.set_type(self, type)
---@param type string The layout type: "horizontal", "vertical", "horizontal_wrap"
---@return druid.layout
function M:set_type(type)
self.type = type
self.is_dirty = true
@@ -115,11 +108,10 @@ function M.set_type(self, type)
end
-- @tparam Layout self @{Layout}
-- @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)
---@param is_hug_width boolean
---@param is_hug_height boolean
---@return druid.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
@@ -128,21 +120,20 @@ function M.set_hug_content(self, is_hug_width, is_hug_height)
end
-- @tparam Layout self @{Layout}
-- @tparam string|node node_or_node_id
-- @treturn druid.layout @{Layout}
function M.add(self, node_or_node_id)
---@param node_or_node_id node|string node_or_node_id
---@return druid.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
---@cast node_or_node_id string|node
node = self:get_node(node_or_node_id)
end
-- @cast node node
---@cast node node
table.insert(self.entities, node)
gui.set_parent(node, self.node)
@@ -152,9 +143,8 @@ function M.add(self, node_or_node_id)
end
-- @tparam Layout self @{Layout}
-- @treturn druid.layout @{Layout}
function M.refresh_layout(self)
---@return druid.layout
function M:refresh_layout()
local layout_node = self.node
local entities = self.entities
@@ -289,9 +279,8 @@ function M.refresh_layout(self)
end
-- @tparam Layout self @{Layout}
-- @treturn druid.layout @{Layout}
function M.clear_layout(self)
---@return druid.layout
function M:clear_layout()
for index = #self.entities, 1, -1 do
self.entities[index] = nil
end
@@ -302,10 +291,9 @@ function M.clear_layout(self)
end
-- @tparam node node
-- @treturn number, number
-- @local
function M.get_node_size(node)
---@param node node
---@return number, number
function M:get_node_size(node)
if not gui.is_enabled(node, false) then
return 0, 0
end
@@ -323,11 +311,10 @@ function M.get_node_size(node)
end
-- @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
-- @local
function M.calculate_rows_data(self)
---Calculate rows data for layout. Contains total width, height and rows info (width, height, count of elements in row)
---@local
---@return druid.layout.rows_data
function M:calculate_rows_data()
local entities = self.entities
local margin = self.margin
local type = self.type
@@ -353,7 +340,7 @@ function M.calculate_rows_data(self)
-- 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)
node_width, node_height = self:get_node_size(node)
rows_data.nodes_width[node] = node_width
rows_data.nodes_height[node] = node_height
end
@@ -407,11 +394,10 @@ function M.calculate_rows_data(self)
end
-- @tparam node node
-- @tparam number x
-- @tparam number y
-- @treturn node
-- @local
---@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

View File

@@ -20,7 +20,7 @@
-- @alias druid.progress
--- On progress bar change callback(self, new_value)
-- @tfield DruidEvent on_change @{DruidEvent}
-- @tfield DruidEvent on_change DruidEvent
--- Progress bar fill node
-- @tfield node node
@@ -49,7 +49,13 @@ local const = require("druid.const")
local helper = require("druid.helper")
local component = require("druid.component")
local Progress = component.create("progress")
---@class druid.progress: druid.base_component
---@field node node
---@field on_change druid.event
---@field style table
---@field key string
---@field prop hash
local M = component.create("progress")
local function check_steps(self, from, to, exactly)
@@ -117,19 +123,19 @@ end
-- @table style
-- @tfield number|nil SPEED Progress bas fill rate. More -> faster. Default: 5
-- @tfield number|nil MIN_DELTA Minimum step to fill progress bar. Default: 0.005
function Progress.on_style_change(self, style)
function M:on_style_change(style)
self.style = {}
self.style.SPEED = style.SPEED or 5
self.style.MIN_DELTA = style.MIN_DELTA or 0.005
end
--- The @{Progress} constructor
-- @tparam Progress self @{Progress}
--- The Progress constructor
-- @tparam Progress self Progress
-- @tparam string|node node Node name or GUI Node itself.
-- @tparam string key Progress bar direction: const.SIDE.X or const.SIDE.Y
-- @tparam number|nil init_value Initial value of progress bar. Default: 1
function Progress.init(self, node, key, init_value)
function M:init(node, key, init_value)
assert(key == const.SIDE.X or const.SIDE.Y, "Progress bar key should be 'x' or 'y'")
self.key = key
@@ -155,18 +161,18 @@ function Progress.init(self, node, key, init_value)
end
function Progress.on_layout_change(self)
function M:on_layout_change()
self:set_to(self.last_value)
end
function Progress.on_remove(self)
function M:on_remove()
-- Return default size
gui.set_size(self.node, self.max_size)
end
function Progress.update(self, dt)
function M:update(dt)
if self.target then
local prev_value = self.last_value
local step = math.abs(self.last_value - self.target) * (self.style.SPEED*dt)
@@ -187,51 +193,51 @@ end
--- Fill a progress bar and stop progress animation
-- @tparam Progress self @{Progress}
function Progress.fill(self)
-- @tparam Progress self Progress
function M:fill()
set_bar_to(self, 1, true)
end
--- Empty a progress bar
-- @tparam Progress self @{Progress}
function Progress.empty(self)
-- @tparam Progress self Progress
function M:empty()
set_bar_to(self, 0, true)
end
--- Instant fill progress bar to value
-- @tparam Progress self @{Progress}
-- @tparam Progress self Progress
-- @tparam number to Progress bar value, from 0 to 1
function Progress.set_to(self, to)
function M:set_to(to)
to = helper.clamp(to, 0, 1)
set_bar_to(self, to)
end
--- Return current progress bar value
-- @tparam Progress self @{Progress}
function Progress.get(self)
-- @tparam Progress self Progress
function M:get()
return self.last_value
end
--- Set points on progress bar to fire the callback
-- @tparam Progress self @{Progress}
-- @tparam Progress self Progress
-- @tparam number[] steps Array of progress bar values
-- @tparam function callback Callback on intersect step value
-- @usage progress:set_steps({0, 0.3, 0.6, 1}, function(self, step) end)
function Progress.set_steps(self, steps, callback)
function M:set_steps(steps, callback)
self.steps = steps
self.step_callback = callback
end
--- Start animation of a progress bar
-- @tparam Progress self @{Progress}
-- @tparam Progress self Progress
-- @tparam number to value between 0..1
-- @tparam function|nil callback Callback on animation ends
function Progress.to(self, to, callback)
function M:to(to, callback)
to = helper.clamp(to, 0, 1)
-- cause of float error
local value = helper.round(to, 5)
@@ -247,14 +253,14 @@ end
--- Set progress bar max node size
-- @tparam Progress self @{Progress}
-- @tparam Progress self Progress
-- @tparam vector3 max_size The new node maximum (full) size
-- @treturn Progress @{Progress}
function Progress.set_max_size(self, max_size)
-- @treturn Progress Progress
function M:set_max_size(max_size)
self.max_size[self.key] = max_size[self.key]
self:set_to(self.last_value)
return self
end
return Progress
return M

View File

@@ -8,7 +8,7 @@
-- @alias druid.slider
--- On change value callback(self, value)
-- @tfield DruidEvent on_change_value @{DruidEvent}
-- @tfield DruidEvent on_change_value DruidEvent
--- Slider pin node
-- @tfield node node
@@ -42,7 +42,19 @@ local helper = require("druid.helper")
local const = require("druid.const")
local component = require("druid.component")
local Slider = component.create("slider", const.PRIORITY_INPUT_HIGH)
---@class druid.slider: druid.base_component
---@field node node
---@field on_change_value druid.event
---@field style table
---@field private start_pos vector3
---@field private pos vector3
---@field private target_pos vector3
---@field private end_pos vector3
---@field private dist vector3
---@field private is_drag boolean
---@field private value number
---@field private steps number[]
local M = component.create("slider", const.PRIORITY_INPUT_HIGH)
local function on_change_value(self)
@@ -56,12 +68,12 @@ local function set_position(self, value)
end
--- The @{Slider} constructor
-- @tparam Slider self @{Slider}
--- The Slider constructor
-- @tparam Slider self Slider
-- @tparam node node Gui pin node
-- @tparam vector3 end_pos The end position of slider
-- @tparam function|nil callback On slider change callback
function Slider.init(self, node, end_pos, callback)
function M:init(node, end_pos, callback)
self.node = self:get_node(node)
self.start_pos = gui.get_position(self.node)
@@ -81,18 +93,18 @@ function Slider.init(self, node, end_pos, callback)
end
function Slider.on_layout_change(self)
function M:on_layout_change()
self:set(self.value)
end
function Slider.on_remove(self)
function M:on_remove()
-- Return pin to start position
gui.set_position(self.node, self.start_pos)
end
function Slider.on_window_resized(self)
function M:on_window_resized()
local x_koef, y_koef = helper.get_screen_aspect_koef()
self._x_koef = x_koef
self._y_koef = y_koef
@@ -100,7 +112,7 @@ function Slider.on_window_resized(self)
end
function Slider.on_input(self, action_id, action)
function M:on_input(action_id, action)
if action_id ~= const.ACTION_TOUCH then
return false
end
@@ -185,10 +197,10 @@ end
--- Set value for slider
-- @tparam Slider self @{Slider}
-- @tparam Slider self Slider
-- @tparam number value Value from 0 to 1
-- @tparam boolean|nil is_silent Don't trigger event if true
function Slider.set(self, value, is_silent)
function M:set(value, is_silent)
value = helper.clamp(value, 0, 1)
set_position(self, value)
self.value = value
@@ -200,11 +212,11 @@ end
--- Set slider steps. Pin node will
-- apply closest step position
-- @tparam Slider self @{Slider}
-- @tparam Slider self Slider
-- @tparam number[] steps Array of steps
-- @usage slider:set_steps({0, 0.2, 0.6, 1})
-- @treturn Slider @{Slider}
function Slider.set_steps(self, steps)
-- @treturn Slider Slider
function M:set_steps(steps)
self.steps = steps
return self
end
@@ -214,29 +226,29 @@ end
-- User can touch any place of node, pin instantly will
-- move at this position and node drag will start.
-- This function require the Defold version 1.3.0+
-- @tparam Slider self @{Slider}
-- @tparam Slider self Slider
-- @tparam node|string|nil input_node
-- @treturn Slider @{Slider}
function Slider.set_input_node(self, input_node)
-- @treturn Slider Slider
function M:set_input_node(input_node)
self._input_node = self:get_node(input_node)
return self
end
--- Set Slider input enabled or disabled
-- @tparam Slider self @{Slider}
-- @tparam Slider self Slider
-- @tparam boolean is_enabled
function Slider.set_enabled(self, is_enabled)
function M:set_enabled(is_enabled)
self._is_enabled = is_enabled
end
--- Check if Slider component is enabled
-- @tparam Slider self @{Slider}
-- @tparam Slider self Slider
-- @treturn boolean
function Slider.is_enabled(self)
function M:is_enabled()
return self._is_enabled
end
return Slider
return M

View File

@@ -16,7 +16,7 @@
-- @tparam node|nil click_zone
--- Trigger on swipe event(self, swipe_side, dist, delta_time)
-- @tfield DruidEvent on_swipe) @{DruidEvent}
-- @tfield DruidEvent on_swipe) DruidEvent
---
@@ -25,7 +25,17 @@ local const = require("druid.const")
local helper = require("druid.helper")
local component = require("druid.component")
local Swipe = component.create("swipe")
---@class druid.swipe: druid.base_component
---@field node node
---@field on_swipe druid.event
---@field style table
---@field click_zone node
---@field private _trigger_on_move boolean
---@field private _swipe_start_time number
---@field private _start_pos vector3
---@field private _is_enabled boolean
---@field private _is_mobile boolean
local M = component.create("swipe")
local function start_swipe(self, action)
@@ -36,7 +46,7 @@ end
local function reset_swipe(self, action)
self._swipe_start_time = false
self._swipe_start_time = 0
end
@@ -77,7 +87,7 @@ end
-- @tfield number|nil SWIPE_TIME Maximum time for swipe trigger. Default: 0.4
-- @tfield number|nil SWIPE_THRESHOLD Minimum distance for swipe trigger. Default: 50
-- @tfield boolean|nil SWIPE_TRIGGER_ON_MOVE If true, trigger on swipe moving, not only release action. Default: false
function Swipe.on_style_change(self, style)
function M:on_style_change(style)
self.style = {}
self.style.SWIPE_TIME = style.SWIPE_TIME or 0.4
self.style.SWIPE_THRESHOLD = style.SWIPE_THRESHOLD or 50
@@ -85,15 +95,19 @@ function Swipe.on_style_change(self, style)
end
--- The @{Swipe} constructor
-- @tparam Swipe self @{Swipe}
--- The Swipe constructor
-- @tparam Swipe self Swipe
-- @tparam node node Gui node
-- @tparam function on_swipe_callback Swipe callback for on_swipe_end event
function Swipe.init(self, node, on_swipe_callback)
self._trigger_on_move = self.style.SWIPE_TRIGGER_ON_MOVE
self.node = self:get_node(node)
self._swipe_start_time = false
---Swipe constructor
---@param node_or_node_id node|string
---@param on_swipe_callback function
function M:init(node_or_node_id, on_swipe_callback)
self._trigger_on_move = self.style.SWIPE_TRIGGER_ON_MOVE
self.node = self:get_node(node_or_node_id)
self._swipe_start_time = 0
self._start_pos = vmath.vector3(0)
self.click_zone = nil
@@ -101,7 +115,7 @@ function Swipe.init(self, node, on_swipe_callback)
end
function Swipe.on_late_init(self)
function M:on_late_init()
if not self.click_zone and const.IS_STENCIL_CHECK then
local stencil_node = helper.get_closest_stencil_node(self.node)
if stencil_node then
@@ -111,7 +125,7 @@ function Swipe.on_late_init(self)
end
function Swipe.on_input(self, action_id, action)
function M:on_input(action_id, action)
if action_id ~= const.ACTION_TOUCH then
return false
end
@@ -126,7 +140,7 @@ function Swipe.on_input(self, action_id, action)
return false
end
if self._swipe_start_time and (self._trigger_on_move or action.released) then
if self._swipe_start_time ~= 0 and (self._trigger_on_move or action.released) then
check_swipe(self, action)
end
@@ -142,18 +156,18 @@ function Swipe.on_input(self, action_id, action)
end
function Swipe.on_input_interrupt(self)
function M:on_input_interrupt()
reset_swipe(self)
end
--- Strict swipe click area. Useful for
-- restrict events outside stencil node
-- @tparam Swipe self @{Swipe}
-- @tparam Swipe self Swipe
-- @tparam node|string|nil zone Gui node
function Swipe.set_click_zone(self, zone)
function M:set_click_zone(zone)
self.click_zone = self:get_node(zone)
end
return Swipe
return M

View File

@@ -1,40 +1,18 @@
-- Copyright (c) 2021 Maksim Tuprikov <insality@gmail.com>. This code is licensed under MIT license
--- Component to handle GUI timers.
-- Timer updating by game delta time. If game is not focused -
-- timer will be not updated.
-- @module Timer
-- @within BaseComponent
-- @alias druid.timer
--- On timer tick. Fire every second callback(self, value)
-- @tfield DruidEvent on_tick @{DruidEvent}
--- On timer change enabled state callback(self, is_enabled)
-- @tfield DruidEvent on_set_enabled @{DruidEvent}
--- On timer end callback
-- @tfield DruidEvent on_timer_end(self, Timer) @{DruidEvent}
--- Trigger node
-- @tfield node node
--- Initial timer value
-- @tfield number from
--- Target timer value
-- @tfield number target
--- Current timer value
-- @tfield number value
---
local Event = require("druid.event")
local helper = require("druid.helper")
local component = require("druid.component")
local Timer = component.create("timer")
---@class druid.timer: druid.base_component
---@field on_tick druid.event
---@field on_set_enabled druid.event
---@field on_timer_end druid.event
---@field style table
---@field node node
---@field from number
---@field target number
---@field value number
---@field is_on boolean|nil
local M = component.create("timer")
local function second_string_min(sec)
@@ -44,13 +22,12 @@ local function second_string_min(sec)
end
--- The @{Timer} constructor
-- @tparam Timer self @{Timer}
-- @tparam node node Gui text node
-- @tparam number|nil seconds_from Start timer value in seconds
-- @tparam number|nil seconds_to End timer value in seconds
-- @tparam function|nil callback Function on timer end
function Timer.init(self, node, seconds_from, seconds_to, callback)
---The Timer constructor
---@param node node Gui text node
---@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 M:init(node, seconds_from, seconds_to, callback)
self.node = self:get_node(node)
seconds_to = math.max(seconds_to or 0, 0)
@@ -73,7 +50,7 @@ function Timer.init(self, node, seconds_from, seconds_to, callback)
end
function Timer.update(self, dt)
function M:update(dt)
if not self.is_on then
return
end
@@ -96,42 +73,47 @@ function Timer.update(self, dt)
end
function Timer.on_layout_change(self)
function M:on_layout_change()
self:set_to(self.last_value)
end
--- Set text to text field
-- @tparam Timer self @{Timer}
-- @tparam number set_to Value in seconds
function Timer.set_to(self, set_to)
---@param self druid.timer
---@param set_to number Value in seconds
---@return druid.timer self
function M:set_to(set_to)
self.last_value = set_to
gui.set_text(self.node, second_string_min(set_to))
return self
end
--- Called when update
-- @tparam Timer self @{Timer}
-- @tparam boolean|nil is_on Timer enable state
function Timer.set_state(self, is_on)
---@param self druid.timer
---@param is_on boolean|nil Timer enable state
---@return druid.timer self
function M:set_state(is_on)
self.is_on = is_on
self.on_set_enabled:trigger(self:get_context(), is_on)
return self
end
--- Set time interval
-- @tparam Timer self @{Timer}
-- @tparam number from Start time in seconds
-- @tparam number to Target time in seconds
function Timer.set_interval(self, from, to)
---@param self druid.timer
---@param from number Start time in seconds
---@param to number Target time in seconds
---@return druid.timer self
function M:set_interval(from, to)
self.from = from
self.value = from
self.temp = 0
self.target = to
self:set_state(true)
self:set_to(from)
return self
end
return Timer
return M

View File

@@ -1,15 +1,13 @@
-- Copyright (c) 2021 Maksim Tuprikov <insality@gmail.com>. This code is licensed under MIT license
--- Helper module with various usefull GUI functions.
-- @usage
-- local helper = require("druid.helper")
-- helper.centrate_nodes(0, node_1, node_2)
-- @module Helper
-- @alias druid.helper
--- Druid Helper module
local const = require("druid.const")
local gui_get_node = gui.get_node
---@class druid.system.helper
local M = {}
local POSITION_X = hash("position.x")
local SCALE_X = hash("scale.x")
local SIZE_X = hash("size.x")
@@ -46,39 +44,35 @@ local function get_width(node)
end
--- Center two nodes.
-- Nodes will be center around 0 x position
-- text_node will be first (at left side)
-- @function helper.centrate_text_with_icon
-- @tparam text|nil text_node Gui text node
-- @tparam box|nil icon_node Gui box node
-- @tparam number margin Offset between nodes
-- @local
---Center two nodes.
--Nodes will be center around 0 x position
--text_node will be first (at left side)
---@param text_node node|nil Gui text node
---@param icon_node node|nil Gui box node
---@param margin number Offset between nodes
---@local
function M.centrate_text_with_icon(text_node, icon_node, margin)
return M.centrate_nodes(margin, text_node, icon_node)
end
--- Center two nodes.
-- Nodes will be center around 0 x position
-- icon_node will be first (at left side)
-- @function helper.centrate_icon_with_text
-- @tparam box|nil icon_node Gui box node
-- @tparam text|nil text_node Gui text node
-- @tparam number|nil margin Offset between nodes
-- @local
---Center two nodes.
--Nodes will be center around 0 x position
--icon_node will be first (at left side)
---@param icon_node node|nil Gui box node
---@param text_node node|nil Gui text node
---@param margin number|nil Offset between nodes
---@local
function M.centrate_icon_with_text(icon_node, text_node, margin)
return M.centrate_nodes(margin, icon_node, text_node)
end
--- Centerate nodes by x position with margin.
--
-- This functions calculate total width of nodes and set position for each node.
-- The centrate will be around 0 x position.
-- @function helper.centrate_nodes
-- @tparam number|nil margin Offset between nodes
-- @param ... Gui nodes
---Centerate nodes by x position with margin.
---
---This functions calculate total width of nodes and set position for each node.
---The centrate will be around 0 x position.
---@param margin number|nil Offset between nodes
---@param ... node Nodes to centrate
function M.centrate_nodes(margin, ...)
margin = margin or 0
@@ -113,10 +107,33 @@ function M.centrate_nodes(margin, ...)
end
--- Get current screen stretch multiplier for each side
-- @function helper.get_screen_aspect_koef
-- @treturn number stretch_x
-- @treturn number stretch_y
---@param node_id string|node
---@param template string|nil @Full Path to the template
---@param nodes table<hash, node>|nil @Nodes what created with gui.clone_tree
---@return node
function M.get_node(node_id, template, nodes)
if type(node_id) ~= "string" then
-- Assume it's already node from gui.get_node
return node_id
end
-- If template is set, then add it to the node_id
if template and #template > 0 then
node_id = template .. "/" .. node_id
end
-- If nodes is set, then try to find node in it
if nodes then
return nodes[node_id]
end
return gui_get_node(node_id)
end
---Get current screen stretch multiplier for each side
---@return number stretch_x
---@return number stretch_y
function M.get_screen_aspect_koef()
local window_x, window_y = window.get_size()
local stretch_x = window_x / gui.get_width()
@@ -126,10 +143,8 @@ function M.get_screen_aspect_koef()
end
--- Get current GUI scale for each side
-- @function helper.get_gui_scale
-- @treturn number scale_x
-- @treturn number scale_y
---Get current GUI scale for each side
---@return number scale_x
function M.get_gui_scale()
local window_x, window_y = window.get_size()
return math.min(window_x / gui.get_width(),
@@ -137,12 +152,11 @@ function M.get_gui_scale()
end
--- Move value from current to target value with step amount
-- @function helper.step
-- @tparam number current Current value
-- @tparam number target Target value
-- @tparam number step Step amount
-- @treturn number New value
---Move value from current to target value with step amount
---@param current number Current value
---@param target number Target value
---@param step number Step amount
---@return number New value
function M.step(current, target, step)
if current < target then
return math.min(current + step, target)
@@ -152,12 +166,11 @@ function M.step(current, target, step)
end
--- Clamp value between min and max
-- @function helper.clamp
-- @tparam number a Value
-- @tparam number min Min value
-- @tparam number max Max value
-- @treturn number Clamped value
---Clamp value between min and max
---@param a number Value
---@param min number Min value
---@param max number Max value
---@return number value Clamped value
function M.clamp(a, min, max)
if min > max then
min, max = max, min
@@ -173,22 +186,20 @@ function M.clamp(a, min, max)
end
--- Calculate distance between two points
-- @function helper.distance
-- @tparam number x1 First point x
-- @tparam number y1 First point y
-- @tparam number x2 Second point x
-- @tparam number y2 Second point y
-- @treturn number Distance
---Calculate distance between two points
---@param x1 number First point x
---@param y1 number First point y
---@param x2 number Second point x
---@param y2 number Second point y
---@return number Distance
function M.distance(x1, y1, x2, y2)
return math.sqrt((x2 - x1) ^ 2 + (y2 - y1) ^ 2)
end
--- Return sign of value (-1, 0, 1)
-- @function helper.sign
-- @tparam number val Value
-- @treturn number Sign
---Return sign of value
---@param val number Value
---@return number sign Sign of value, -1, 0 or 1
function M.sign(val)
if val == 0 then
return 0
@@ -198,47 +209,42 @@ function M.sign(val)
end
--- Round number to specified decimal places
-- @function helper.round
-- @tparam number num Number
-- @tparam number|nil num_decimal_places Decimal places
-- @treturn number Rounded number
---Round number to specified decimal places
---@param num number Number
---@param num_decimal_places number|nil Decimal places
---@return number value Rounded number
function M.round(num, num_decimal_places)
local mult = 10^(num_decimal_places or 0)
return math.floor(num * mult + 0.5) / mult
end
--- Lerp between two values
-- @function helper.lerp
-- @tparam number a First value
-- @tparam number b Second value
-- @tparam number t Lerp amount
-- @treturn number Lerped value
---Lerp between two values
---@param a number First value
---@param b number Second value
---@param t number Lerp amount
---@return number value Lerped value
function M.lerp(a, b, t)
return a + (b - a) * t
end
--- Check if value is in array and return index of it
-- @function helper.contains
-- @tparam table t Array
-- @param value Value
-- @treturn number|nil Index of value or nil
function M.contains(t, value)
for i = 1, #t do
if t[i] == value then
return i
---Check if value contains in array
---@param array any[] Array to check
---@param value any Value
function M.contains(array, value)
for index = 1, #array do
if array[index] == value then
return index
end
end
return nil
end
--- Make a copy table with all nested tables
-- @function helper.deepcopy
-- @tparam table orig_table Original table
-- @treturn table Copy of original table
---Make a copy table with all nested tables
---@param orig_table table Original table
---@return table Copy of original table
function M.deepcopy(orig_table)
local orig_type = type(orig_table)
local copy
@@ -254,11 +260,10 @@ function M.deepcopy(orig_table)
end
--- Add all elements from source array to the target array
-- @function helper.add_array
-- @tparam any[] target Array to put elements from source
-- @tparam any[]|nil source The source array to get elements from
-- @treturn any[] The target array
---Add all elements from source array to the target array
---@param target any[] Array to put elements from source
---@param source any[]|nil The source array to get elements from
---@return any[] The target array
function M.add_array(target, source)
assert(target)
@@ -274,13 +279,12 @@ function M.add_array(target, source)
end
--- Make a check with gui.pick_node, but with additional node_click_area check.
-- @function helper.pick_node
-- @tparam node node
-- @tparam number x
-- @tparam number y
-- @tparam node|nil node_click_area
-- @local
---Make a check with gui.pick_node, but with additional node_click_area check.
---@param node node
---@param x number
---@param y number
---@param node_click_area node|nil
---@local
function M.pick_node(node, x, y, node_click_area)
local is_pick = gui.pick_node(node, x, y)
@@ -291,20 +295,19 @@ function M.pick_node(node, x, y, node_click_area)
return is_pick
end
--- Get node size adjusted by scale
-- @function helper.get_scaled_size
-- @tparam node node GUI node
-- @treturn vector3 Scaled size
---Get size of node with scale multiplier
---@param node node GUI node
---@treturn vector3 Scaled size
function M.get_scaled_size(node)
return vmath.mul_per_elem(gui.get_size(node), gui.get_scale(node))
end
--- Get cumulative parent's node scale
-- @function helper.get_scene_scale
-- @tparam node node Gui node
-- @tparam boolean|nil include_passed_node_scale True if add current node scale to result
-- @treturn vector3 The scene node scale
---Get cumulative parent's node scale
---@param node node Gui node
---@param include_passed_node_scale boolean|nil True if add current node scale to result
---@return vector3 The scene node scale
function M.get_scene_scale(node, include_passed_node_scale)
local scale = include_passed_node_scale and gui.get_scale(node) or vmath.vector3(1)
local parent = gui.get_parent(node)
@@ -317,10 +320,9 @@ function M.get_scene_scale(node, include_passed_node_scale)
end
--- Return closest non inverted clipping parent node for given node
-- @function helper.get_closest_stencil_node
-- @tparam node node GUI node
-- @treturn node|nil The closest stencil node or nil
---Return closest non inverted clipping parent node for given node
---@param node node GUI node
---@return node|nil stencil_node The closest stencil node or nil
function M.get_closest_stencil_node(node)
if not node then
return nil
@@ -348,8 +350,15 @@ end
-- @function helper.get_pivot_offset
-- @tparam number pivot The gui.PIVOT_* constant
-- @treturn vector3 Vector offset with [-0.5..0.5] values
function M.get_pivot_offset(pivot)
return const.PIVOTS[pivot]
---Get pivot offset for given pivot or node
---@param pivot_or_node number|node GUI pivot or node
---@return vector3 offset The pivot offset
function M.get_pivot_offset(pivot_or_node)
if type(pivot_or_node) == "number" then
return const.PIVOTS[pivot_or_node]
end
return const.PIVOTS[gui.get_pivot(pivot_or_node)]
end

View File

@@ -1,5 +1,3 @@
-- Copyright (c) 2021 Maksim Tuprikov <insality@gmail.com>. This code is licensed under MIT license
local const = require("druid.const")
local settings = require("druid.system.settings")

View File

@@ -10,11 +10,11 @@
--
-- Please review the following API pages:
--
-- @{Helper} - A useful set of functions for working with GUI nodes, such as centering nodes, get GUI scale ratio, etc
-- Helper - A useful set of functions for working with GUI nodes, such as centering nodes, get GUI scale ratio, etc
--
-- @{DruidEvent} - The core event system in Druid. Learn how to subscribe to any event in every Druid component.
-- DruidEvent - The core event system in Druid. Learn how to subscribe to any event in every Druid component.
--
-- @{BaseComponent} - The parent class of all Druid components. You can find all default component methods there.
-- BaseComponent - The parent class of all Druid components. You can find all default component methods there.
--
-- # Tech Info #
--
@@ -89,7 +89,21 @@ local back_handler = require("druid.base.back_handler")
-- local lang_text = require("druid.extended.lang_text")
-- local timer_component = require("druid.extended.timer")
local DruidInstance = {}
---@class druid_instance
---@field components_all druid.base_component[] All created components
---@field components_interest table<string, druid.base_component[]> All components sorted by interest
---@field url url
---@field private _context table Druid context
---@field private _style table Druid style table
---@field private _deleted boolean
---@field private _is_late_remove_enabled boolean
---@field private _late_remove druid.base_component[]
---@field private _input_blacklist druid.base_component[]|nil
---@field private _input_whitelist druid.base_component[]|nil
---@field private _input_inited boolean
---@field private _late_init_timer_id number
---@field private _input_components druid.base_component[]
local M = {}
local MSG_ADD_FOCUS = hash("acquire_input_focus")
local MSG_REMOVE_FOCUS = hash("release_input_focus")
@@ -219,17 +233,15 @@ end
--- Druid class constructor
-- @tparam DruidInstance self
-- @tparam table context Druid context. Usually it is self of gui script
-- @tparam table style Druid style table
-- @local
function DruidInstance.initialize(self, context, style)
function M:initialize(context, style)
self._context = context
self._style = style or settings.default_style
self._deleted = false
self._is_late_remove_enabled = false
self._late_remove = {}
self._is_debug = false
self.url = msg.url()
self._input_blacklist = nil
@@ -244,11 +256,10 @@ end
-- Create new component.
-- @tparam DruidInstance self
-- @tparam BaseComponent component Component module
-- @tparam any ... Other component params to pass it to component:init function
-- @treturn BaseComponent Component instance
function DruidInstance.new(self, component, ...)
function M:new(component, ...)
local instance = create(self, component)
if instance.init then
@@ -263,8 +274,7 @@ end
--- Call this in gui_script final function.
-- @tparam DruidInstance self
function DruidInstance.final(self)
function M:final()
local components = self.components_all
for i = #components, 1, -1 do
@@ -282,10 +292,9 @@ end
--- Remove created component from Druid instance.
--
-- Component `on_remove` function will be invoked, if exist.
-- @tparam DruidInstance self
-- @tparam BaseComponent component Component instance
-- @treturn boolean True if component was removed
function DruidInstance.remove(self, component)
function M:remove(component)
if self._is_late_remove_enabled then
table.insert(self._late_remove, component)
return false
@@ -334,9 +343,8 @@ end
--- Druid late update function called after initialization and before the regular update step
-- This function is used to check the GUI state and perform actions after all components and nodes have been created.
-- An example use case is performing an auto stencil check in the GUI hierarchy for input components.
-- @tparam DruidInstance self
-- @local
function DruidInstance.late_init(self)
function M:late_init()
local late_init_components = self.components_interest[base_component.ON_LATE_INIT]
while late_init_components[1] do
late_init_components[1]:on_late_init()
@@ -353,9 +361,8 @@ end
--- Call this in gui_script update function.
--
-- Used for: scroll, progress, timer components
-- @tparam DruidInstance self
-- @tparam number dt Delta time
function DruidInstance.update(self, dt)
function M:update(dt)
self._is_late_remove_enabled = true
local components = self.components_interest[base_component.ON_UPDATE]
@@ -371,11 +378,10 @@ end
--- Call this in gui_script on_input function.
--
-- Used for almost all components
-- @tparam DruidInstance self
-- @tparam hash action_id Action_id from on_input
-- @tparam table action Action from on_input
-- @treturn boolean The boolean value is input was consumed
function DruidInstance.on_input(self, action_id, action)
function M:on_input(action_id, action)
self._is_late_remove_enabled = true
local components = self.components_interest[base_component.ON_INPUT]
@@ -392,11 +398,10 @@ end
--- Call this in gui_script on_message function.
--
-- Used for special actions. See SPECIFIC_UI_MESSAGES table
-- @tparam DruidInstance self
-- @tparam hash message_id Message_id from on_message
-- @tparam table message Message from on_message
-- @tparam url sender Sender from on_message
function DruidInstance.on_message(self, message_id, message, sender)
function M:on_message(message_id, message, sender)
local specific_ui_message = base_component.SPECIFIC_UI_MESSAGES[message_id]
if specific_ui_message == base_component.ON_MESSAGE_INPUT then
@@ -431,9 +436,8 @@ end
--- Calls the on_focus_lost function in all related components
-- This one called by on_window_callback by global window listener
-- @tparam DruidInstance self
-- @local
function DruidInstance.on_focus_lost(self)
function M:on_focus_lost()
local components = self.components_interest[base_component.ON_FOCUS_LOST]
for i = 1, #components do
components[i]:on_focus_lost()
@@ -443,9 +447,8 @@ end
--- Calls the on_focus_gained function in all related components
-- This one called by on_window_callback by global window listener
-- @tparam DruidInstance self
-- @local
function DruidInstance.on_focus_gained(self)
function M:on_focus_gained()
local components = self.components_interest[base_component.ON_FOCUS_GAINED]
for i = 1, #components do
components[i]:on_focus_gained()
@@ -456,9 +459,8 @@ end
--- Calls the on_language_change function in all related components
-- This one called by global druid.on_language_change, but can be
-- call manualy to update all translations
-- @tparam DruidInstance self
-- @local
function DruidInstance.on_language_change(self)
function M:on_language_change()
local components = self.components_interest[base_component.ON_LANGUAGE_CHANGE]
for i = 1, #components do
components[i]:on_language_change()
@@ -470,10 +472,9 @@ end
--
-- If whitelist is not empty and component not contains in this list,
-- component will be not processed on input step
-- @tparam DruidInstance self
-- @tparam table|BaseComponent|nil whitelist_components The array of component to whitelist
-- @treturn self @{DruidInstance}
function DruidInstance.set_whitelist(self, whitelist_components)
-- @treturn self DruidInstance
function M:set_whitelist(whitelist_components)
if whitelist_components and whitelist_components._component then
whitelist_components = { whitelist_components }
end
@@ -491,11 +492,10 @@ end
--- Set blacklist components for input processing.
--
-- If blacklist is not empty and component contains in this list,
-- component will be not processed on input step
-- @tparam DruidInstance self @{DruidInstance}
-- component will be not processed on input step DruidInstance
-- @tparam table|BaseComponent|nil blacklist_components The array of component to blacklist
-- @treturn self @{DruidInstance}
function DruidInstance.set_blacklist(self, blacklist_components)
-- @treturn self DruidInstance
function M:set_blacklist(blacklist_components)
if blacklist_components and blacklist_components._component then
blacklist_components = { blacklist_components }
end
@@ -510,35 +510,9 @@ function DruidInstance.set_blacklist(self, blacklist_components)
end
--- Set debug mode for current Druid instance. It's enable debug log messages
-- @tparam DruidInstance self @{DruidInstance}
-- @tparam boolean|nil is_debug
-- @treturn self @{DruidInstance}
--- Remove all components on late remove step DruidInstance
-- @local
function DruidInstance.set_debug(self, is_debug)
self._is_debug = is_debug
return self
end
--- Log message, if is_debug mode is enabled
-- @tparam DruidInstance self @{DruidInstance}
-- @tparam string message
-- @tparam table|nil context
-- @local
function DruidInstance.log_message(self, message, context)
if not self._is_debug then
return
end
print("[Druid]:", message, helper.table_to_string(context))
end
--- Remove all components on late remove step
-- @tparam DruidInstance self @{DruidInstance}
-- @local
function DruidInstance._clear_late_remove(self)
function M:_clear_late_remove()
if #self._late_remove == 0 then
return
end
@@ -550,229 +524,188 @@ function DruidInstance._clear_late_remove(self)
end
--- Create @{Button} component
-- @tparam DruidInstance self
-- @tparam string|node node The node_id or gui.get_node(node_id)
-- @tparam function|nil callback Button callback
-- @tparam any|nil params Button callback params
-- @tparam node|string|nil anim_node Button anim node (node, if not provided)
-- @treturn Button @{Button} component
function DruidInstance.new_button(self, node, callback, params, anim_node)
return DruidInstance.new(self, button, node, callback, params, anim_node)
--- Create Button component
---@param node string|node The node_id or gui.get_node(node_id)
---@param callback function|nil Button callback
---@param params any|nil Button callback params
---@param anim_node node|string|nil Button anim node (node, if not provided)
---@return druid.button Button component
function M:new_button(node, callback, params, anim_node)
return self:new(button, node, callback, params, anim_node)
end
--- Create @{Blocker} component
-- @tparam DruidInstance self
-- @tparam string|node node The node_id or gui.get_node(node_id)
-- @treturn Blocker @{Blocker} component
function DruidInstance.new_blocker(self, node)
return DruidInstance.new(self, blocker, node)
--- Create Blocker component
---@param node string|node The node_id or gui.get_node(node_id)
---@return druid.blocker Blocker component
function M:new_blocker(node)
return self:new(blocker, node)
end
--- Create @{BackHandler} component
-- @tparam DruidInstance self
-- @tparam function|nil callback @The callback(self, custom_args) to call on back event
-- @tparam any|nil params Callback argument
-- @treturn BackHandler @{BackHandler} component
function DruidInstance.new_back_handler(self, callback, params)
return DruidInstance.new(self, back_handler, callback, params)
--- Create BackHandler component
---@param callback function|nil The callback(self, custom_args) to call on back event
---@param params any|nil Callback argument
---@return druid.back_handler BackHandler component
function M:new_back_handler(callback, params)
return self:new(back_handler, callback, params)
end
--- Create @{Hover} component
-- @tparam DruidInstance self
-- @tparam string|node node The node_id or gui.get_node(node_id)
-- @tparam function|nil on_hover_callback Hover callback
-- @tparam function|nil on_mouse_hover_callback Mouse hover callback
-- @treturn Hover @{Hover} component
function DruidInstance.new_hover(self, node, on_hover_callback, on_mouse_hover_callback)
return DruidInstance.new(self, hover, node, on_hover_callback, on_mouse_hover_callback)
--- Create Hover component
---@param node string|node The node_id or gui.get_node(node_id)
---@param on_hover_callback function|nil Hover callback
---@param on_mouse_hover_callback function|nil Mouse hover callback
---@return druid.hover Hover component
function M:new_hover(node, on_hover_callback, on_mouse_hover_callback)
return self:new(hover, node, on_hover_callback, on_mouse_hover_callback)
end
--- Create @{Text} component
-- @tparam DruidInstance self
-- @tparam string|node node The node_id or gui.get_node(node_id)
-- @tparam string|nil value Initial text. Default value is node text from GUI scene.
-- @tparam boolean|nil no_adjust If true, text will be not auto-adjust size
-- @treturn Text @{Text} component
function DruidInstance.new_text(self, node, value, no_adjust)
return DruidInstance.new(self, text, node, value, no_adjust)
--- Create Text component
---@param node string|node The node_id or gui.get_node(node_id)
---@param value string|nil Initial text. Default value is node text from GUI scene.
---@param no_adjust boolean|nil If true, text will be not auto-adjust size
---@return druid.text Text component
function M:new_text(node, value, no_adjust)
return self:new(text, node, value, no_adjust)
end
--- Create @{StaticGrid} component
-- @tparam DruidInstance self
-- @tparam string|node parent_node The node_id or gui.get_node(node_id). Parent of all Grid items.
-- @tparam node item Element prefab. Required to get grid's item size. Can be adjusted separately.
-- @tparam number|nil in_row How many nodes in row can be placed
-- @treturn StaticGrid @{StaticGrid} component
-- @local
function DruidInstance.new_grid(self, parent_node, item, in_row)
return DruidInstance.new(self, static_grid, parent_node, item, in_row)
--- Create StaticGrid component
---@param parent_node string|node The node_id or gui.get_node(node_id). Parent of all Grid items.
---@param item node Element prefab. Required to get grid's item size. Can be adjusted separately.
---@param in_row number|nil How many nodes in row can be placed
---@return druid.grid StaticGrid component
function M:new_grid(parent_node, item, in_row)
return self:new(static_grid, parent_node, item, in_row)
end
--- Create @{StaticGrid} component
-- @tparam DruidInstance self
-- @tparam string|node parent_node The node_id or gui.get_node(node_id). Parent of all Grid items.
-- @tparam string|node item Item prefab. Required to get grid's item size. Can be adjusted separately.
-- @tparam number|nil in_row How many nodes in row can be placed
-- @treturn StaticGrid @{StaticGrid} component
function DruidInstance.new_static_grid(self, parent_node, item, in_row)
return DruidInstance.new(self, static_grid, parent_node, item, in_row)
--- Create StaticGrid component
---@param parent_node string|node The node_id or gui.get_node(node_id). Parent of all Grid items.
---@param item string|node Item prefab. Required to get grid's item size. Can be adjusted separately.
---@param in_row number|nil How many nodes in row can be placed
---@return druid.grid StaticGrid component
function M:new_static_grid(parent_node, item, in_row)
return self:new(static_grid, parent_node, item, in_row)
end
--- Create @{Scroll} component
-- @tparam DruidInstance self
-- @tparam string|node view_node The node_id or gui.get_node(node_id). Will used as user input node.
-- @tparam string|node content_node The node_id or gui.get_node(node_id). Will used as scrollable node inside view_node.
-- @treturn Scroll @{Scroll} component
function DruidInstance.new_scroll(self, view_node, content_node)
return DruidInstance.new(self, scroll, view_node, content_node)
--- Create Scroll component
---@param view_node string|node The node_id or gui.get_node(node_id). Will used as user input node.
---@param content_node string|node The node_id or gui.get_node(node_id). Will used as scrollable node inside view_node.
---@return druid.scroll Scroll component
function M:new_scroll(view_node, content_node)
return self:new(scroll, view_node, content_node)
end
--- Create @{Drag} component
-- @tparam DruidInstance self
-- @tparam string|node node The node_id or gui.get_node(node_id). Will used as user input node.
-- @tparam function|nil on_drag_callback Callback for on_drag_event(self, dx, dy)
-- @treturn Drag @{Drag} component
function DruidInstance.new_drag(self, node, on_drag_callback)
return DruidInstance.new(self, drag, node, on_drag_callback)
--- Create Drag component
---@param node string|node The node_id or gui.get_node(node_id). Will used as user input node.
---@param on_drag_callback function|nil Callback for on_drag_event(self, dx, dy)
---@return druid.drag Drag component
function M:new_drag(node, on_drag_callback)
return self:new(drag, node, on_drag_callback)
end
--- Create @{Swipe} component
-- @tparam DruidInstance self
-- @tparam string|node node The node_id or gui.get_node(node_id). Will used as user input node.
-- @tparam function|nil on_swipe_callback Swipe callback for on_swipe_end event
-- @treturn Swipe @{Swipe} component
function DruidInstance.new_swipe(self, node, on_swipe_callback)
return helper.require_component_message("swipe")
--- Create Swipe component
---@param node string|node The node_id or gui.get_node(node_id). Will used as user input node.
---@param on_swipe_callback function|nil Swipe callback for on_swipe_end event
---@return druid.swipe Swipe component
function M:new_swipe(node, on_swipe_callback)
return helper.require_component_message("swipe") --[[@as druid.swipe]]
end
--- Create @{DynamicGrid} component
-- Deprecated
-- @tparam DruidInstance self
-- @tparam string|node parent_node The node_id or gui.get_node(node_id). Parent of all Grid items.
-- @treturn DynamicGrid @{DynamicGrid} component
function DruidInstance.new_dynamic_grid(self, parent_node)
return helper.require_component_message("dynamic_grid")
--- Create LangText component
---@param node string|node The_node id or gui.get_node(node_id)
---@param locale_id string|nil Default locale id or text from node as default
---@param adjust_type string|nil Adjust type for text node. Default: const.TEXT_ADJUST.DOWNSCALE
---@return druid.lang_text LangText component
function M:new_lang_text(node, locale_id, adjust_type)
return helper.require_component_message("lang_text") --[[@as druid.lang_text]]
end
--- Create @{LangText} component
-- @tparam DruidInstance self
-- @tparam string|node node The_node id or gui.get_node(node_id)
-- @tparam string|nil locale_id Default locale id or text from node as default
-- @tparam string|nil adjust_type Adjust type for text node. Default: const.TEXT_ADJUST.DOWNSCALE
-- @treturn LangText @{LangText} component
function DruidInstance.new_lang_text(self, node, locale_id, adjust_type)
return helper.require_component_message("lang_text")
--- Create Slider component
---@param pin_node string|node The_node id or gui.get_node(node_id).
---@param end_pos vector3 The end position of slider
---@param callback function|nil On slider change callback
---@return druid.slider Slider component
function M:new_slider(pin_node, end_pos, callback)
return helper.require_component_message("slider") --[[@as druid.slider]]
end
--- Create @{Slider} component
-- @tparam DruidInstance self
-- @tparam string|node pin_node The_node id or gui.get_node(node_id).
-- @tparam vector3 end_pos The end position of slider
-- @tparam function|nil callback On slider change callback
-- @treturn Slider @{Slider} component
function DruidInstance.new_slider(self, pin_node, end_pos, callback)
return helper.require_component_message("slider")
--- Create Input component
---@param click_node string|node Button node to enabled input component
---@param text_node string|node|druid.text Text node what will be changed on user input
---@param keyboard_type number|nil Gui keyboard type for input field
---@return druid.input Input component
function M:new_input(click_node, text_node, keyboard_type)
return helper.require_component_message("input") --[[@as druid.input]]
end
--- Create @{Input} component
-- @tparam DruidInstance self
-- @tparam string|node click_node Button node to enabled input component
-- @tparam string|node|druid.text text_node Text node what will be changed on user input
-- @tparam number|nil keyboard_type Gui keyboard type for input field
-- @treturn Input @{Input} component
function DruidInstance.new_input(self, click_node, text_node, keyboard_type)
return helper.require_component_message("input")
--- Create DataList component
---@param druid_scroll druid.scroll The Scroll instance for Data List component
---@param druid_grid druid.grid The StaticGrid} or @{DynamicGrid instance for Data List component
---@param create_function function The create function callback(self, data, index, data_list). Function should return (node, [component])
---@return druid.data_list DataList component
function M:new_data_list(druid_scroll, druid_grid, create_function)
return helper.require_component_message("data_list") --[[@as druid.data_list]]
end
--- Create @{DataList} component
-- @tparam DruidInstance self
-- @tparam Scroll druid_scroll The Scroll instance for Data List component
-- @tparam StaticGrid druid_grid The @{StaticGrid} or @{DynamicGrid} instance for Data List component
-- @tparam function create_function The create function callback(self, data, index, data_list). Function should return (node, [component])
-- @treturn DataList @{DataList} component
function DruidInstance.new_data_list(self, druid_scroll, druid_grid, create_function)
return helper.require_component_message("data_list")
--- Create Timer component
---@param node string|node Gui text node
---@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
function M:new_timer(node, seconds_from, seconds_to, callback)
return helper.require_component_message("timer") --[[@as druid.timer]]
end
--- Create @{Timer} component
-- @tparam DruidInstance self
-- @tparam string|node node Gui text node
-- @tparam number seconds_from Start timer value in seconds
-- @tparam number|nil seconds_to End timer value in seconds
-- @tparam function|nil callback Function on timer end
-- @treturn Timer @{Timer} component
function DruidInstance.new_timer(self, node, seconds_from, seconds_to, callback)
return helper.require_component_message("timer")
--- Create Progress component
---@param node string|node Progress bar fill node or node name
---@param key string Progress bar direction: const.SIDE.X or const.SIDE.Y
---@param init_value number|nil Initial value of progress bar. Default: 1
---@return druid.progress Progress component
function M:new_progress(node, key, init_value)
return helper.require_component_message("progress") --[[@as druid.progress]]
end
--- Create @{Progress} component
-- @tparam DruidInstance self
-- @tparam string|node node Progress bar fill node or node name
-- @tparam string key Progress bar direction: const.SIDE.X or const.SIDE.Y
-- @tparam number|nil init_value Initial value of progress bar. Default: 1
-- @treturn Progress @{Progress} component
function DruidInstance.new_progress(self, node, key, init_value)
return helper.require_component_message("progress")
--- Create Layout component
---@param node string|node The_node id or gui.get_node(node_id).
---@param mode string The layout mode
---@return druid.layout Layout component
function M:new_layout(node, mode)
return helper.require_component_message("layout") --[[@as druid.layout]]
end
--- Create @{Layout} component
-- @tparam DruidInstance self
-- @tparam string|node node The_node id or gui.get_node(node_id).
-- @tparam string mode The layout mode
-- @treturn Layout @{Layout} component
function DruidInstance.new_layout(self, node, mode)
return helper.require_component_message("layout")
--- Create Hotkey component
---@param keys_array string|string[] Keys for trigger action. Should contains one action key and any amount of modificator keys
---@param callback function The callback function
---@param callback_argument any|nil The argument to pass into the callback function
---@return druid.hotkey Hotkey component
function M:new_hotkey(keys_array, callback, callback_argument)
return helper.require_component_message("hotkey") --[[@as druid.hotkey]]
end
--- Create @{Hotkey} component
-- @tparam DruidInstance self
-- @tparam string|string[] keys_array Keys for trigger action. Should contains one action key and any amount of modificator keys
-- @tparam function callback The callback function
-- @tparam any|nil callback_argument The argument to pass into the callback function
-- @treturn Hotkey @{Hotkey} component
function DruidInstance.new_hotkey(self, keys_array, callback, callback_argument)
return helper.require_component_message("hotkey")
--- Create RichText component.
---@param text_node string|node The text node to make Rich Text
---@param value string|nil The initial text value. Default will be gui.get_text(text_node)
---@return druid.rich_text RichText component
function M:new_rich_text(text_node, value)
return helper.require_component_message("rich_text", "custom") --[[@as druid.rich_text]]
end
--- Create @{RichText} component.
-- @tparam DruidInstance self
-- @tparam string|node 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)
-- @treturn RichText @{RichText} component
function DruidInstance.new_rich_text(self, text_node, value)
return helper.require_component_message("rich_text", "custom")
end
--- Create @{RichInput} component.
--- Create RichInput component.
-- As a template please check rich_input.gui layout.
-- @tparam DruidInstance self
-- @tparam string template The template string name
-- @tparam table nodes Nodes table from gui.clone_tree
-- @treturn RichInput @{RichInput} component
function DruidInstance.new_rich_input(self, template, nodes)
return helper.require_component_message("rich_input", "custom")
---@param template string The template string name
---@param nodes table Nodes table from gui.clone_tree
---@return druid.rich_input RichInput component
function M:new_rich_input(template, nodes)
return helper.require_component_message("rich_input", "custom") --[[@as druid.rich_input]]
end
return DruidInstance
return M

View File

@@ -1,21 +1,15 @@
-- Copyright (c) 2021 Maksim Tuprikov <insality@gmail.com>. This code is licensed under MIT license
--- Druid settings file
-- @module settings
-- @local
---@class druid.system.settings
local M = {}
M.default_style = nil
function M.get_text(name, a, b, c, d, e, f, g)
---@param text_id string
---@vararg any
function M.get_text(text_id, ...)
return "[Druid]: locales not inited"
end
function M.play_sound(name)
function M.play_sound(sound_id)
end
return M

View File

@@ -1,21 +1,16 @@
local component = require("druid.component")
---@class component_name : druid.base_component
local Component = component.create("component_name")
local M = component.create("component_name")
-- 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("root")
function M:init(template, nodes)
self.druid = self:get_druid(template, nodes)
self.root = self:get_node("root")
self.button = self.druid:new_button("button", function() end)
self.button = self.druid:new_button("button", function() end)
end
-- [OPTIONAL] Call on component remove or on druid:final
function Component:on_remove()
end
return Component
return M

View File

@@ -1,10 +1,10 @@
local component = require("druid.component")
---@class component_name : druid.base_component
local Component = component.create("component_name")
---@class new_component: druid.base_component
local M = component.create("new_component")
-- Component constructor. Template name and nodes are optional. Pass it if you use it in your component
function Component:init(template, nodes)
function M:init(template, nodes)
-- If your component is gui template, pass the template name and set it
-- If your component is cloned my gui.clone_tree, pass nodes to component and set it
-- Use inner druid instance to create components inside this component
@@ -17,55 +17,55 @@ end
-- [OPTIONAL] Call every update step
function Component:update(dt)
function M:update(dt)
end
-- [OPTIONAL] Call default on_input from gui script
function Component:on_input(action_id, action)
function M:on_input(action_id, action)
return false
end
-- [OPTIONAL] Call on component creation and on component:set_style() function
function Component:on_style_change(style)
function M:on_style_change(style)
end
-- [OPTIONAL] Call default on_message from gui script
function Component:on_message(message_id, message, sender)
function M:on_message(message_id, message, sender)
end
-- [OPTIONAL] Call if druid has triggered on_language_change
function Component:on_language_change()
function M:on_language_change()
end
-- [OPTIONAL] Call if game layout has changed and need to restore values in component
function Component:on_layout_change()
function M:on_layout_change()
end
-- [OPTIONAL] Call, if input was capturing before this component
-- Example: scroll is start scrolling, so you need unhover button
function Component:on_input_interrupt()
function M:on_input_interrupt()
end
-- [OPTIONAL] Call, if game lost focus
function Component:on_focus_lost()
function M:on_focus_lost()
end
-- [OPTIONAL] Call, if game gained focus
function Component:on_focus_gained()
function M:on_focus_gained()
end
-- [OPTIONAL] Call on component remove or on druid:final
function Component:on_remove()
function M:on_remove()
end
return Component
return M