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

View File

@ -17,13 +17,15 @@ In this example you can inspect a variety of **Druid** components and see how th
## Setup
### Dependency
### [Dependency](https://www.defold.com/manuals/libraries/)
To integrate the **Druid** extension into your own project, add this project as a [dependency](https://www.defold.com/manuals/libraries/) in your **Defold** game. Open your `game.project` file and add the following line to the dependencies field under the project section:
Open your `game.project` file and add the following line to the dependencies field under the project section:
**Druid v1.0**
**[Druid](https://github.com/Insality/druid/archive/refs/tags/1.0.zip)**
> [https://github.com/Insality/druid/archive/refs/tags/1.0.zip](https://github.com/Insality/druid/archive/refs/tags/1.0.zip)
```
https://github.com/Insality/druid/archive/refs/tags/1.0.zip
```
Here is a list of [all releases](https://github.com/Insality/druid/releases).
@ -119,15 +121,15 @@ Here is full **Druid** components list.
| Name | Description | Example | <div style="width:200px">Preview</div> |
|------|-------------|---------|---------|
| **[Button](https://insality.github.io/druid/modules/Button.html)** | Logic over GUI Node. Handle the user click interactions: click, long click, double click, etc. | [Button Example](https://insality.github.io/druid/druid/?example=general_buttons) | <img src="media/preview/button.gif" width="200" height="100"> |
| **[Text](https://insality.github.io/druid/modules/Text.html)** | Logic over GUI Text. By default Text component fit the text inside text node size area with different adjust modes. | [Text Example](https://insality.github.io/druid/druid/?example=texts_general) | <img src="media/preview/text.gif" width="200" height="100"> |
| **[Scroll](https://insality.github.io/druid/modules/Scroll.html)** | Logic over two GUI Nodes: input and content. Provides basic behaviour for scrollable content. | [Scroll Example](https://insality.github.io/druid/druid/?example=general_scroll) | <img src="media/preview/scroll.gif" width="200" height="100"> |
| **[Blocker](https://insality.github.io/druid/modules/Blocker.html)** | Logic over GUI Node. Don't pass any user input below node area size. | [Blocker Example](https://insality.github.io/druid/druid/?example=timer) | <img src="media/preview/blocker.gif" width="200" height="100"> |
| **[Back Handler](https://insality.github.io/druid/modules/BackHandler.html)** | Call callback on user "Back" action. It's a Android back button or keyboard backspace key | [Back Handler Example](https://insality.github.io/druid/druid/?example=timer) | <img src="media/preview/back_handler.gif" width="200" height="100"> |
| **[Static Grid](https://insality.github.io/druid/modules/StaticGrid.html)** | Logic over GUI Node. Component to manage node positions with all equal node sizes. | [Static Gid Example](https://insality.github.io/druid/druid/?example=general_grid) | <img src="media/preview/static_grid.gif" width="200" height="100"> |
| **[Hover](https://insality.github.io/druid/modules/Hover.html)** | Logic over GUI Node. Handle hover action over node. For both: mobile touch and mouse cursor. | [Hover Example](https://insality.github.io/druid/druid/?example=timer) | <img src="media/preview/hover.gif" width="200" height="100"> |
| **[Swipe](https://insality.github.io/druid/modules/Swipe.html)** | Logic over GUI Node. Handle swipe gestures over node. | [Swipe Example](https://insality.github.io/druid/druid/?example=general_swipe) | <img src="media/preview/swipe.gif" width="200" height="100"> |
| **[Drag](https://insality.github.io/druid/modules/Drag.html)** | Logic over GUI Node. Handle drag input actions. Can be useful to make on screen controlls. | [Drag Example](https://insality.github.io/druid/druid/?example=general_drag) | <img src="media/preview/drag.gif" width="200" height="100"> |
| **[Button](https://insality.github.io/druid/modules/Button.html)** | Logic over GUI Node. Handle the user click interactions: click, long click, double click, etc. | [Button Example](https://insality.github.io/druid/druid/?example=ui_example_basic_button) | <img src="media/preview/button.gif" width="200" height="100"> |
| **[Text](https://insality.github.io/druid/modules/Text.html)** | Logic over GUI Text. By default Text component fit the text inside text node size area with different adjust modes. | [Text Example](https://insality.github.io/druid/druid/?example=ui_example_basic_text) | <img src="media/preview/text.gif" width="200" height="100"> |
| **[Scroll](https://insality.github.io/druid/modules/Scroll.html)** | Logic over two GUI Nodes: input and content. Provides basic behaviour for scrollable content. | [Scroll Example](https://insality.github.io/druid/druid/?example=ui_example_basic_scroll) | <img src="media/preview/scroll.gif" width="200" height="100"> |
| **[Blocker](https://insality.github.io/druid/modules/Blocker.html)** | Logic over GUI Node. Don't pass any user input below node area size. | [Blocker Example](https://insality.github.io/druid/druid/?example=ui_example_basic_blocker) | <img src="media/preview/blocker.gif" width="200" height="100"> |
| **[Back Handler](https://insality.github.io/druid/modules/BackHandler.html)** | Call callback on user "Back" action. It's a Android back button or keyboard backspace key | [Back Handler Example](https://insality.github.io/druid/druid/?example=ui_example_basic_back_handler) | <img src="media/preview/back_handler.gif" width="200" height="100"> |
| **[Static Grid](https://insality.github.io/druid/modules/StaticGrid.html)** | Logic over GUI Node. Component to manage node positions with all equal node sizes. | [Static Gid Example](https://insality.github.io/druid/druid/?example=ui_example_basic_grid) | <img src="media/preview/static_grid.gif" width="200" height="100"> |
| **[Hover](https://insality.github.io/druid/modules/Hover.html)** | Logic over GUI Node. Handle hover action over node. For both: mobile touch and mouse cursor. | [Hover Example](https://insality.github.io/druid/druid/?example=ui_example_basic_hover) | <img src="media/preview/hover.gif" width="200" height="100"> |
| **[Swipe](https://insality.github.io/druid/modules/Swipe.html)** | Logic over GUI Node. Handle swipe gestures over node. | [Swipe Example](https://insality.github.io/druid/druid/?example=ui_example_basic_swipe) | <img src="media/preview/swipe.gif" width="200" height="100"> |
| **[Drag](https://insality.github.io/druid/modules/Drag.html)** | Logic over GUI Node. Handle drag input actions. Can be useful to make on screen controlls. | [Drag Example](https://insality.github.io/druid/druid/?example=ui_example_basic_drag) | <img src="media/preview/drag.gif" width="200" height="100"> |
### Extended components
@ -141,16 +143,16 @@ druid.register("data_list", data_list)
| Name | Description | Example | <div style="width:200px">Preview</div> |
|------|-------------|---------|---------|
| **[Data List](https://insality.github.io/druid/modules/DataList.html)** | Logic over Scroll and Grid components. Create only visible GUI nodes or components to make "infinity" scroll befaviour | [Data List Example](https://insality.github.io/druid/druid/?example=general_data_list) | <img src="media/preview/data_list.gif" width="200" height="100"> |
| **[Input](https://insality.github.io/druid/modules/Input.html)** | Logic over GUI Node and GUI Text (or Text component). Provides basic user text input. | [Input Example](https://insality.github.io/druid/druid/?example=general_input) | <img src="media/preview/input.gif" width="200" height="100"> |
| **[Lang text](https://insality.github.io/druid/modules/LangText.html)** | Logic over Text component to handle localization. Can be translated in real-time with `druid.on_language_change` | [Lang Text Example](https://insality.github.io/druid/druid/?example=timer) | <img src="media/preview/lang_text.gif" width="200" height="100"> |
| **[Progress](https://insality.github.io/druid/modules/Progress.html)** | Logic over GUI Node. Handle node size and scale to handle progress node size. | [Progress Example](https://insality.github.io/druid/druid/?example=general_progress_bar) | <img src="media/preview/progress.gif" width="200" height="100"> |
| **[Slider](https://insality.github.io/druid/modules/Slider.html)** | Logic over GUI Node. Handle draggable node with position restrictions. | [Slider Example](https://insality.github.io/druid/druid/?example=general_sliders) | <img src="media/preview/slider.gif" width="200" height="100"> |
| **[Timer](https://insality.github.io/druid/modules/Timer.html)** | Logic over GUI Text. Handle basic timer functions. | [Timer Example](https://insality.github.io/druid/druid/?example=timer) | <img src="media/preview/timer.gif" width="200" height="100"> |
| **[Hotkey](https://insality.github.io/druid/modules/Hotkey.html)** | Allow to set callbacks for keyboard hotkeys with key modificators. | [Hotkey Example](https://insality.github.io/druid/druid/?example=general_hotkey) | <img src="media/preview/hotkey.gif" width="200" height="100"> |
| **[Layout](https://insality.github.io/druid/modules/Layout.html)** | Logic over GUI Node. Arrange nodes inside layout node with margin/paddings settings. | [Layout Example](https://insality.github.io/druid/druid/?example=general_layout) | <img src="media/preview/layout.gif" width="200" height="100"> |
| **[Rich Input](https://insality.github.io/druid/modules/RichInput.html)** | Logic over GUI Node and GUI Text (or Text component). Provides rich text input with different styles and text formatting. | [Rich Input Example](https://insality.github.io/druid/druid/?example=general_rich_input) | <img src="media/preview/rich_input.gif" width="200" height="100"> |
| **[Rich Text](https://insality.github.io/druid/modules/RichText.html)** | Logic over GUI Text. Provides rich text formatting with different styles and text formatting. | [Rich Text Example](https://insality.github.io/druid/druid/?example=general_rich_text) | <img src="media/preview/rich_text.gif" width="200" height="100"> |
| **[Data List](https://insality.github.io/druid/modules/DataList.html)** | Logic over Scroll and Grid components. Create only visible GUI nodes or components to make "infinity" scroll befaviour | [Data List Example](https://insality.github.io/druid/druid/?example=ui_example_data_list_basic) | <img src="media/preview/data_list.gif" width="200" height="100"> |
| **[Input](https://insality.github.io/druid/modules/Input.html)** | Logic over GUI Node and GUI Text (or Text component). Provides basic user text input. | [Input Example](https://insality.github.io/druid/druid/?example=ui_example_basic_input) | <img src="media/preview/input.gif" width="200" height="100"> |
| **[Lang text](https://insality.github.io/druid/modules/LangText.html)** | Logic over Text component to handle localization. Can be translated in real-time with `druid.on_language_change` | [Lang Text Example](https://insality.github.io/druid/druid/?example=ui_example_window_language) | <img src="media/preview/lang_text.gif" width="200" height="100"> |
| **[Progress](https://insality.github.io/druid/modules/Progress.html)** | Logic over GUI Node. Handle node size and scale to handle progress node size. | [Progress Example](https://insality.github.io/druid/druid/?example=ui_example_basic_progress_bar) | <img src="media/preview/progress.gif" width="200" height="100"> |
| **[Slider](https://insality.github.io/druid/modules/Slider.html)** | Logic over GUI Node. Handle draggable node with position restrictions. | [Slider Example](https://insality.github.io/druid/druid/?example=ui_example_basic_slider) | <img src="media/preview/slider.gif" width="200" height="100"> |
| **[Timer](https://insality.github.io/druid/modules/Timer.html)** | Logic over GUI Text. Handle basic timer functions. | [Timer Example](https://insality.github.io/druid/druid/?example=ui_example_basic_timer) | <img src="media/preview/timer.gif" width="200" height="100"> |
| **[Hotkey](https://insality.github.io/druid/modules/Hotkey.html)** | Allow to set callbacks for keyboard hotkeys with key modificators. | [Hotkey Example](https://insality.github.io/druid/druid/?example=ui_example_basic_hotkey) | <img src="media/preview/hotkey.gif" width="200" height="100"> |
| **[Layout](https://insality.github.io/druid/modules/Layout.html)** | Logic over GUI Node. Arrange nodes inside layout node with margin/paddings settings. | [Layout Example](https://insality.github.io/druid/druid/?example=ui_example_layout_basic) | <img src="media/preview/layout.gif" width="200" height="100"> |
| **[Rich Input](https://insality.github.io/druid/modules/RichInput.html)** | Logic over GUI Node and GUI Text (or Text component). Provides rich text input with different styles and text formatting. | [Rich Input Example](https://insality.github.io/druid/druid/?example=ui_example_basic_rich_input) | <img src="media/preview/rich_input.gif" width="200" height="100"> |
| **[Rich Text](https://insality.github.io/druid/modules/RichText.html)** | Logic over GUI Text. Provides rich text formatting with different styles and text formatting. | [Rich Text Example](https://insality.github.io/druid/druid/?example=ui_example_basic_rich_text) | <img src="media/preview/rich_text.gif" width="200" height="100"> |
For a complete overview, see: **_[components.md](docs_md/01-components.md)_**.

View File

@ -227,33 +227,6 @@ Create component with druid: `grid = druid:new_static_grid(parent_node, prefab_
- _Prefab node_ used to get node size and anchor
- You can point *position_function* for animations with _static_grid:set_position_function(node, pos)_ callback. Default - *gui.set_position()*
## Dynamic Grid
[Dynamic Grid API here](https://insality.github.io/druid/modules/DynamicGrid.html)
### Overview
Component for manage node positions with different node sizes.
Unlike Static Grid, Dynamic Grid can place nodes only in one row or in one column.
Dynamic Grid can't have gaps between elements
- you will get error, if try spawn element far away from others.
Dynamic Grid should have __West__, __East__, __South__ or __North__ pivot (vertical or horizontal element placement)
### Setup
Create component with druid: `grid = druid:new_dynamic_grid(parent_node)`
Check the _parent_node_ have correct pivot point. You will get the error otherwise.
### Notes
- On _add node_ grid will set node parent to _parent_node_
- You can get array of position of every element for setup points of interest in scroll component
- You can get size of all elements for setup size in scroll component
- You can also bind the grid to the scroll component for auto resize scroll content size
- Pivot of parent_node matter for node placement
- You can point *position_function* for animations with _static_grid:set_position_function(node, pos)_ callback. Default - *gui.set_position()*
- First node placed at Grid pivot point. Other nodes placed nearby of other nodes.
- On *add/remove* nodes always shifted. You can point the shift side in this functions (*is_shift_left* boolean argumentp
## Data List
[Data List API here](https://insality.github.io/druid/modules/DataList.html)

View File

@ -148,7 +148,6 @@ Available keywords:
- `text`: Adds a [Druid Text](01-components.md#text) component.
- `lang_text`: Adds a [Druid Lang Text](01-components.md#lang-text) component.
- `grid` or `static_grid`: Adds a [Druid Static Grid](01-components.md#static-grid) component. You should set up the Grid prefab for this component after generating the file.
- `dynamic_grid`: Adds a [Druid Dynamic Grid](01-components.md#dynamic-grid) component.
- `scroll_view`: Adds a [Druid Scroll](01-components.md#scroll) component. It also adds a `scroll_content` node with the same postfix. Ensure that it's the correct node.
- `blocker`: Adds a [Druid Blocker](01-components.md#blocker) component.
- `slider`: Adds a [Druid Slider](01-components.md#slider) component. You should adjust the end position of the Slider after generating the file.

View File

@ -121,7 +121,7 @@ window.set_listener(on_window_callback)
```
## EmmyLua Annotations
## Lua Annotations
[EmmyLua](https://emmylua.github.io/annotation.html) is a Lua annotation library. It is a useful tool for enabling Lua code autocompletion in editors such as [VSCode](https://github.com/EmmyLua/VSCode-EmmyLua) and [IntelliJ IDEA](https://github.com/EmmyLua/IntelliJ-EmmyLua).

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)
---@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)
---@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
@ -424,33 +350,30 @@ end
---Get current component nodes
-- @tparam BaseComponent self @{BaseComponent}
-- @treturn table BaseComponent nodes table
-- @local
function BaseComponent.__get_nodes(self)
---@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)
---@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(...)
---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
---@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)
---@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()
@ -196,11 +148,6 @@ 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()
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")
@ -49,36 +47,32 @@ 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
---@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
---@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
---
---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
---@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
-- @function helper.get_screen_aspect_koef
-- @treturn number stretch_x
-- @treturn number stretch_y
---@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()
@ -127,9 +144,7 @@ end
---Get current GUI scale for each side
-- @function helper.get_gui_scale
-- @treturn number scale_x
-- @treturn number scale_y
---@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(),
@ -138,11 +153,10 @@ 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
---@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)
@ -153,11 +167,10 @@ 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
---@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
@ -174,21 +187,19 @@ 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
---@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
@ -199,10 +210,9 @@ 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
---@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
@ -210,25 +220,22 @@ 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
---@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
@ -236,9 +243,8 @@ end
---Make a copy table with all nested tables
-- @function helper.deepcopy
-- @tparam table orig_table Original table
-- @treturn table Copy of original table
---@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
@ -255,10 +261,9 @@ 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
---@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)
@ -275,12 +280,11 @@ 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
---@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
---@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)
@ -318,9 +321,8 @@ 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
---@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,11 +1,11 @@
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)
function M:init(template, nodes)
self.druid = self:get_druid(template, nodes)
self.root = self:get_node("root")
@ -13,9 +13,4 @@ function Component:init(template, nodes)
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

View File

@ -134,7 +134,7 @@ end
--- Set new size of layout node
---@param width number|nil
---@param height number|nil
---@return druid.container @{Container}
---@return druid.container Container
function M:set_size(width, height)
width = width or self.size.x
height = height or self.size.y
@ -191,7 +191,7 @@ end
--- Set size for layout node to fit inside it
---@param target_size vector3
---@return druid.container @{Container}
---@return druid.container Container
function M:fit_into_size(target_size)
self.fit_size = target_size
self:refresh()
@ -200,7 +200,7 @@ end
--- Set current size for layout node to fit inside it
---@return druid.container @{Container}
---@return druid.container Container
function M:fit_into_window()
return self:fit_into_size(vmath.vector3(gui.get_width(), gui.get_height(), 0))
end
@ -221,7 +221,7 @@ end
---@param node_or_container node|string|druid.container|table
---@param mode string|nil stretch, fit, stretch_x, stretch_y. Default: Pick from node, "fit" or "stretch"
---@param on_resize_callback fun(self: userdata, size: vector3)|nil
---@return druid.container @{Container} New created layout instance
---@return druid.container Container New created layout instance
function M:add_container(node_or_container, mode, on_resize_callback)
local container = nil
local node = node_or_container
@ -422,7 +422,7 @@ function M:update_child_containers()
end
---@return druid.container @{Container}
---@return druid.container Container
function M:create_draggable_corners()
self:clear_draggable_corners()
@ -452,7 +452,7 @@ function M:create_draggable_corners()
end
---@return druid.container @{Container}
---@return druid.container Container
function M:clear_draggable_corners()
for index = 1, #self._draggable_corners do
local drag_component = self._draggable_corners[index]
@ -505,7 +505,7 @@ end
--- Set node for layout node to fit inside it. Pass nil to reset
---@param node string|node The node_id or gui.get_node(node_id)
---@return druid.container @{Layout}
---@return druid.container Layout
function M:fit_into_node(node)
self._fit_node = self:get_node(node)
self:refresh_scale()

View File

@ -1,6 +1,6 @@
local component = require("druid.component")
---@class basic_blocker: druid.component
---@class basic_blocker: druid.base_component
---@field druid druid_instance
---@field root node
---@field blocker druid.blocker

View File

@ -104,23 +104,23 @@ function druid_instance.new(self, component, ...) 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
---@generic T: druid.base_component
---@param self T @{BaseComponent}
---@param self T BaseComponent
---@param druid_style table|nil Druid style module
---@return T @{BaseComponent}
---@return T BaseComponent
function druid__base_component.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()
---@generic T: druid.base_component
---@param self T @{BaseComponent}
---@param self T BaseComponent
---@param template string BaseComponent template name
---@return T @{BaseComponent}
---@return T BaseComponent
function druid__base_component.set_template(self, template) end
--- Set current component nodes.
--- Use if your component nodes was cloned with `gui.clone_tree` and you got the node tree.
---@generic T: druid.base_component
---@param self T @{BaseComponent}
---@param self T BaseComponent
---@param nodes table BaseComponent nodes table
---@return T @{BaseComponent}
---@return T BaseComponent
function druid__base_component.set_nodes(self, nodes) end