diff --git a/.vscode/settings.json b/.vscode/settings.json
index 125c6f08..fc31eb25 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -27,6 +27,11 @@
"test/tests/*.lua",
"utils/annotations_manual.lua"
],
+ "Lua.runtime.pathStrict": true,
"Lua.diagnostics.libraryFiles": "Enable",
- "Lua.runtime.version": "Lua 5.1"
+ "Lua.runtime.version": "Lua 5.1",
+ "Lua.workspace.library": [
+ "~/Library/Application Support/Code/User/globalStorage/astronachos.defold",
+ "~/Library/Application Support/Code/User/workspaceStorage/72e25b7e0fdc873ee6f7baa61edbd6b1/astronachos.defold"
+ ]
}
\ No newline at end of file
diff --git a/README.md b/README.md
index 84f0cb52..75a3ae53 100644
--- a/README.md
+++ b/README.md
@@ -17,9 +17,9 @@ Try the [**HTML5 version**](https://insality.github.io/druid/druid/) of the **Dr
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:
-**Druid v0.11.0**
+**Druid v0.12.0**
-> [https://github.com/Insality/druid/archive/refs/tags/0.11.0.zip](https://github.com/Insality/druid/archive/refs/tags/0.11.0.zip)
+> [https://github.com/Insality/druid/archive/refs/tags/0.12.0.zip](https://github.com/Insality/druid/archive/refs/tags/0.12.0.zip)
Here is a list of [all releases](https://github.com/Insality/druid/releases).
diff --git a/config.ld b/config.ld
index 1a295b1b..a487ec90 100644
--- a/config.ld
+++ b/config.ld
@@ -4,7 +4,6 @@ description='Documentation for Druid Framework'
file={"./druid",
exclude = {
"./druid/styles/",
- "./druid/system/middleclass.lua",
"./druid/templates/",
"./druid/annotations.lua",
"./druid/custom/rich_text/module",
diff --git a/docs/modules/BaseComponent.html b/docs/modules/BaseComponent.html
index 1c816176..4d4d649c 100644
--- a/docs/modules/BaseComponent.html
+++ b/docs/modules/BaseComponent.html
@@ -106,7 +106,7 @@
Context used as first arg in all Druid events
- get_druid(self)
+ get_druid(self, template, nodes)
Get Druid instance for inner component creation.
@@ -226,7 +226,7 @@ Returns:
- get_druid(self)
+ get_druid(self, template, nodes)
Get Druid instance for inner component creation.
@@ -238,6 +238,14 @@ Parameters:
BaseComponent
BaseComponent
+ template
+ string or nil
+ The template name
+
+ nodes
+ table or nil
+ The nodes table
+
Returns:
diff --git a/docs/modules/DataList.html b/docs/modules/DataList.html
index 8003f364..7dcbdbe6 100644
--- a/docs/modules/DataList.html
+++ b/docs/modules/DataList.html
@@ -100,22 +100,10 @@
Return current data from DataList component
- get_first_index(self)
- Return first index from data.
-
-
get_index(self, data)
Return index for data value
- get_last_index(self)
- Return last index from data
-
-
- get_length(self)
- Return amount of data
-
-
init(self, scroll, grid, create_function)
The DataList constructor
@@ -131,6 +119,10 @@
set_data(self, data)
Set new data set for DataList component
+
+ set_use_cache(self, is_use_cache)
+ Set refresh function for DataList component
+
@@ -140,7 +132,7 @@
last_index
- The current visual last data index
+ The current last index of visual elements
on_element_add
@@ -164,7 +156,7 @@
top_index
- The current visual top data index
+ The current top index of visual elements
@@ -276,27 +268,6 @@ Returns:
-
-
-
- get_first_index(self)
-
-
- Return first index from data. It not always equals to 1
-
-
- Parameters:
-
-
-
-
-
-
@@ -322,48 +293,6 @@ Parameters:
-
-
-
- get_last_index(self)
-
-
- Return last index from data
-
-
- Parameters:
-
-
-
-
-
-
-
-
-
- get_length(self)
-
-
- Return amount of data
-
-
- Parameters:
-
-
-
-
-
-
@@ -474,6 +403,37 @@ Returns:
+
+
+
+ set_use_cache(self, is_use_cache)
+
+
+ Set refresh function for DataList component
+
+
+ Parameters:
+
+ self
+ DataList
+ DataList
+
+ is_use_cache
+ boolean
+ Use cache version of DataList. Requires make setup of components in on_element_add callback and clean in on_element_remove
+
+
+
+ Returns:
+
+
+ druid.data_list
+ Current DataList instance
+
+
+
+
+
@@ -504,7 +464,7 @@
last_index
- The current visual last data index
+ The current last index of visual elements
+
+
+
+
+ is_empty(self)
+
+
+ Return true, if event not have handler
+
+
+ Parameters:
+
+
+ Returns:
+
+
+ boolean
+ True if event not have handlers
+
+
+
+
+ Usage:
+ local is_long_click_handler_not_exists = button.on_long_click:is_empty()
@@ -201,10 +240,45 @@ Usage:
local is_long_click_handler_exists = button.on_long_click:is_exist()
+
+
+
+ is_subscribed(self, callback, callback_context)
+
+
+ Check is event subscribed.
+
+
+ Parameters:
+
+ self
+ DruidEvent
+ DruidEvent
+
+ callback
+ function
+ Callback itself
+
+ callback_context
+ any or nil
+ Additional context as first param to callback call
+
+
+
+ Returns:
+
+
+ boolean,
+ number|nil @Is event subscribed, return index of callback in event as second param
+
+
+
+
+
- subscribe(self, callback, context)
+ subscribe(self, callback, callback_context)
Subscribe callback on event
@@ -220,12 +294,18 @@ Parameters:
function
Callback itself
- context
+ callback_context
any or nil
Additional context as first param to callback call, usually it's self
+ Returns:
+
+
+ boolean
+ True if callback was subscribed
+
@@ -274,7 +354,7 @@ Usage:
- unsubscribe(self, callback, context)
+ unsubscribe(self, callback, callback_context)
Unsubscribe callback on event
@@ -290,7 +370,7 @@ Parameters:
function
Callback itself
- context
+ callback_context
any or nil
Additional context as first param to callback call
diff --git a/docs/modules/DruidInstance.html b/docs/modules/DruidInstance.html
index 71a1df52..5007e3c9 100644
--- a/docs/modules/DruidInstance.html
+++ b/docs/modules/DruidInstance.html
@@ -142,10 +142,6 @@
Call this in gui_script final function.
- new(self, component, ...)
- Create new component.
-
-
new_back_handler(self, callback, params)
Create BackHandler component
@@ -182,7 +178,7 @@
Create Hotkey component
- new_hover(self, node, on_hover_callback)
+ new_hover(self, node, on_hover_callback, on_mouse_hover_callback)
Create Hover component
@@ -194,7 +190,7 @@
Create LangText component
- new_layout(self, node, mode, on_size_changed_callback)
+ new_layout(self, node, mode)
Create Layout component
@@ -286,41 +282,6 @@ Parameters:
-
-
-
- new(self, component, ...)
-
-
- Create new component.
-
-
- Parameters:
-
- self
- DruidInstance
-
-
- component
- BaseComponent
- Component module
-
- ...
- any
- Other component params to pass it to component:init function
-
-
-
- Returns:
-
-
- BaseComponent
- Component instance
-
-
-
-
-
@@ -411,7 +372,7 @@ Parameters:
Button callback
params
- table or nil
+ any or nil
Button callback params
anim_node
@@ -659,7 +620,7 @@ Returns:
- new_hover(self, node, on_hover_callback)
+ new_hover(self, node, on_hover_callback, on_mouse_hover_callback)
Create Hover component
@@ -679,6 +640,10 @@ Parameters:
function or nil
Hover callback
+ on_mouse_hover_callback
+ function or nil
+ Mouse hover callback
+
Returns:
@@ -772,7 +737,7 @@ Returns:
- new_layout(self, node, mode, on_size_changed_callback)
+ new_layout(self, node, mode)
Create Layout component
@@ -792,10 +757,6 @@ Parameters:
string
The layout mode
- on_size_changed_callback
- function or nil
- The callback on window resize
-
Returns:
@@ -1244,6 +1205,12 @@ Parameters:
+ Returns:
+
+
+ boolean
+ True if component was removed
+
diff --git a/docs/modules/Helper.html b/docs/modules/Helper.html
index 2ede020a..f465d57f 100644
--- a/docs/modules/Helper.html
+++ b/docs/modules/Helper.html
@@ -151,10 +151,18 @@
Check if device is native mobile (Android or iOS)
+ helper.is_multitouch_supported()
+ Check if device is mobile and can support multitouch
+
+
helper.is_web()
Check if device is HTML5
+ helper.is_web_mobile()
+ Check if device is HTML5 mobile
+
+
helper.lerp(a, b, t)
Lerp between two values
@@ -658,6 +666,26 @@ Returns:
+
+
+
+ helper.is_multitouch_supported()
+
+
+ Check if device is mobile and can support multitouch
+
+
+
+ Returns:
+
+
+ boolean
+ Is multitouch supported
+
+
+
+
+
@@ -678,6 +706,26 @@ Returns:
+
+
+
+ helper.is_web_mobile()
+
+
+ Check if device is HTML5 mobile
+
+
+
+ Returns:
+
+
+ boolean
+ Is web mobile
+
+
+
+
+
diff --git a/docs/modules/Hotkey.html b/docs/modules/Hotkey.html
index bb8c0daa..68e40151 100644
--- a/docs/modules/Hotkey.html
+++ b/docs/modules/Hotkey.html
@@ -117,8 +117,12 @@
Visual node
- on_change_state
- On change state callback(self, state)
+ on_hotkey_pressed
+ On hotkey released callback(self, argument)
+
+
+ on_hotkey_released
+ On hotkey released callback(self, argument)
@@ -320,15 +324,35 @@
-
- on_change_state
+
+ on_hotkey_pressed
+
+
+ On hotkey released callback(self, argument)
+
+
+
+
+
+
+
+
+
+
+
+ on_hotkey_released
- On change state callback(self, state)
+ On hotkey released callback(self, argument)
+
+
+ get_text_selected_replaced(self, text)
+
+
+ Replace selected text with new text
+
+
+ Parameters:
+
+ self
+ Input
+ Input
+
+ text
+ string
+ The text to replace selected text
+
+
+
+ Returns:
+
+
+ string
+ New input text
+
+
+
+
+
@@ -250,6 +333,39 @@ Parameters:
+
+
+
+ move_selection(self, delta, is_add_to_selection, is_move_to_end)
+
+
+ Change cursor position by delta
+
+
+ Parameters:
+
+ self
+ Input
+ Input
+
+ delta
+ number
+ side for cursor position, -1 for left, 1 for right
+
+ is_add_to_selection
+ boolean
+ (Shift key)
+
+ is_move_to_end
+ boolean
+ (Ctrl key)
+
+
+
+
+
+
+
@@ -267,6 +383,12 @@ Parameters:
+ Returns:
+
+
+ druid.input
+ Current input instance
+
@@ -292,6 +414,45 @@ Parameters:
+
+
+
+ select_cursor(self, cursor_index, start_index, end_index)
+
+
+ Set cursor position in input field
+
+
+ Parameters:
+
+ self
+ Input
+ Input
+
+ cursor_index
+ number or nil
+ Cursor index for cursor position, if nil - will be set to the end of the text
+
+ start_index
+ number or nil
+ Start index for cursor position, if nil - will be set to the end of the text
+
+ end_index
+ number or nil
+ End index for cursor position, if nil - will be set to the start_index
+
+
+
+ Returns:
+
+
+ druid.input
+ Current input instance
+
+
+
+
+
@@ -432,10 +593,6 @@ Fields:
boolean
If true, call unselect on select selected input. Default: false
- NO_CONSUME_INPUT_WHILE_SELECTED
- boolean
- If true, will not consume input while input is selected. It's allow to interact with other components while input is selected (text input still captured). Default: false
-
on_select
function
(self, button_node) Callback on input field selecting
@@ -502,6 +659,66 @@
+
+
+
+ current_value
+
+
+ Current input value with marked text
+
+
+
+ current_value
+ string
+
+
+
+
+
+
+
+
+
+
+
+ cursor_index
+
+
+ The cursor index. The index of letter cursor after. Leftmost cursor - 0
+
+
+
+ cursor_index
+ number
+
+
+
+
+
+
+
+
+
+
+
+ end_index
+
+
+ Theselection end index. The index of letter cursor before. Rightmost selection - #text
+
+
+
+ end_index
+ number
+
+
+
+
+
+
+
+
@@ -562,6 +779,46 @@
+
+
+
+ marked_text_width
+
+
+ Marked text width
+
+
+
+ marked_text_width
+ number
+
+
+
+
+
+
+
+
+
+
+
+ marked_value
+
+
+ Marked text for input field. Info: https://defold.com/manuals/input-key-and-text/#marked-text
+
+
+
+
+
+
+
+
@@ -628,7 +885,7 @@
on_input_select
- On input field select callback(self, button_node)
+ On input field select callback(self, input_instance)
@@ -668,7 +925,7 @@
on_input_unselect
- On input field unselect callback(self, input_text)
+ On input field unselect callback(self, input_text, input_instance)
@@ -688,7 +945,7 @@
on_input_wrong
- On trying user input with not allowed character callback(self, params, button_instance)
+ On trying user input with not allowed character callback(self, params, input_text)
+
+
+ on_select_cursor_change
+
+
+ On cursor position change callback(self, cursor_index, start_index, end_index)
+
+
+
+ on_select_cursor_change
+ DruidEvent
+ DruidEvent
+
+
+
+
+
+
+
+
+
+
+ previous_value
+
+
+ Previous input value
+
+
+
+ previous_value
+ string
+
+
+
+
+
+
+
+
+
+
+
+ start_index
+
+
+ The selection start index. The index of letter cursor after. Leftmost selection - 0
+
+
+
+ start_index
+ number
+
+
+
+
+
+
+
+
@@ -722,6 +1039,46 @@
+
+
+
+ text_width
+
+
+ Text width
+
+
+
+ text_width
+ number
+
+
+
+
+
+
+
+
+
+
+
+ value
+
+
+ Current input value
+
+
+
+
+
+
+
+
diff --git a/docs/modules/Layout.html b/docs/modules/Layout.html
index edad2b69..c8f41408 100644
--- a/docs/modules/Layout.html
+++ b/docs/modules/Layout.html
@@ -32,7 +32,6 @@ Druid
Contents
@@ -79,45 +78,6 @@ Module Layout
Example Link
-
-
-
- fit_into_node(self, node)
- Set node for layout node to fit inside it.
-
-
- fit_into_size(self, target_size)
- Set size for layout node to fit inside it
-
-
- fit_into_window(self)
- Set current size for layout node to fit inside it
-
-
- init(self, node, mode, on_size_changed_callback)
- The Layout constructor
-
-
- set_max_gui_upscale(self, max_gui_upscale)
- Set max gui upscale for FIT adjust mode (or side).
-
-
- set_max_size(self, max_size)
- Set maximum size of layout node
-
-
- set_min_size(self, min_size)
- Set minimal size of layout node
-
-
- set_origin_position(self, new_origin_position)
- Set new origin position of layout node.
-
-
- set_origin_size(self, new_origin_size)
- Set new origin size of layout node.
-
-
@@ -128,297 +88,12 @@
node
Layout node
-
- on_size_changed
- On window resize callback(self, new_size)
-
-
-
-
-
-
- fit_into_node(self, node)
-
-
- Set node for layout node to fit inside it. Pass nil to reset
-
-
- Parameters:
-
- self
- Layout
- Layout
-
- node
- node or nil
-
-
-
-
- Returns:
-
-
- Layout
- Layout
-
-
-
-
-
-
-
-
- fit_into_size(self, target_size)
-
-
- Set size for layout node to fit inside it
-
-
- Parameters:
-
- self
- Layout
- Layout
-
- target_size
- vector3
-
-
-
-
- Returns:
-
-
- Layout
- Layout
-
-
-
-
-
-
-
-
- fit_into_window(self)
-
-
- Set current size for layout node to fit inside it
-
-
- Parameters:
-
-
- Returns:
-
-
- Layout
- Layout
-
-
-
-
-
-
-
-
- init(self, node, mode, on_size_changed_callback)
-
-
- The Layout constructor
-
-
- Parameters:
-
- self
- Layout
- Layout
-
- node
- node
- Gui node
-
- mode
- string
- The layout mode (from const.LAYOUT_MODE)
-
- on_size_changed_callback
- function or nil
- The callback on window resize
-
-
-
-
-
-
-
-
-
-
- set_max_gui_upscale(self, max_gui_upscale)
-
-
- Set max gui upscale for FIT adjust mode (or side). It happens on bigger render gui screen
-
-
- Parameters:
-
- self
- Layout
- Layout
-
- max_gui_upscale
- number
-
-
-
-
- Returns:
-
-
- Layout
- Layout
-
-
-
-
-
-
-
-
- set_max_size(self, max_size)
-
-
- Set maximum size of layout node
-
-
- Parameters:
-
- self
- Layout
- Layout
-
- max_size
- vector3
-
-
-
-
- Returns:
-
-
- Layout
- Layout
-
-
-
-
-
-
-
-
- set_min_size(self, min_size)
-
-
- Set minimal size of layout node
-
-
- Parameters:
-
- self
- Layout
- Layout
-
- min_size
- vector3
-
-
-
-
- Returns:
-
-
- Layout
- Layout
-
-
-
-
-
-
-
-
- set_origin_position(self, new_origin_position)
-
-
- Set new origin position of layout node. You should apply this on node movement
-
-
- Parameters:
-
- self
- Layout
- Layout
-
- new_origin_position
- vector3
-
-
-
-
- Returns:
-
-
- Layout
- Layout
-
-
-
-
-
-
-
-
- set_origin_size(self, new_origin_size)
-
-
- Set new origin size of layout node. You should apply this on node manual size change
-
-
- Parameters:
-
- self
- Layout
- Layout
-
- new_origin_size
- vector3
-
-
-
-
- Returns:
-
-
- Layout
- Layout
-
-
-
-
-
-
-
@@ -461,26 +136,6 @@
-
-
-
- on_size_changed
-
-
- On window resize callback(self, new_size)
-
-
-
-
-
-
-
-
diff --git a/docs/modules/RichInput.html b/docs/modules/RichInput.html
index c8d62dca..0c709361 100644
--- a/docs/modules/RichInput.html
+++ b/docs/modules/RichInput.html
@@ -84,20 +84,32 @@
@@ -135,7 +147,7 @@
get_text(self)
- GSet input field text
+ Set input field text
Parameters:
@@ -146,12 +158,6 @@ Parameters:
- Returns:
-
-
- string
- Current input text
-
@@ -185,6 +191,27 @@ Parameters:
+
+
+
+ select(self)
+
+
+ Select input field
+
+
+ Parameters:
+
+
+
+
+
+
@@ -218,6 +245,37 @@ Returns:
+
+
+
+ set_font(self, font)
+
+
+ Set input field font
+
+
+ Parameters:
+
+ self
+ RichInput
+ RichInput
+
+ font
+ hash
+ The font hash
+
+
+
+ Returns:
+
+
+ druid.input
+ Current input instance
+
+
+
+
+
@@ -234,16 +292,41 @@ Parameters:
RichInput
placeholder_text
- string or nil
+ string
The placeholder text
+
+
+
+
+
+
+
+ set_text(self, text)
+
+
+ Set input field text
+
+
+ Parameters:
+
+
Returns:
- RichInput
- Current instance
+ druid.input
+ Current input instance
diff --git a/docs/modules/RichText.html b/docs/modules/RichText.html
index 990ce514..c72269b9 100644
--- a/docs/modules/RichText.html
+++ b/docs/modules/RichText.html
@@ -150,6 +150,10 @@ Usage:
+
+ characters(self, word)
+ Split a word into it's characters
+
clear()
Clear all created words.
@@ -159,11 +163,15 @@
Get current line metrics
+ get_text(self)
+ Get current text
+
+
get_words()
Get all current words.
- init(self, template, nodes)
+ init(self, text_node, value)
The RichText constructor
@@ -171,7 +179,7 @@
Set text for Rich Text
- tagged(tag)
+ tagged(self, tag)
Get all words, which has a passed tag.
@@ -188,6 +196,18 @@
druid
The component druid instance
+
+ icon_prefab
+ The icon prefab node
+
+
+ root
+ The root node of the Rich Text
+
+
+ text_prefab
+ The text prefab node
+
@@ -197,6 +217,37 @@
+
+
+ characters(self, word)
+
+
+ Split a word into it's characters
+
+
+ Parameters:
+
+ self
+ RichText
+ RichText
+
+ word
+ druid.rich_text.word
+
+
+
+
+ Returns:
+
+
+ druid.rich_text.word[]
+ characters
+
+
+
+
+
+
clear()
@@ -230,6 +281,33 @@ Returns:
+
+
+
+ get_text(self)
+
+
+ Get current text
+
+
+ Parameters:
+
+
+ Returns:
+
+
+ string
+ text
+
+
+
+
+
@@ -253,7 +331,7 @@ Returns:
- init(self, template, nodes)
+ init(self, text_node, value)
The RichText constructor
@@ -265,13 +343,13 @@ Parameters:
RichText
RichText
- template
- string
- The Rich Text template name
+ text_node
+ node or string
+ The text node to make Rich Text
- nodes
- table
- The node table, if prefab was copied by gui.clone_tree()
+ value
+ string or nil
+ The initial text value. Default will be gui.get_text(text_node)
@@ -295,7 +373,7 @@ Parameters:
RichText
text
- string
+ string or nil
The text to set
@@ -361,7 +439,7 @@ Usage:
- tagged(tag)
+ tagged(self, tag)
Get all words, which has a passed tag.
@@ -369,6 +447,10 @@ Usage:
Parameters:
+
+
+ icon_prefab
+
+
+ The icon prefab node
+
+
+
+ icon_prefab
+ node
+
+
+
+
+
+
+
+
+
+
+
+ root
+
+
+ The root node of the Rich Text
+
+
+
+
+
+
+
+
+
+
+
+ text_prefab
+
+
+ The text prefab node
+
+
+
+ text_prefab
+ node
+
+
+
+
+
+
+
+
diff --git a/docs/modules/Scroll.html b/docs/modules/Scroll.html
index 6eefd066..dcf5180b 100644
--- a/docs/modules/Scroll.html
+++ b/docs/modules/Scroll.html
@@ -167,6 +167,14 @@
set_vertical_scroll(self, state)
Lock or unlock vertical scroll
+
+ set_view_size(self, size)
+ Set new scroll view size in case the node size was changed.
+
+
+ update_view_size(self)
+ Refresh scroll view size
+
@@ -733,6 +745,58 @@ Returns:
+
+
+
+ set_view_size(self, size)
+
+
+ Set new scroll view size in case the node size was changed.
+
+
+ Parameters:
+
+ self
+ Scroll
+ Scroll
+
+ size
+ vector3
+ The new size for view node
+
+
+
+ Returns:
+
+
+ druid.scroll
+ Current scroll instance
+
+
+
+
+
+
+
+
+ update_view_size(self)
+
+
+ Refresh scroll view size
+
+
+ Parameters:
+
+
+
+
+
+
@@ -819,7 +883,7 @@
_is_inert
- bool
+ boolean
@@ -1088,6 +1152,26 @@
+
+
+
+ view_size
+
+
+ Scroll view size
+
+
+
+ view_size
+ vector3
+
+
+
+
+
+
+
+
diff --git a/docs/modules/Slider.html b/docs/modules/Slider.html
index bc726439..94fc9f08 100644
--- a/docs/modules/Slider.html
+++ b/docs/modules/Slider.html
@@ -86,10 +86,18 @@
The Slider constructor
+ is_enabled(self)
+ Check if Slider component is enabled
+
+
set(self, value, is_silent)
Set value for slider
+ set_enabled(self, is_enabled)
+ Set Slider input enabled or disabled
+
+
set_input_node(self, input_node)
Set input zone for slider.
@@ -177,6 +185,33 @@ Parameters:
+
+
+
+ is_enabled(self)
+
+
+ Check if Slider component is enabled
+
+
+ Parameters:
+
+
+ Returns:
+
+
+ boolean
+
+
+
+
+
+
@@ -206,6 +241,31 @@ Parameters:
+
+
+
+ set_enabled(self, is_enabled)
+
+
+ Set Slider input enabled or disabled
+
+
+ Parameters:
+
+ self
+ Slider
+ Slider
+
+ is_enabled
+ boolean
+
+
+
+
+
+
+
+
@@ -291,7 +351,7 @@
diff --git a/docs/modules/StaticGrid.html b/docs/modules/StaticGrid.html
index 4f2daeed..076f3bb0 100644
--- a/docs/modules/StaticGrid.html
+++ b/docs/modules/StaticGrid.html
@@ -154,9 +154,21 @@
Set new in_row elements for grid
+ set_item_size(self[, width[, height]])
+ Set new node size for grid
+
+
+ set_items(self, nodes[, is_instant=false])
+ Set new items to the grid.
+
+
set_position_function(self, callback)
Change set position function for grid nodes.
+
+ sort_nodes(self, comparator)
+ Sort grid nodes by custom comparator function
+
@@ -621,6 +633,73 @@ Returns:
+
+
+
+ set_item_size(self[, width[, height]])
+
+
+ Set new node size for grid
+
+
+ Parameters:
+
+ self
+ StaticGrid
+ StaticGrid
+
+ width
+ number
+ The new node width
+ (optional )
+
+ height
+ number
+ The new node height
+ (optional )
+
+
+
+ Returns:
+
+
+ druid.static_grid
+ Current grid instance
+
+
+
+
+
+
+
+
+ set_items(self, nodes[, is_instant=false])
+
+
+ Set new items to the grid. All previous items will be removed
+
+
+ Parameters:
+
+ self
+ StaticGrid
+ StaticGrid
+
+ nodes
+ node[]
+ The new grid nodes
+
+ is_instant
+ boolean
+ If true, update node positions instantly
+ (default false)
+
+
+
+
+
+
+
@@ -653,6 +732,37 @@ Returns:
+
+
+
+ sort_nodes(self, comparator)
+
+
+ Sort grid nodes by custom comparator function
+
+
+ Parameters:
+
+ self
+ StaticGrid
+ StaticGrid
+
+ comparator
+ function
+ The comparator function. (a, b) -> boolean
+
+
+
+ Returns:
+
+
+ druid.static_grid
+ Current grid instance
+
+
+
+
+
diff --git a/docs/modules/Text.html b/docs/modules/Text.html
index eb925ac7..4f7d1967 100644
--- a/docs/modules/Text.html
+++ b/docs/modules/Text.html
@@ -105,6 +105,10 @@
Return current text adjust type
+ get_text_index_by_width(self, width)
+ Get chars count by width
+
+
get_text_size(self, text)
Calculate text width with font with respect to trailing space
@@ -247,6 +251,37 @@ Returns:
+
+
+
+ get_text_index_by_width(self, width)
+
+
+ Get chars count by width
+
+
+ Parameters:
+
+ self
+ Text
+ Text
+
+ width
+ number
+
+
+
+
+ Returns:
+
+
+ number
+ Chars count
+
+
+
+
+
@@ -263,8 +298,8 @@ Parameters:
Text
text
- string or nil
-
+ string
+ |nil
@@ -302,11 +337,11 @@ Parameters:
value
string or nil
- Initial text. Default value is node text from GUI scene.
+ Initial text. Default value is node text from GUI scene. Default: nil
adjust_type
string or nil
- Adjust type for text. By default is DOWNSCALE. Look const.TEXT_ADJUST for reference. Default: downscale
+ Adjust type for text. By default is DOWNSCALE. Look const.TEXT_ADJUST for reference. Default: DOWNSCALE
@@ -618,6 +653,14 @@ Fields:
string or nil
The default adjust type for any text component. Default: DOWNSCALE
+ ADJUST_STEPS
+ string or nil
+ Amount of iterations for text adjust by height. Default: 20
+
+ ADJUST_SCALE_DELTA
+ string or nil
+ Scale step on each height adjust step. Default: 0.02
+
diff --git a/docs/modules/Timer.html b/docs/modules/Timer.html
index 7c3eb829..9dca7a5f 100644
--- a/docs/modules/Timer.html
+++ b/docs/modules/Timer.html
@@ -158,7 +158,7 @@ Parameters:
Gui text node
seconds_from
- number
+ number or nil
Start timer value in seconds
seconds_to
diff --git a/docs/modules/druid.extended.layout.html b/docs/modules/druid.extended.layout.html
new file mode 100644
index 00000000..4f01fd5c
--- /dev/null
+++ b/docs/modules/druid.extended.layout.html
@@ -0,0 +1,95 @@
+
+
+
+
+ Defold Druid UI Framework
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Druid
+
+
+
+
+
+
Modules
+
+
+
+
+
+
+
Module druid.extended.layout
+
Druid layout module
+
# Overview #
+
Layout component works like Dynamic Grid before - for aligning elements in a row or column.
+
Works like a Figma layout.
+
# Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
generated by LDoc TESTING
+
Last updated 2015-01-01 12:00:00
+
+
+
+
diff --git a/docs/modules/druid.system.utf8.html b/docs/modules/druid.system.utf8.html
new file mode 100644
index 00000000..05821062
--- /dev/null
+++ b/docs/modules/druid.system.utf8.html
@@ -0,0 +1,93 @@
+
+
+
+
+ Defold Druid UI Framework
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Druid
+
+
+
+
+
+
Modules
+
+
+
+
+
+
+
Module druid.system.utf8
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
generated by LDoc TESTING
+
Last updated 2015-01-01 12:00:00
+
+
+
+
diff --git a/docs_md/02-creating_custom_components.md b/docs_md/02-creating_custom_components.md
index b7aaf993..b763f6f2 100644
--- a/docs_md/02-creating_custom_components.md
+++ b/docs_md/02-creating_custom_components.md
@@ -14,21 +14,14 @@ A basic custom component template looks like this (you can copy it from `/druid/
```lua
local component = require("druid.component")
----@class component_name : druid.base_component
+---@class component_name: druid.base_component
local Component = component.create("component_name")
-local SCHEME = {
- ROOT = "root",
- BUTTON = "button",
-}
-
function Component:init(template, nodes)
- self:set_template(template)
- self:set_nodes(nodes)
- self.root = self:get_node(SCHEME.ROOT)
- self.druid = self:get_druid()
+ self.druid = self:get_druid(template, nodes)
+ self.root = self:get_node("root")
- self.button = self.druid:new_button(SCHEME.BUTTON, function() end)
+ self.button = self.druid:new_button("button", function() end)
end
function Component:on_remove() end
@@ -43,18 +36,12 @@ A full custom component template looks like this (you can copy it from `/druid/t
```lua
local component = require("druid.component")
----@class component_name : druid.base_component
+---@class component_name: druid.base_component
local Component = component.create("component_name")
-local SCHEME = {
- ROOT = "root",
- BUTTON = "button",
-}
function Component:init(template, nodes)
- self:set_template(template)
- self:set_nodes(nodes)
- self.root = self:get_node(SCHEME.ROOT)
- self.druid = self:get_druid()
+ self.druid = self:get_druid(template, nodes)
+ self.root = self:get_node("root")
end
function Component:update(dt) end
@@ -92,7 +79,7 @@ local my_component = require("my.amazing.component")
function init(self)
self.druid = druid.new(self)
- self.druid:new(my_component, "template_name", nodes)
+ self.druid:new(my_component, "template_name")
end
```
@@ -107,6 +94,7 @@ local druid = require("druid.druid")
local my_component = require("my.amazing.component")
function init(self)
+ -- Register makes a "druid:new_{component_name}" function available
druid.register("my_component", my_component)
end
```
@@ -149,33 +137,6 @@ Available keywords:
uid Timer](01-components.md#timer) component.
-## Best Practices for Custom Components
-
-When working with each component, it's recommended to describe the component scheme in the following way:
-
-```lua
--- Component module
-local component = require("druid.component")
-
-local M = component.create("your_component")
-
-local SCHEME = {
- ROOT = "root",
- ITEM = "item",
- TITLE = "title"
-}
-
-function M.init(self, template_name, node_table)
- self:set_template(template_name)
- self:set_nodes(node_table)
-
- local root = self:get_node(SCHEME.ROOT)
- local druid = self:get_druid()
-
- -- Create components inside this component using the inner druid instance
-end
-```
-
## The Power of Using Templates
With Druid, you can use a single component but create and customize templates for it. Templates only need to match the component scheme. For example, you can have a component named `player_panel` and two GUI templates named `player_panel` and `enemy_panel` with different layouts. The same component script can be used for both templates.
diff --git a/druid/annotations.lua b/druid/annotations.lua
index e8395b4a..c8e29dd4 100644
--- a/druid/annotations.lua
+++ b/druid/annotations.lua
@@ -367,6 +367,12 @@ function druid__data_list.scroll_to_index(self, index) end
---@return druid.data_list Current DataList instance
function druid__data_list.set_data(self, data) end
+--- Set refresh function for DataList component
+---@param self druid.data_list @{DataList}
+---@param is_use_cache boolean Use cache version of DataList. Requires make setup of components in on_element_add callback and clean in on_element_remove
+---@return druid.data_list Current DataList instance
+function druid__data_list.set_use_cache(self, is_use_cache) end
+
---@class druid.drag : druid.base_component
---@field can_x boolean Is drag component process vertical dragging.
@@ -374,8 +380,8 @@ function druid__data_list.set_data(self, data) end
---@field is_drag boolean Is component now dragging
---@field is_touch boolean Is component now touching
---@field node node Drag node
----@field on_drag druid.event on drag progress callback(self, dx, dy, total_x, total_y)
----@field on_drag_end druid.event Event on drag end callback(self, total_x, total_y)
+---@field on_drag druid.event on drag progress callback(self, dx, dy, total_x, total_y, touch)
+---@field on_drag_end druid.event Event on drag end callback(self, total_x, total_y, touch)
---@field on_drag_start druid.event Event on drag start callback(self, touch)
---@field on_touch_end druid.event Event on touch end callback(self)
---@field on_touch_start druid.event Event on touch start callback(self)
@@ -559,7 +565,8 @@ function druid__event.unsubscribe(self, callback, callback_context) end
---@field button druid.button Button component from click_node
---@field click_node node|nil Button trigger node
---@field node node Visual node
----@field on_change_state druid.event On change state callback(self, state)
+---@field on_hotkey_pressed druid.event On hotkey released callback(self, argument)
+---@field on_hotkey_released druid.event On hotkey released callback(self, argument)
---@field style druid.hotkey.style Component style params.
local druid__hotkey = {}
@@ -803,67 +810,8 @@ function druid__lang_text.translate(self, locale_id, a, b, c, d, e, f, g) end
---@class druid.layout : druid.base_component
---@field mode string Current layout mode
---@field node node Layout node
----@field on_size_changed druid.event On window resize callback(self, new_size)
local druid__layout = {}
---- Set node for layout node to fit inside it.
---- Pass nil to reset
----@param self druid.layout @{Layout}
----@param node node|nil
----@return druid.layout @{Layout}
-function druid__layout.fit_into_node(self, node) end
-
---- Set size for layout node to fit inside it
----@param self druid.layout @{Layout}
----@param target_size vector3
----@return druid.layout @{Layout}
-function druid__layout.fit_into_size(self, target_size) end
-
---- Set current size for layout node to fit inside it
----@param self druid.layout @{Layout}
----@return druid.layout @{Layout}
-function druid__layout.fit_into_window(self) end
-
---- The @{Layout} constructor
----@param self druid.layout @{Layout}
----@param node node Gui node
----@param mode string The layout mode (from const.LAYOUT_MODE)
----@param on_size_changed_callback function|nil The callback on window resize
-function druid__layout.init(self, node, mode, on_size_changed_callback) end
-
---- Set max gui upscale for FIT adjust mode (or side).
---- It happens on bigger render gui screen
----@param self druid.layout @{Layout}
----@param max_gui_upscale number
----@return druid.layout @{Layout}
-function druid__layout.set_max_gui_upscale(self, max_gui_upscale) end
-
---- Set maximum size of layout node
----@param self druid.layout @{Layout}
----@param max_size vector3
----@return druid.layout @{Layout}
-function druid__layout.set_max_size(self, max_size) end
-
---- Set minimal size of layout node
----@param self druid.layout @{Layout}
----@param min_size vector3
----@return druid.layout @{Layout}
-function druid__layout.set_min_size(self, min_size) end
-
---- Set new origin position of layout node.
---- You should apply this on node movement
----@param self druid.layout @{Layout}
----@param new_origin_position vector3
----@return druid.layout @{Layout}
-function druid__layout.set_origin_position(self, new_origin_position) end
-
---- Set new origin size of layout node.
---- You should apply this on node manual size change
----@param self druid.layout @{Layout}
----@param new_origin_size vector3
----@return druid.layout @{Layout}
-function druid__layout.set_origin_size(self, new_origin_size) end
-
---@class druid.pin_knob : druid.base_component
---@field druid druid_instance The component druid instance
@@ -1056,9 +1004,9 @@ function druid__rich_text.get_words() end
--- The @{RichText} constructor
---@param self druid.rich_text @{RichText}
----@param template string The Rich Text template name
----@param nodes table The node table, if prefab was copied by gui.clone_tree()
-function druid__rich_text.init(self, template, nodes) end
+---@param text_node node|string The text node to make Rich Text
+---@param value string|nil The initial text value. Default will be gui.get_text(text_node)
+function druid__rich_text.init(self, text_node, value) end
--- Set text for Rich Text
---@param self druid.rich_text @{RichText}
@@ -1097,6 +1045,7 @@ local druid__rich_text__style = {}
---@field style druid.scroll.style Component style params.
---@field target_position vector3 Current scroll target position
---@field view_node node Scroll view node
+---@field view_size vector3 Scroll view size
local druid__scroll = {}
--- Bind the grid component (Static or Dynamic) to recalculate scroll size on grid changes
@@ -1205,6 +1154,10 @@ function druid__scroll.set_vertical_scroll(self, state) end
---@return druid.scroll Current scroll instance
function druid__scroll.set_view_size(self, size) end
+--- Refresh scroll view size
+---@param self druid.scroll @{Scroll}
+function druid__scroll.update_view_size(self) end
+
---@class druid.scroll.style
---@field ANIM_SPEED number|nil Scroll gui.animation speed for scroll_to function. Default: 2
@@ -1550,7 +1503,7 @@ local druid__timer = {}
--- The @{Timer} constructor
---@param self druid.timer @{Timer}
---@param node node Gui text node
----@param seconds_from number Start timer value in seconds
+---@param seconds_from number|nil Start timer value in seconds
---@param seconds_to number|nil End timer value in seconds
---@param callback function|nil Function on timer end
function druid__timer.init(self, node, seconds_from, seconds_to, callback) end
@@ -1579,14 +1532,6 @@ local druid_instance = {}
---@param self druid_instance
function druid_instance.final(self) end
---- Create new component.
----@generic T
----@param self druid_instance
----@param component T Component module
----@param ... any Other component params to pass it to component:init function
----@return T Component instance
-function druid_instance.new(self, component, ...) end
-
--- Create @{BackHandler} component
---@param self druid_instance
---@param callback function|nil @The callback(self, custom_args) to call on back event
@@ -1683,9 +1628,8 @@ function druid_instance.new_lang_text(self, node, locale_id, adjust_type) end
---@param self druid_instance
---@param node string|node The_node id or gui.get_node(node_id).
---@param mode string The layout mode
----@param on_size_changed_callback function|nil The callback on window resize
---@return druid.layout @{Layout} component
-function druid_instance.new_layout(self, node, mode, on_size_changed_callback) end
+function druid_instance.new_layout(self, node, mode) end
--- Create @{Progress} component
---@param self druid_instance
@@ -1752,7 +1696,7 @@ function druid_instance.new_text(self, node, value, no_adjust) end
--- Create @{Timer} component
---@param self druid_instance
---@param node string|node Gui text node
----@param seconds_from number|nil Start timer value in seconds
+---@param seconds_from number Start timer value in seconds
---@param seconds_to number|nil End timer value in seconds
---@param callback function|nil Function on timer end
---@return druid.timer @{Timer} component
@@ -2021,3 +1965,22 @@ function helper.table_to_string(t) end
---@field height number
---@field max_ascent number
---@field max_descent number
+
+---@class utf8
+---@field len fun(string: string): number
+---@field sub fun(string: string, i: number, j: number): string
+---@field gmatch fun(string: string, pattern: string): fun(): string
+---@field gsub fun(string: string, pattern: string, repl: string, n: number): string
+---@field char fun(...: number): string
+---@field byte fun(string: string, i: number, j: number): number
+
+
+---Add generics to some functions.
+
+---Create new component.
+---@generic T: druid.base_component
+---@param self druid_instance
+---@param component T Component module
+---@param ... any Other component params to pass it to component:init function
+---@return T Component instance
+function druid_instance.new(self, component, ...) end
\ No newline at end of file
diff --git a/druid/base/button.lua b/druid/base/button.lua
index b08d897f..ada2a26f 100755
--- a/druid/base/button.lua
+++ b/druid/base/button.lua
@@ -344,10 +344,6 @@ function Button.on_input(self, action_id, action)
return false
end
- if not self:is_enabled() then
- return false
- end
-
local is_consume = true
local is_pick = true
local is_key_trigger = (action_id == self.key_trigger)
diff --git a/druid/base/drag.lua b/druid/base/drag.lua
index 3cae2b9e..fd791058 100644
--- a/druid/base/drag.lua
+++ b/druid/base/drag.lua
@@ -22,10 +22,10 @@
--- Event on drag start callback(self, touch)
-- @tfield DruidEvent on_drag_start @{DruidEvent}
---- on drag progress callback(self, dx, dy, total_x, total_y)
+--- on drag progress callback(self, dx, dy, total_x, total_y, touch)
-- @tfield DruidEvent on_drag Event @{DruidEvent}
---- Event on drag end callback(self, total_x, total_y)
+--- Event on drag end callback(self, total_x, total_y, touch)
-- @tfield DruidEvent on_drag_end @{DruidEvent}
--- Is component now touching
diff --git a/druid/base/scroll.lua b/druid/base/scroll.lua
index 58ba1b56..7a806203 100755
--- a/druid/base/scroll.lua
+++ b/druid/base/scroll.lua
@@ -50,6 +50,9 @@
--- Scroll view node
-- @tfield node view_node
+--- Scroll view size
+-- @tfield vector3 view_size
+
--- Scroll content node
-- @tfield node content_node
diff --git a/druid/base/text.lua b/druid/base/text.lua
index 3aaa063b..39188f44 100755
--- a/druid/base/text.lua
+++ b/druid/base/text.lua
@@ -211,6 +211,9 @@ local function update_text_with_trim(self, trim_postfix)
text_length = text_length - 1
new_text = utf8.sub(self.last_value, 1, text_length)
text_width = self:get_text_size(new_text .. trim_postfix)
+ if text_length == 0 then
+ break
+ end
end
gui.set_text(self.node, new_text .. trim_postfix)
diff --git a/druid/custom/rich_text/module/rt.lua b/druid/custom/rich_text/module/rt.lua
index dc27bf5b..74aa6791 100755
--- a/druid/custom/rich_text/module/rt.lua
+++ b/druid/custom/rich_text/module/rt.lua
@@ -227,7 +227,7 @@ function M._fill_properties(word, metrics, settings)
else
-- Text properties
word.scale = settings.scale * word.relative_scale * settings.adjust_scale
- word.pivot = gui.PIVOT_W -- With this pivot adjustments works correctly, but with another some misalignment
+ word.pivot = gui.PIVOT_SW -- With this pivot adjustments works more correctly than with other pivots
word.size = vmath.vector3(metrics.width, metrics.height, 0)
word.offset = vmath.vector3(metrics.offset_x, metrics.offset_y, 0)
end
diff --git a/druid/custom/rich_text/rich_text.gui b/druid/custom/rich_text/rich_text.gui
deleted file mode 100644
index 0d6a8fe8..00000000
--- a/druid/custom/rich_text/rich_text.gui
+++ /dev/null
@@ -1,45 +0,0 @@
-fonts {
- name: "game"
- font: "/example/assets/fonts/game.font"
-}
-textures {
- name: "items"
- texture: "/example/assets/images/kenney.atlas"
-}
-nodes {
- size {
- x: 400.0
- y: 100.0
- }
- type: TYPE_BOX
- id: "root"
- inherit_alpha: true
- visible: false
-}
-nodes {
- position {
- x: -200.0
- }
- size {
- x: 400.0
- y: 100.0
- }
- type: TYPE_TEXT
- text: "Rich text"
- font: "game"
- id: "text_prefab"
- pivot: PIVOT_W
- parent: "root"
- inherit_alpha: true
- outline_alpha: 0.0
- shadow_alpha: 0.0
-}
-nodes {
- type: TYPE_BOX
- id: "icon_prefab"
- parent: "root"
- inherit_alpha: true
- size_mode: SIZE_MODE_AUTO
-}
-material: "/builtins/materials/gui.material"
-adjust_reference: ADJUST_REFERENCE_PARENT
diff --git a/druid/custom/rich_text/rich_text.lua b/druid/custom/rich_text/rich_text.lua
index d4f08f9e..29bb0987 100644
--- a/druid/custom/rich_text/rich_text.lua
+++ b/druid/custom/rich_text/rich_text.lua
@@ -109,8 +109,8 @@ local RichText = component.create("rich_text")
--- The @{RichText} constructor
-- @tparam RichText self @{RichText}
--- @tparam string template The Rich Text template name
--- @tparam table nodes The node table, if prefab was copied by gui.clone_tree()
+-- @tparam node|string text_node The text node to make Rich Text
+-- @tparam string|nil value The initial text value. Default will be gui.get_text(text_node)
function RichText.init(self, text_node, value)
self.root = self:get_node(text_node)
self.text_prefab = self.root
diff --git a/druid/editor_scripts/druid.editor_script b/druid/editor_scripts/druid.editor_script
index 6534c1dd..f72488a1 100644
--- a/druid/editor_scripts/druid.editor_script
+++ b/druid/editor_scripts/druid.editor_script
@@ -25,35 +25,6 @@ end
function M.get_commands()
return {
- {
- label = "Print GUI Scheme",
-
- locations = { "Outline" },
-
- query = {
- selection = {type = "outline", cardinality = "many"}
- },
-
- active = function(opts)
- return true
- end,
-
- run = function(opts)
- print("local SCHEME = {")
-
- for i = 1, #opts.selection do
- local file = opts.selection[i]
- if editor.can_get(file, "id") then
- local id = editor.get(file, "id")
- print("\t" .. string.upper(id) .. " = \"" .. id .. "\",")
- end
- end
-
- print("}")
- print("")
- end
- },
-
{
label = "Assign layers",
diff --git a/druid/extended/data_list.lua b/druid/extended/data_list.lua
index fa9adcaa..17aba867 100644
--- a/druid/extended/data_list.lua
+++ b/druid/extended/data_list.lua
@@ -84,6 +84,8 @@ end
--- Set refresh function for DataList component
-- @tparam DataList self @{DataList}
+-- @tparam boolean is_use_cache Use cache version of DataList. Requires make setup of components in on_element_add callback and clean in on_element_remove
+-- @treturn druid.data_list Current DataList instance
function DataList.set_use_cache(self, is_use_cache)
self._is_use_cache = is_use_cache
return self
@@ -280,7 +282,7 @@ end
function DataList._refresh(self)
self.scroll:set_size(self.grid:get_size_for(#self._data))
- local start_pos = -self.scroll.position
+ local start_pos = -self.scroll.position --[[@as vector3]]
local start_index = self.grid:get_index(start_pos)
start_index = math.max(1, start_index)
diff --git a/druid/extended/figma_layout.lua b/druid/extended/figma_layout.lua
deleted file mode 100644
index 0748a4e1..00000000
--- a/druid/extended/figma_layout.lua
+++ /dev/null
@@ -1,392 +0,0 @@
-local helper = require("druid.helper")
-local component = require("druid.component")
-
----@class druid.figma_layout.row_data
----@field width number
----@field height number
----@field count number
-
----@class druid.figma_layout.rows_data
----@field total_width number
----@field total_height number
----@field nodes_width table
----@field nodes_height table
----@field rows druid.figma_layout.row_data[]>
-
----@class druid.figma_layout: druid.base_component
-local M = component.create("layout")
-
-
---- The @{Layout} constructor
--- @tparam Layout self @{Layout}
--- @tparam node node Gui node
--- @tparam string layout_type The layout mode (from const.LAYOUT_MODE)
--- @tparam function|nil on_size_changed_callback The callback on window resize
-function M:init(node, layout_type)
- self.node = self:get_node(node)
-
- print(self.node)
- self.is_dirty = true
- self.entities = {}
- self.margin = { x = 0, y = 0 }
- self.padding = gui.get_slice9(self.node)
- self.type = layout_type or "horizontal"
- self.is_resize_width = false
- self.is_resize_height = false
- self.is_justify = false
-end
-
-
-function M:update()
- if not self.is_dirty then
- return
- end
-
- self:refresh_layout()
-end
-
-
----@param margin_x number|nil
----@param margin_y number|nil
----@return druid.figma_layout
-function M:set_margin(margin_x, margin_y)
- self.margin.x = margin_x or self.margin.x
- self.margin.y = margin_y or self.margin.y
- self.is_dirty = true
-
- return self
-end
-
-
----@param padding vector4 The vector4 with padding values, where x - left, y - top, z - right, w - bottom
-function M:set_padding(padding)
- self.padding = padding
- self.is_dirty = true
-
- return self
-end
-
-
-function M:set_dirty()
- self.is_dirty = true
-
- return self
-end
-
-
----@param is_justify boolean
----@return druid.figma_layout
-function M:set_justify(is_justify)
- self.is_justify = is_justify
- self.is_dirty = true
-
- return self
-end
-
-
----@param type string The layout type: "horizontal", "vertical", "horizontal_wrap"
-function M:set_type(type)
- self.type = type
- self.is_dirty = true
-
- return self
-end
-
-
-
----@param is_hug_width boolean
----@param is_hug_height boolean
----@return druid.figma_layout
-function M:set_hug_content(is_hug_width, is_hug_height)
- self.is_resize_width = is_hug_width or false
- self.is_resize_height = is_hug_height or false
- self.is_dirty = true
-
- return self
-end
-
----@param node_or_node_id string|node
----@return druid.figma_layout
-function M:add(node_or_node_id)
- -- Acquire node from entity or by id
- local node = node_or_node_id
- if type(node_or_node_id) == "table" then
- assert(node_or_node_id.node, "The entity should have a node")
- node = node_or_node_id.node
- else
- ---@cast node_or_node_id string|node
- node = self:get_node(node_or_node_id)
- end
-
- ---@cast node node
- table.insert(self.entities, node)
- gui.set_parent(node, self.node)
-
- self.is_dirty = true
-
- return self
-end
-
-
----@return druid.figma_layout
-function M:refresh_layout()
- local layout_node = self.node
-
- local entities = self.entities
- local type = self.type -- vertical, horizontal, horizontal_wrap
- local margin = self.margin -- {x: horizontal, y: vertical} in pixels, between elements
- local padding = self.padding -- {x: left, y: top, z: right, w: bottom} in pixels
- local is_justify = self.is_justify
- local size = gui.get_size(layout_node)
- local max_width = size.x - padding.x - padding.z
- local max_height = size.y - padding.y - padding.w
- local layout_pivot_offset = helper.get_pivot_offset(gui.get_pivot(layout_node)) -- {x: -0.5, y: -0.5} - is left bot, {x: 0.5, y: 0.5} - is right top
-
- local rows_data = self:calculate_rows_data()
- local rows = rows_data.rows
- local row_index = 1
- local row = rows[row_index]
-
- -- Current x and Current y is a top left corner of the node
- local current_x = -row.width * (0.5 + layout_pivot_offset.x)
- local current_y = rows_data.total_height * (0.5 - layout_pivot_offset.y)
-
- if is_justify then
- if (type == "horizontal" or type == "horizontal_wrap") and row.count > 1 then
- current_x = -max_width * (0.5 + layout_pivot_offset.x)
- end
- if type == "vertical" then
- current_y = max_height * (0.5 - layout_pivot_offset.y)
- end
- end
-
- for index = 1, #entities do
- local node = entities[index]
- local node_width = rows_data.nodes_width[node]
- local node_height = rows_data.nodes_height[node]
- local pivot_offset = helper.get_pivot_offset(gui.get_pivot(node))
-
- if node_width > 0 and node_height > 0 then
- -- Calculate position for current node
- local position_x, position_y
-
- if type == "horizontal" then
- position_x = current_x + node_width * (0.5 + pivot_offset.x)
- position_y = current_y - row.height * (0.5 - pivot_offset.y)
-
- local node_margin = margin.x
- if is_justify and row.count > 1 then
- node_margin = (max_width - row.width) / (row.count - 1) + margin.x
- end
- current_x = current_x + node_width + node_margin
- end
-
- if type == "vertical" then
- position_x = current_x + row.width * (0.5 - pivot_offset.x)
- position_y = current_y - node_height * (0.5 + pivot_offset.y)
-
- local node_margin = margin.y
- if is_justify then
- node_margin = (max_height - rows_data.total_height) / (#rows - 1) + margin.y
- end
-
- current_y = current_y - node_height - node_margin
- end
-
- if type == "horizontal_wrap" then
- local width = row.width
- if is_justify and row.count > 0 then
- width = math.max(row.width, max_width)
- end
- local new_row_width = width * (0.5 - layout_pivot_offset.x)
-
- -- Compare with eps due the float loss and element flickering
- if current_x + node_width - new_row_width > 0.0001 then
- if row_index < #rows then
- row_index = row_index + 1
- row = rows[row_index]
- end
-
- current_x = -row.width * (0.5 + layout_pivot_offset.x)
- current_y = current_y - row.height - margin.y
- if is_justify and row.count > 1 then
- current_x = -max_width * (0.5 + layout_pivot_offset.x)
- end
- end
-
- position_x = current_x + node_width * (0.5 + pivot_offset.x)
- position_y = current_y - row.height * (0.5 - pivot_offset.y)
-
- local node_margin = margin.x
- if is_justify and row.count > 1 then
- node_margin = (max_width - row.width) / (row.count - 1) + margin.x
- end
- current_x = current_x + node_width + node_margin
- end
-
- do -- Padding offset
- if layout_pivot_offset.x == -0.5 then
- position_x = position_x + padding.x
- end
- if layout_pivot_offset.y == 0.5 then
- position_y = position_y - padding.y
- end
- if layout_pivot_offset.x == 0.5 then
- position_x = position_x - padding.z
- end
- if layout_pivot_offset.y == -0.5 then
- position_y = position_y + padding.w
- end
- end
-
- self:set_node_position(node, position_x, position_y)
- end
- end
-
- if self.is_resize_width or self.is_resize_height then
- if self.is_resize_width then
- size.x = rows_data.total_width + padding.x + padding.z
- end
- if self.is_resize_height then
- size.y = rows_data.total_height + padding.y + padding.w
- end
- gui.set_size(layout_node, size)
- end
-
- self.is_dirty = false
-
- return self
-end
-
-
----@return druid.figma_layout
-function M:clear_layout()
- for index = #self.entities, 1, -1 do
- self.entities[index] = nil
- end
-
- self.is_dirty = true
-
- return self
-end
-
-
----@param node node
----@return number, number
-function M.get_node_size(node)
- if not gui.is_enabled(node, false) then
- return 0, 0
- end
-
- local scale = gui.get_scale(node)
-
- -- If node has text - get text size instead of node size
- if gui.get_text(node) then
- local text_metrics = helper.get_text_metrics_from_node(node)
- return text_metrics.width * scale.x, text_metrics.height * scale.y
- end
-
- local size = gui.get_size(node)
- return size.x * scale.x, size.y * scale.y
-end
-
-
----Calculate rows data for layout. Contains total width, height and rows info (width, height, count of elements in row)
----@private
----@return druid.figma_layout.rows_data
-function M:calculate_rows_data()
- local entities = self.entities
- local margin = self.margin
- local type = self.type
- local padding = self.padding
-
- local size = gui.get_size(self.node)
- local max_width = size.x - padding.x - padding.z
-
- -- Collect rows info about width, height and count of elements in row
- local current_row = { width = 0, height = 0, count = 0 }
- local rows_data = {
- total_width = 0,
- total_height = 0,
- nodes_width = {},
- nodes_height = {},
- rows = { current_row }
- }
-
- for index = 1, #entities do
- local node = entities[index]
- local node_width = rows_data.nodes_width[node]
- local node_height = rows_data.nodes_height[node]
-
- -- Get node size if it's not calculated yet
- if not node_width or not node_height then
- node_width, node_height = M.get_node_size(node)
- rows_data.nodes_width[node] = node_width
- rows_data.nodes_height[node] = node_height
- end
-
- if node_width > 0 and node_height > 0 then
- if type == "horizontal" then
- current_row.width = current_row.width + node_width + margin.x
- current_row.height = math.max(current_row.height, node_height)
- current_row.count = current_row.count + 1
- end
-
- if type == "vertical" then
- if current_row.count > 0 then
- current_row = { width = 0, height = 0, count = 0 }
- table.insert(rows_data.rows, current_row)
- end
-
- current_row.width = math.max(current_row.width, node_width + margin.x)
- current_row.height = node_height
- current_row.count = current_row.count + 1
- end
-
- if type == "horizontal_wrap" then
- if current_row.width + node_width > max_width and current_row.count > 0 then
- current_row = { width = 0, height = 0, count = 0 }
- table.insert(rows_data.rows, current_row)
- end
-
- current_row.width = current_row.width + node_width + margin.x
- current_row.height = math.max(current_row.height, node_height)
- current_row.count = current_row.count + 1
- end
- end
- end
-
- -- Remove last margin of each row
- -- Calculate total width and height
- local rows_count = #rows_data.rows
- for index = 1, rows_count do
- local row = rows_data.rows[index]
- if row.width > 0 then
- row.width = row.width - margin.x
- end
-
- rows_data.total_width = math.max(rows_data.total_width, row.width)
- rows_data.total_height = rows_data.total_height + row.height
- end
-
- rows_data.total_height = rows_data.total_height + margin.y * (rows_count - 1)
- return rows_data
-end
-
-
----@private
----@param node node
----@param x number
----@param y number
----@return node
-function M:set_node_position(node, x, y)
- local position = gui.get_position(node)
- position.x = x
- position.y = y
- gui.set_position(node, position)
-
- return node
-end
-
-
-return M
\ No newline at end of file
diff --git a/druid/extended/hotkey.lua b/druid/extended/hotkey.lua
index 283839c4..41507bd7 100644
--- a/druid/extended/hotkey.lua
+++ b/druid/extended/hotkey.lua
@@ -27,7 +27,6 @@
local helper = require("druid.helper")
local component = require("druid.component")
local Event = require("druid.event")
-local const = require("druid.const")
local Hotkey = component.create("hotkey")
diff --git a/druid/extended/layout.lua b/druid/extended/layout.lua
index ff24fefa..d8ac7327 100644
--- a/druid/extended/layout.lua
+++ b/druid/extended/layout.lua
@@ -1,4 +1,4 @@
--- Copyright (c) 2021 Maksim Tuprikov . This code is licensed under MIT license
+-- Copyright (c) 2024 Maksim Tuprikov . This code is licensed under MIT license
--- Layout management on node
--
@@ -13,205 +13,406 @@
--- Current layout mode
-- @tfield string mode
----On window resize callback(self, new_size)
--- @tfield DruidEvent on_size_changed @{DruidEvent}
-
---
-
-local const = require("druid.const")
local helper = require("druid.helper")
local component = require("druid.component")
-local Event = require("druid.event")
+-- @class druid.layout.row_data
+-- @tfield width number
+-- @tfield height number
+-- @tfield count number
-local Layout = component.create("layout")
+-- @class druid.layout.rows_data
+-- @tfield total_width number
+-- @tfield total_height number
+-- @tfield nodes_width table
+-- @tfield nodes_height table
+-- @tfield rows druid.layout.row_data[]>
+-- @class druid.layout: druid.base_component
+local M = component.create("layout")
---- The @{Layout} constructor
+-- The @{Layout} constructor
-- @tparam Layout self @{Layout}
-- @tparam node node Gui node
--- @tparam string mode The layout mode (from const.LAYOUT_MODE)
+-- @tparam string layout_type The layout mode (from const.LAYOUT_MODE)
-- @tparam function|nil on_size_changed_callback The callback on window resize
-function Layout.init(self, node, mode, on_size_changed_callback)
+function M.init(self, node, layout_type)
self.node = self:get_node(node)
- self._min_size = nil
- self._max_size = nil
- self._current_size = vmath.vector3(0)
- self._inited = false
- self._max_gui_upscale = nil
- self._fit_node = nil
-
- self._anchors = {}
-
- self.mode = mode or const.LAYOUT_MODE.FIT
-
- self.on_size_changed = Event(on_size_changed_callback)
-end
-
-
-function Layout.on_late_init(self)
- self._inited = true
- self.origin_size = self.origin_size or gui.get_size(self.node)
- self.fit_size = self.fit_size or vmath.vector3(self.origin_size)
- self.pivot = helper.get_pivot_offset(gui.get_pivot(self.node))
- self.origin_position = gui.get_position(self.node)
- self.position = vmath.vector3(self.origin_position)
- gui.set_size_mode(self.node, gui.SIZE_MODE_MANUAL)
- gui.set_adjust_mode(self.node, gui.ADJUST_FIT)
- self:on_window_resized()
+ self.is_dirty = true
+ self.entities = {}
+ self.margin = { x = 0, y = 0 }
+ self.padding = gui.get_slice9(self.node)
+ self.type = layout_type or "horizontal"
+ self.is_resize_width = false
+ self.is_resize_height = false
+ self.is_justify = false
end
-
-function Layout.on_window_resized(self)
- if not self._inited then
+function M:update()
+ if not self.is_dirty then
return
end
- local x_koef, y_koef = helper.get_screen_aspect_koef()
-
- local revert_scale = 1
- if self._max_gui_upscale then
- revert_scale = self._max_gui_upscale / helper.get_gui_scale()
- revert_scale = math.min(revert_scale, 1)
- end
- gui.set_scale(self.node, vmath.vector3(revert_scale))
-
- if self._fit_node then
- self.fit_size = gui.get_size(self._fit_node)
- self.fit_size.x = self.fit_size.x / x_koef
- self.fit_size.y = self.fit_size.y / y_koef
- end
+ self:refresh_layout()
+end
- x_koef = self.fit_size.x / self.origin_size.x * x_koef
- y_koef = self.fit_size.y / self.origin_size.y * y_koef
- local new_size = vmath.vector3(self.origin_size)
+-- @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)
+ self.margin.x = margin_x or self.margin.x
+ self.margin.y = margin_y or self.margin.y
+ self.is_dirty = true
- if self.mode == const.LAYOUT_MODE.STRETCH then
- new_size.x = new_size.x * x_koef / revert_scale
- new_size.y = new_size.y * y_koef / revert_scale
- end
+ return self
+end
- if self.mode == const.LAYOUT_MODE.STRETCH_X then
- new_size.x = new_size.x * x_koef / revert_scale
- end
- if self.mode == const.LAYOUT_MODE.STRETCH_Y then
- new_size.y = new_size.y * y_koef / revert_scale
- end
+-- @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)
+ self.padding = padding
+ self.is_dirty = true
- -- Fit to the stretched container (node size or other defined)
- if self.mode == const.LAYOUT_MODE.ZOOM_MIN then
- new_size = new_size * math.min(x_koef, y_koef)
- end
- if self.mode == const.LAYOUT_MODE.ZOOM_MAX then
- new_size = new_size * math.max(x_koef, y_koef)
- end
+ return self
+end
- if self._min_size then
- new_size.x = math.max(new_size.x, self._min_size.x)
- new_size.y = math.max(new_size.y, self._min_size.y)
- end
- if self._max_size then
- new_size.x = math.min(new_size.x, self._max_size.x)
- new_size.y = math.min(new_size.y, self._max_size.y)
- end
- self._current_size = new_size
- gui.set_size(self.node, new_size)
- self.position.x = self.origin_position.x + self.origin_position.x * (x_koef - 1)
- self.position.y = self.origin_position.y + self.origin_position.y * (y_koef - 1)
- gui.set_position(self.node, self.position)
+-- @tparam Layout self @{Layout}
+-- @treturn druid.layout @{Layout}
+function M.set_dirty(self)
+ self.is_dirty = true
- self.on_size_changed:trigger(self:get_context(), new_size)
+ return self
end
---- Set minimal size of layout node
-- @tparam Layout self @{Layout}
--- @tparam vector3 min_size
--- @treturn Layout @{Layout}
-function Layout.set_min_size(self, min_size)
- self._min_size = min_size
+-- @tparam boolean is_justify
+-- @treturn druid.layout @{Layout}
+function M.set_justify(self, is_justify)
+ self.is_justify = is_justify
+ self.is_dirty = true
+
return self
end
---- Set maximum size of layout node
-- @tparam Layout self @{Layout}
--- @tparam vector3 max_size
--- @treturn Layout @{Layout}
-function Layout.set_max_size(self, max_size)
- self._max_size = max_size
+-- @tparam string type The layout type: "horizontal", "vertical", "horizontal_wrap"
+-- @treturn druid.layout @{Layout}
+function M.set_type(self, type)
+ self.type = type
+ self.is_dirty = true
+
return self
end
---- Set new origin position of layout node. You should apply this on node movement
-- @tparam Layout self @{Layout}
--- @tparam vector3 new_origin_position
--- @treturn Layout @{Layout}
-function Layout.set_origin_position(self, new_origin_position)
- self.origin_position = new_origin_position or self.origin_position
- self:on_window_resized()
+-- @tparam boolean is_hug_width
+-- @tparam boolean is_hug_height
+-- @treturn druid.layout @{Layout}
+function M.set_hug_content(self, is_hug_width, is_hug_height)
+ self.is_resize_width = is_hug_width or false
+ self.is_resize_height = is_hug_height or false
+ self.is_dirty = true
+
return self
end
---- Set new origin size of layout node. You should apply this on node manual size change
-- @tparam Layout self @{Layout}
--- @tparam vector3 new_origin_size
--- @treturn Layout @{Layout}
-function Layout.set_origin_size(self, new_origin_size)
- self.origin_size = new_origin_size or self.origin_size
- self:on_window_resized()
+-- @tparam string|node node_or_node_id
+-- @treturn druid.layout @{Layout}
+function M.add(self, node_or_node_id)
+ -- Acquire node from entity or by id
+ local node = node_or_node_id
+ if type(node_or_node_id) == "table" then
+ assert(node_or_node_id.node, "The entity should have a node")
+ node = node_or_node_id.node
+ else
+ -- @cast node_or_node_id string|node
+ node = self:get_node(node_or_node_id)
+ end
+
+ -- @cast node node
+ table.insert(self.entities, node)
+ gui.set_parent(node, self.node)
+
+ self.is_dirty = true
+
return self
end
---- Set max gui upscale for FIT adjust mode (or side). It happens on bigger render gui screen
-- @tparam Layout self @{Layout}
--- @tparam number max_gui_upscale
--- @treturn Layout @{Layout}
-function Layout.set_max_gui_upscale(self, max_gui_upscale)
- self._max_gui_upscale = max_gui_upscale
- self:on_window_resized()
-end
+-- @treturn druid.layout @{Layout}
+function M.refresh_layout(self)
+ local layout_node = self.node
+
+ local entities = self.entities
+ local type = self.type -- vertical, horizontal, horizontal_wrap
+ local margin = self.margin -- {x: horizontal, y: vertical} in pixels, between elements
+ local padding = self.padding -- {x: left, y: top, z: right, w: bottom} in pixels
+ local is_justify = self.is_justify
+ local size = gui.get_size(layout_node)
+ local max_width = size.x - padding.x - padding.z
+ local max_height = size.y - padding.y - padding.w
+ local layout_pivot_offset = helper.get_pivot_offset(gui.get_pivot(layout_node)) -- {x: -0.5, y: -0.5} - is left bot, {x: 0.5, y: 0.5} - is right top
+
+ local rows_data = self:calculate_rows_data()
+ local rows = rows_data.rows
+ local row_index = 1
+ local row = rows[row_index]
+
+ -- Current x and Current y is a top left corner of the node
+ local current_x = -row.width * (0.5 + layout_pivot_offset.x)
+ local current_y = rows_data.total_height * (0.5 - layout_pivot_offset.y)
+
+ if is_justify then
+ if (type == "horizontal" or type == "horizontal_wrap") and row.count > 1 then
+ current_x = -max_width * (0.5 + layout_pivot_offset.x)
+ end
+ if type == "vertical" then
+ current_y = max_height * (0.5 - layout_pivot_offset.y)
+ end
+ end
+ for index = 1, #entities do
+ local node = entities[index]
+ local node_width = rows_data.nodes_width[node]
+ local node_height = rows_data.nodes_height[node]
+ local pivot_offset = helper.get_pivot_offset(gui.get_pivot(node))
+
+ if node_width > 0 and node_height > 0 then
+ -- Calculate position for current node
+ local position_x, position_y
+
+ if type == "horizontal" then
+ position_x = current_x + node_width * (0.5 + pivot_offset.x)
+ position_y = current_y - row.height * (0.5 - pivot_offset.y)
+
+ local node_margin = margin.x
+ if is_justify and row.count > 1 then
+ node_margin = (max_width - row.width) / (row.count - 1) + margin.x
+ end
+ current_x = current_x + node_width + node_margin
+ end
+
+ if type == "vertical" then
+ position_x = current_x + row.width * (0.5 - pivot_offset.x)
+ position_y = current_y - node_height * (0.5 + pivot_offset.y)
+
+ local node_margin = margin.y
+ if is_justify then
+ node_margin = (max_height - rows_data.total_height) / (#rows - 1) + margin.y
+ end
+
+ current_y = current_y - node_height - node_margin
+ end
+
+ if type == "horizontal_wrap" then
+ local width = row.width
+ if is_justify and row.count > 0 then
+ width = math.max(row.width, max_width)
+ end
+ local new_row_width = width * (0.5 - layout_pivot_offset.x)
+
+ -- Compare with eps due the float loss and element flickering
+ if current_x + node_width - new_row_width > 0.0001 then
+ if row_index < #rows then
+ row_index = row_index + 1
+ row = rows[row_index]
+ end
+
+ current_x = -row.width * (0.5 + layout_pivot_offset.x)
+ current_y = current_y - row.height - margin.y
+ if is_justify and row.count > 1 then
+ current_x = -max_width * (0.5 + layout_pivot_offset.x)
+ end
+ end
+
+ position_x = current_x + node_width * (0.5 + pivot_offset.x)
+ position_y = current_y - row.height * (0.5 - pivot_offset.y)
+
+ local node_margin = margin.x
+ if is_justify and row.count > 1 then
+ node_margin = (max_width - row.width) / (row.count - 1) + margin.x
+ end
+ current_x = current_x + node_width + node_margin
+ end
+
+ do -- Padding offset
+ if layout_pivot_offset.x == -0.5 then
+ position_x = position_x + padding.x
+ end
+ if layout_pivot_offset.y == 0.5 then
+ position_y = position_y - padding.y
+ end
+ if layout_pivot_offset.x == 0.5 then
+ position_x = position_x - padding.z
+ end
+ if layout_pivot_offset.y == -0.5 then
+ position_y = position_y + padding.w
+ end
+ end
+
+ self:set_node_position(node, position_x, position_y)
+ end
+ end
+
+ if self.is_resize_width or self.is_resize_height then
+ if self.is_resize_width then
+ size.x = rows_data.total_width + padding.x + padding.z
+ end
+ if self.is_resize_height then
+ size.y = rows_data.total_height + padding.y + padding.w
+ end
+ gui.set_size(layout_node, size)
+ end
+
+ self.is_dirty = false
---- Set size for layout node to fit inside it
--- @tparam Layout self @{Layout}
--- @tparam vector3 target_size
--- @treturn Layout @{Layout}
-function Layout.fit_into_size(self, target_size)
- self.fit_size = target_size
- self:on_window_resized()
return self
end
---- Set node for layout node to fit inside it. Pass nil to reset
-- @tparam Layout self @{Layout}
--- @tparam node|nil node
--- @treturn Layout @{Layout}
-function Layout.fit_into_node(self, node)
- self._fit_node = node
- self:on_window_resized()
+-- @treturn druid.layout @{Layout}
+function M.clear_layout(self)
+ for index = #self.entities, 1, -1 do
+ self.entities[index] = nil
+ end
+
+ self.is_dirty = true
+
return self
end
---- Set current size for layout node to fit inside it
+-- @private
+-- @tparam node node
+-- @treturn number, number
+function M.get_node_size(node)
+ if not gui.is_enabled(node, false) then
+ return 0, 0
+ end
+
+ local scale = gui.get_scale(node)
+
+ -- If node has text - get text size instead of node size
+ if gui.get_text(node) then
+ local text_metrics = helper.get_text_metrics_from_node(node)
+ return text_metrics.width * scale.x, text_metrics.height * scale.y
+ end
+
+ local size = gui.get_size(node)
+ return size.x * scale.x, size.y * scale.y
+end
+
+
+-- @private
-- @tparam Layout self @{Layout}
--- @treturn Layout @{Layout}
-function Layout.fit_into_window(self)
- return self:fit_into_size(vmath.vector3(
- gui.get_width(),
- gui.get_height(),
- 0))
+-- Calculate rows data for layout. Contains total width, height and rows info (width, height, count of elements in row)
+-- @treturn druid.layout.rows_data
+function M.calculate_rows_data(self)
+ local entities = self.entities
+ local margin = self.margin
+ local type = self.type
+ local padding = self.padding
+
+ local size = gui.get_size(self.node)
+ local max_width = size.x - padding.x - padding.z
+
+ -- Collect rows info about width, height and count of elements in row
+ local current_row = { width = 0, height = 0, count = 0 }
+ local rows_data = {
+ total_width = 0,
+ total_height = 0,
+ nodes_width = {},
+ nodes_height = {},
+ rows = { current_row }
+ }
+
+ for index = 1, #entities do
+ local node = entities[index]
+ local node_width = rows_data.nodes_width[node]
+ local node_height = rows_data.nodes_height[node]
+
+ -- Get node size if it's not calculated yet
+ if not node_width or not node_height then
+ node_width, node_height = M.get_node_size(node)
+ rows_data.nodes_width[node] = node_width
+ rows_data.nodes_height[node] = node_height
+ end
+
+ if node_width > 0 and node_height > 0 then
+ if type == "horizontal" then
+ current_row.width = current_row.width + node_width + margin.x
+ current_row.height = math.max(current_row.height, node_height)
+ current_row.count = current_row.count + 1
+ end
+
+ if type == "vertical" then
+ if current_row.count > 0 then
+ current_row = { width = 0, height = 0, count = 0 }
+ table.insert(rows_data.rows, current_row)
+ end
+
+ current_row.width = math.max(current_row.width, node_width + margin.x)
+ current_row.height = node_height
+ current_row.count = current_row.count + 1
+ end
+
+ if type == "horizontal_wrap" then
+ if current_row.width + node_width > max_width and current_row.count > 0 then
+ current_row = { width = 0, height = 0, count = 0 }
+ table.insert(rows_data.rows, current_row)
+ end
+
+ current_row.width = current_row.width + node_width + margin.x
+ current_row.height = math.max(current_row.height, node_height)
+ current_row.count = current_row.count + 1
+ end
+ end
+ end
+
+ -- Remove last margin of each row
+ -- Calculate total width and height
+ local rows_count = #rows_data.rows
+ for index = 1, rows_count do
+ local row = rows_data.rows[index]
+ if row.width > 0 then
+ row.width = row.width - margin.x
+ end
+
+ rows_data.total_width = math.max(rows_data.total_width, row.width)
+ rows_data.total_height = rows_data.total_height + row.height
+ end
+
+ rows_data.total_height = rows_data.total_height + margin.y * (rows_count - 1)
+ return rows_data
end
+-- @private
+-- @tparam node node
+-- @tparam number x
+-- @tparam number y
+-- @treturn node
+function M:set_node_position(node, x, y)
+ local position = gui.get_position(node)
+ position.x = x
+ position.y = y
+ gui.set_position(node, position)
+
+ return node
+end
-return Layout
+return M
diff --git a/druid/extended/slider.lua b/druid/extended/slider.lua
index 22bd4e49..7af97d35 100644
--- a/druid/extended/slider.lua
+++ b/druid/extended/slider.lua
@@ -151,6 +151,8 @@ function Slider.on_input(self, action_id, action)
self.value = (self.target_pos.y - self.start_pos.y) / self.dist.y
end
+ self.value = math.abs(self.value)
+
if self.steps then
local closest_dist = 1000
local closest = nil
diff --git a/druid/styles/default/style.lua b/druid/styles/default/style.lua
index 2ce473e7..1a857b99 100644
--- a/druid/styles/default/style.lua
+++ b/druid/styles/default/style.lua
@@ -42,15 +42,15 @@ M["button"] = {
end,
on_click_disabled = function(self, node)
- settings.play_sound(M.button.BTN_SOUND_DISABLED)
+ local start_pos = self.start_pos
+ gui.animate(node, "position.x", start_pos.x - 3, gui.EASING_OUTSINE, 0.05, 0, function()
+ gui.animate(node, "position.x", start_pos.x + 3, gui.EASING_OUTSINE, 0.1, 0, function()
+ gui.animate(node, "position.x", start_pos.x, gui.EASING_OUTSINE, 0.05)
+ end)
+ end)
end,
on_set_enabled = function(self, node, state)
- if state then
- gui.set_color(node, M.button.ENABLED_COLOR)
- else
- gui.set_color(node, M.button.DISABLED_COLOR)
- end
end
}
diff --git a/druid/system/druid_instance.lua b/druid/system/druid_instance.lua
index 52a43c55..4f8f31bb 100755
--- a/druid/system/druid_instance.lua
+++ b/druid/system/druid_instance.lua
@@ -247,7 +247,7 @@ function DruidInstance.initialize(self, context, style)
end
---- Create new component.
+-- Create new component.
-- @tparam DruidInstance self
-- @tparam BaseComponent component Component module
-- @tparam any ... Other component params to pass it to component:init function
@@ -776,9 +776,8 @@ end
-- @tparam DruidInstance self
-- @tparam string|node node The_node id or gui.get_node(node_id).
-- @tparam string mode The layout mode
--- @tparam function|nil on_size_changed_callback The callback on window resize
-- @treturn Layout @{Layout} component
-function DruidInstance.new_layout(self, node, mode, on_size_changed_callback)
+function DruidInstance.new_layout(self, node, mode)
return helper.require_component_message("layout")
end
diff --git a/druid/system/utf8.lua b/druid/system/utf8.lua
index 7f139149..6c99fcf7 100644
--- a/druid/system/utf8.lua
+++ b/druid/system/utf8.lua
@@ -688,7 +688,8 @@ local function matcherGenerator(regex, plain)
local sum = 0
local bc, ec = utf8sub(str, 1, 1), utf8sub(str, 2, 2)
local skip = len(bc) + len(ec)
- bc, ec = utf8unicode(bc), utf8unicode(ec)
+ bc = utf8unicode(bc) --[[@as string]]
+ ec = utf8unicode(ec) --[[@as string]]
return function(cC)
if cC == ec and sum > 0 then
sum = sum - 1
diff --git a/druid/templates/component.template.lua b/druid/templates/component.template.lua
index df482777..d67bf32f 100644
--- a/druid/templates/component.template.lua
+++ b/druid/templates/component.template.lua
@@ -3,18 +3,13 @@ local component = require("druid.component")
---@class component_name : druid.base_component
local Component = component.create("component_name")
-local SCHEME = {
- ROOT = "root",
- BUTTON = "button",
-}
-
-- Component constructor. Template name and nodes are optional. Pass it if you use it in your component
function Component:init(template, nodes)
self.druid = self:get_druid(template, nodes)
- self.root = self:get_node(SCHEME.ROOT)
+ self.root = self:get_node("root")
- self.button = self.druid:new_button(SCHEME.BUTTON, function() end)
+ self.button = self.druid:new_button("button", function() end)
end
diff --git a/druid/templates/component_full.template.lua b/druid/templates/component_full.template.lua
index e4a2f7a9..1978cf2f 100644
--- a/druid/templates/component_full.template.lua
+++ b/druid/templates/component_full.template.lua
@@ -3,13 +3,6 @@ local component = require("druid.component")
---@class component_name : druid.base_component
local Component = component.create("component_name")
--- Scheme of component gui nodes
-local SCHEME = {
- ROOT = "root",
- BUTTON = "button",
-}
-
-
-- Component constructor. Template name and nodes are optional. Pass it if you use it in your component
function Component:init(template, nodes)
-- If your component is gui template, pass the template name and set it
@@ -18,7 +11,7 @@ function Component:init(template, nodes)
self.druid = self:get_druid(template, nodes)
-- self:get_node will auto process component template and nodes
- self.root = self:get_node(SCHEME.ROOT)
+ self.root = self:get_node("root")
end
diff --git a/example/examples/data_list/with_component/button_component/button_component.lua b/example/examples/data_list/with_component/button_component/button_component.lua
index 39c6c234..2609c8ef 100644
--- a/example/examples/data_list/with_component/button_component/button_component.lua
+++ b/example/examples/data_list/with_component/button_component/button_component.lua
@@ -14,24 +14,15 @@ local component = require("druid.component")
---@field druid druid_instance
local ButtonComponent = component.create("button_component")
-local SCHEME = {
- ROOT = "root",
- TEXT = "text",
- ICON = "icon",
- CHECKBOX = "checkbox"
-}
-
---@param template string
---@param nodes table
function ButtonComponent:init(template, nodes)
- self:set_template(template)
- self:set_nodes(nodes)
- self.druid = self:get_druid()
+ self.druid = self:get_druid(template, nodes)
- self.root = self:get_node(SCHEME.ROOT)
- self.text = self.druid:new_text(SCHEME.TEXT)
- self.checkbox = self:get_node(SCHEME.CHECKBOX)
+ self.root = self:get_node("root")
+ self.text = self.druid:new_text("text")
+ self.checkbox = self:get_node("checkbox")
self.button = self.druid:new_button(self.root, self._on_click)
diff --git a/utils/annotations_manual.lua b/utils/annotations_manual.lua
index cc258b29..a72a32e8 100644
--- a/utils/annotations_manual.lua
+++ b/utils/annotations_manual.lua
@@ -7,6 +7,8 @@
---@field height number
---@field offset_x number|nil
---@field offset_y number|nil
+---@field max_ascent number
+---@field max_descent number
---@field node_size vector3|nil @For images only
---@class druid.rich_text.lines_metrics
@@ -47,6 +49,7 @@
---@field parent node
---@field size number
---@field fonts table
+---@field scale vector3
---@field color vector4
---@field shadow vector4
---@field outline vector4
@@ -54,12 +57,9 @@
---@field image_pixel_grid_snap boolean
---@field combine_words boolean
---@field default_animation string
----@field node_prefab node
---@field text_prefab node
----@field text_scale vector3
---@field adjust_scale number
---@field default_texture string
----@field node_scale vector3
---@field is_multiline boolean
---@field text_leading number
---@field font hash
@@ -71,3 +71,22 @@
---@field height number
---@field max_ascent number
---@field max_descent number
+
+---@class utf8
+---@field len fun(string: string): number
+---@field sub fun(string: string, i: number, j: number): string
+---@field gmatch fun(string: string, pattern: string): fun(): string
+---@field gsub fun(string: string, pattern: string, repl: string, n: number): string
+---@field char fun(...: number): string
+---@field byte fun(string: string, i: number, j: number): number
+
+
+---Add generics to some functions.
+
+---Create new component.
+---@generic T: druid.base_component
+---@param self druid_instance
+---@param component T Component module
+---@param ... any Other component params to pass it to component:init function
+---@return T Component instance
+function druid_instance.new(self, component, ...) end
\ No newline at end of file