This commit is contained in:
Insality
2025-03-16 02:24:47 +02:00
parent a2bb93a03b
commit ad0447b649
16 changed files with 32 additions and 16 deletions

372
wiki/01-components.md Normal file
View File

@@ -0,0 +1,372 @@
# Druid components
## Button
[Button API here](https://insality.github.io/druid/modules/Button.html)
### Overview
Basic Druid input component. Handle input on node and provide different callbacks on touch events.
### Setup
Create button with druid: `button = druid:new_button(node_name, callback, [params], [animation_node])`
Where node name is name of node from GUI scene. You can use `node_name` as input trigger zone and point another node for animation via `animation_node`
### Usecase
_fill example usecases_
### Notes
- Button callback have next params: (self, params, button_instance)
- **self** - Druid self context
- **params** - Additional params, specified on button creating
- **button_instance** - button itself
- You can set _params_ on button callback on button creating: `druid:new_button("node_name", callback, params)`. This _params_ will pass in callback as second argument
- Button have next events:
- **on_click** - basic button callback
- **on_repeated_click** - repeated click callback, while holding the button, don't trigger if callback is empty
- **on_long_click** - callback on long button tap, don't trigger if callback is empty
- **on_hold_click** - hold callback, before long_click trigger, don't trigger if callback is empty
- **on_double_click** - different callback, if tap button 2+ in row, don't trigger if callback is empty
- Click event will not trigger, if between pressed and released state cursor was outside of node zone
- If button have double click event and it is triggered, usual callback will be not invoked
- If you have stencil on buttons and you don't want trigger them outside of stencil node, you can use `button:set_click_zone` to restrict button click zone
- Button can have key trigger to use then by key: `button:set_key_trigger`
- Animation node can be used for example to animate small icon on big panel. Node name of trigger zone will be `big panel` and animation node will be `small icon`
## Text
[Text API here](https://insality.github.io/druid/modules/Text.html)
### Overview
Basic Druid text component. Text components by default have the text size adjusting.
### Setup
Create text node with druid: `text = druid:new_text(node_name, [initial_value], [text_adjust_type])`
### Notes
- Text component by default have auto adjust text sizing. Text never will be bigger, than text node size, which you can setup in GUI scene. It can be disabled on component creating by settings argument `is_no_adjust` to _true_
![](../media/text_autosize.png)
- Text pivot can be changed with `text:set_pivot`, and text will save their position inside their text size box:
![](../media/text_anchor.gif)
- There is several text adjust types. Default Downscale. You can change the default adjust type in the text style table.
- **const.TEXT_ADJUST.DOWNSCALE** - Change text's scale to fit in the text node size
- **const.TEXT_ADJUST.TRIM** - Trim the text with postfix (default - "...", override in styles) to fit in the text node size
- **const.TEXT_ADJUST.NO_ADJUST** - No any adjust, like default Defold text node
- **const.TEXT_ADJUST.DOWNSCALE_LIMITED** - Change text's scale list downscale, but there is limit for text's scale
- **const.TEXT_ADJUST.SCROLL** - Change text's pivot to imitate scrolling in the text box. Use with stencil node for better effect.
- **const.TEXT_ADJUST.SCALE_THEN_SCROLL** - Combine two modes: first limited downscale, then scroll
## Blocker
[Blocker API here](https://insality.github.io/druid/modules/Blocker.html)
### Overview
Druid component for block input. Use it to block input in special zone.
### Setup
Create blocker component with druid: `druid:new_blocker(node_name)`
### Notes
Explanation:
![](../media/blocker_scheme.png)
Blue zone is **button** with close_window callback
Yellow zone is blocker with window content
So you can do the safe zones, when you have the big buttons
## Back Handler
[Back handler API here](https://insality.github.io/druid/modules/BackHandler.html)
### Overview
Component to handle back button. It handle Android back button and Backspace key. Key triggers in `input.binding` should be setup for correct working.
### Setup
Setup callback with `druid:new_back_handler(callback)`
### Notes
## Lang text
[Lang text API here](https://insality.github.io/druid/modules/LangText.html)
### Overview
Wrap on Text component to handle localization. It uses druid get_text_function to set text by it's id
### Setup
Create lang text component with druid `text = druid:new_lang_text(node_name, locale_id)`
### Notes
## Scroll
[Scroll API here](https://insality.github.io/druid/modules/Scroll.html)
### Overview
Basic Druid scroll component. Handle all scrolling stuff in druid GUI
### Setup
Create scroll component with druid: `scroll = druid:new_scroll(view_node, content_node)`.
_View_node_ - is static part. It capturing user input and recognize scrolling touches
_Content_node_ - is dynamic part. This node will change position by scroll system
Initial scroll size will be equal to _content_node_ node size. The initial view box will be equal to _view_node_ node size
Usually, Place _view_node_ and as children add _content_node_:
![](../media/scroll_scheme.png)
![](../media/scroll_outline.png)
*Here content_node below view_node, in game content_node be able to scroll left until end*
### Notes
- Scroll by default style have inertion and extra size for strecthing effect. It can be adjust via scroll [style settings](https://insality.github.io/druid/modules/Scroll.html#Style)
- You can setup "points of interest". Scroll always will be centered on closes point of interest. It is able to create slider without inertion and points of interest on each scroll element.
- Scroll have next events:
- *on_scroll* (self, position) On scroll move callback
- *on_scroll_to* (self, position, is_instant) On scroll_to function callback
- *on_point_scroll* (self, item_index, position) On scroll_to_index function callback
- You can adjust scroll content size by `scroll:set_size(node_size)`. It will setup new size to _content node_
- You can enabled or disable inertion mode via `scroll:set_inert(state)`
- You can adjust extra stretch size via `scroll:set_extra_stretch_size`
- Multitouch is required for scroll. Scroll is correctly handling touch_id swap while dragging scroll
## Progress
[Progress API here](https://insality.github.io/druid/modules/Progress.html)
### Overview
Basic Druid progress bar component
### Setup
Create progress bar component with druid: `progress = druid:new_progress(node_name, key, init_value)`
Node name should have maximum node size, so in GUI scene, node_name should be fully filled.
Key is value from druid const: const.SIDE.X (or just "x") or const.SIDE.Y (or just "y")
### Notes
- Progress correct working with 9slice nodes, it trying to set size by _set_size_ first, if it is not possible, it set up sizing via _set_scale_
- Progress bar can fill only by vertical or horizontal size. If you want make diagonal progress bar, just rotate node in GUI scene
- If you have glitchy or dark texture bug with progress bar, try to disable mipmaps in your texture profiles
## Slider
[Slider API here](https://insality.github.io/druid/modules/Slider.html)
### Overview
Basic Druid slider component
### Setup
Create slider component with druid: `slider = druid:new_slider(node_name, end_pos, callback)`
Pin node (node_name in params) should be placed in zero position (initial). It will be available to mode Pin node between start pos and end pos.
### Notes
- You can setup points of interests on slider via `slider:set_steps`. If steps are exist, slider values will be only from this steps (notched slider)
- For now, start pos and end pos should be on vertical or horizontal line (their x or y value should be equal)
## Input
[Input API here](https://insality.github.io/druid/modules/Input.html)
### Overview
Basic Druid text input component
### Setup
Create input component with druid: `input = druid:new_input(button_node_name, text_node_name, keyboard_type)`
### Notes
- Input component handle user text input. Input contains from button and text components. Button needed for selecting/unselecting input field
- Long click on input field for clear and select input field (clearing can be disable via styles)
- Click outside of button to unselect input field
- On focus lost (game minimized) input field will be unselected
- You can setup max length of the text
- You can setup allowed characters. On add not allowed characters `on_input_wrong` will be called. By default it cause simple shake animation
- The keyboard for input will not show on mobile HTML5. So input field in mobile HTML5 is not working now
- To make work different keyboard type, make sure value in game.project Android:InputMethod set to HidderInputField (https://defold.com/manuals/project-settings/#input-method)
## Timer
[Timer API here](https://insality.github.io/druid/modules/Timer.html)
### Overview
Handle timer work on gui text node
### Setup
Create timer component with druid: `timer = druid:new_timer(text_node, from_seconds, to_seconds, callback)`
### Notes
- Timer fires callback, when timer value equals to _to_seconds_
- Timer will set text node with current timer value
- Timer uses update function to handle time
## Static Grid
[Static Grid API here](https://insality.github.io/druid/modules/StaticGrid.html)
### Overview
Component for manage node positions.
Static grid have constant node size, so it possible to calculate node positions before placement. Nodes can be placed with gaps.
Static grid can shift elements on add/remove functions.
### Setup
Create component with druid: `grid = druid:new_grid(parent_node, prefab_node, max_in_row_elements)`
### Notes
- On _add node_ grid will set nodeup 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
- _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()*
## Data List
[Data List API here](https://insality.github.io/druid/modules/DataList.html)
### Overview
Component to manage data for huge dataset in scroll. DataList create elements only in scroll view.
It requires Druid Scroll and Druid Grid (Static or Dynamic) components
### Setup
Create component with druid: `grid = druid:new_data_list(scroll, grid, create_callback)`
- scroll - already created Scroll component
- grid - already created StaticGrid or DynamicGrid component
- create_function - your function to create node instances. This callback have next parameters: fun(self, data, index, data_list)
- self - Script/Druid context
- data- your element data
- index - element index
- data_list - current DataList component
Create function should return root node and optionaly, Druid component. Its required to manage create/remove lifecycle.
### Notes
- Set data with `data_list:set_data({...})` after component initialize
- You can use `data_list:scroll_to_index()` function to show data element
## Hover
[Hover API here](https://insality.github.io/druid/modules/Hover.html)
### Overview
System Druid component, handle hover node state.
### Setup
Create hover component with druid: `hover = druid:new_hover(node, callback)`
### Notes
- By default, hover handles _hover event_ with pressed touch action_id. So it's mean, what mouse or touch have to be pressed
- On desktop platforms there is _on_mouse_hover_ event. It's event on mouse hover without any action id
- By default, assume what node is on not hovered state (both _hover_ and _mouse_hover_)
## Swipe
[Swipe API here](https://insality.github.io/druid/modules/Swipe.html)
### Overview
System Druid component, handle swipe actions on node
### Setup
Create swipe component with druid: `hover = druid:new_swipe(node, swipe_callback)`
### Notes
- Swipe callback have next params: (self, swipe_side, distance, time)
- **self** - Druid self context
- **swipe_side**: *string* - values from ["up", "down", "left", "right"]
- **distance**: *number* - in pixels, distance of swipe
- **time**: *number* - in seconds, time of swiping
- Swipe trigger only, if all input actions was on swipe node. If action will be outside of node, swipe status will be reseted
- In swipe style table you can adjust minimal distance and maximum time to trigg- Hover state trigger only with touch on mobile devices or button mouse holding. Just mouse over swipe
- In swipe style table you can toggle type of swipe triggering. if SWIPE_TRIGGER_ON_MOVE setup to true - swipe will trigger as swipe can be triggered. If setup to false - swipe will trigger only on released action
- If you have stencil on swipe node and you don't want trigger it outside of stencil node, you can use `swipe:set_click_zone` to restrict swipe zonethout buttons is now not allowed.
## Drag
[Drag API here](https://insality.github.io/druid/modules/Drag.html)
### Overview
System Druid component, handle drag actions on node
### Setup
Create drag component with druid: `hover = druid:new_drag(node, drag_callback)`
### Notes
- Drag callback have next params: (self, dx, dy, total_x, total_y)
- **self**: Druid self context
- **dx**: *number* - delta x position
- **dy**: *number* - delta y position
- **total_x**: *number* - total delta x position
- **total_y**: *number* - total delta y position
- In styles, you can point the drag start deadzone. Default value is 10 pixels
- Drag correctly process multitouch. You can switch touch_id, while dragging on node with correct _dx_ and _dy_ values (made for correct scrolling)
- You can restrict horizontal or vertical dragging by setting `drag.can_x` or `drag.can_y` to _false_ value
- You can get info about current drag state:
- _is_touch_ - Is currently node touching
- _is_drag_ - Is currently node is dragging
- _x_ and _y_ - Current touch position
- _touch_start_pos_ - Touch stat positions
- Drag have next events:
- _on_touch_start_ (self) - Event on touch start
- _on_touch_end_ (self) - Event on touch end
- _on_drag_start_ (self) - Event on drag start
- _on_drag_ (self, dx, dy) - Event on drag process
- _on_drag_end_ (self) - Event on drag end
- Drag node zone can be restricted via `drag:set_click_zone(node)`
## Hotkey
[Hotkey API here](https://insality.github.io/druid/modules/Hotkey.html)
### Overview
Druid component to handle keyboard hotkeys with key modificators
### Setup
This is extended component. Before use it, you should register it:
```
local druid = require("druid.druid")
local hotkey = require("druid.extended.hotkey")
druid.register("hotkey", hotkey)
```
Create hotkey component with druid: `hotkey = druid:new_hotkey(keys_array, callback, [callback_argument])`
### Notes
- Hotkey callback is similar with button callback: (self, callback_argument)
- **self**: Druid self context
- **callback_argument**: *value* - Any value passed at component constructor
- In styles, you can point the array of modificator keys. This keys should be pressed with main key to trigger the callback
- The keys_arrays should contains one action key and any amount of modificator keys
- You can add additional hotkeys to hotkey component with `hotkey:add_hotkey(keys_array, callback_argument)`
## Layout
[Layout API here](https://insality.github.io/druid/modules/Layout.html)
### Overview
Component to arrange nodes inside layout node with margin/paddings settings. Works with different node sizes and pivots. Works in the same way as Figma AutoLayout
### Setup
This is extended component. Before use it, you should register it:
```
local druid = require("druid.druid")
local layout = require("druid.extended.layout")
druid.register("layout", layout)
```
Create layout component with druid: `layout = druid:new_layout(node, layout_mode)`
### Notes
- Layout mode can be next:
- `horizontal` - arrange nodes in horizontal line
- `vertical` - arrange nodes in vertical line
- `horizontal_wrap` - arrange nodes in horizontal line with wrap to next line
- You can setup margin and padding for layout nodes
- You can set "justify" alignment to place nodes with the same margin between layout node borders
- You can set "hug" by content. This option will set layout node size by content size. Can be setup separately for width and height

View File

@@ -0,0 +1,164 @@
# Creating Custom Components
# Deprecated
Custom compomnents from 1.1 release are deprecated. Now we have a new way to create custom components - widgets.
Custom components are will exists for more system things like basic components. You don't have to migrate to widgets.
Read more about widgets in [widgets.md](widgets.md)
## Overview
Druid offers the flexibility to create custom components that contain your own logic, as well as other Druid basic components or custom components. While Druid provides a set of predefined components like buttons and scrolls, it goes beyond that and provides a way to handle all your GUI elements in a more abstract manner. Custom components are a powerful way to separate logic and create higher levels of abstraction in your code.
Every component is a child of the Basic Druid component. You can call methods of basic components using `self:{method_name}`.
## Custom Components
### Basic Component Template
A basic custom component template looks like this (you can copy it from `/druid/templates/component.template.lua`):
```lua
local component = require("druid.component")
---@class component_name: druid.base_component
local M = component.create("component_name")
function M:init(template, nodes)
self.druid = self:get_druid(template, nodes)
self.root = self:get_node("root")
self.button = self.druid:new_button("button", function() end)
end
function M:hello()
print("Hello from custom component")
end
return M
```
Then you can create your custom component with Druid:
```lua
local druid = require("druid.druid")
local my_component = require("my.amazing.component")
function init(self)
self.druid = druid.new(self)
-- We pass a GUI template "template_name" and skip nodes due it already on the scene
self.my_component = self.druid:new(my_component, "template_name")
self.my_component:hello() -- Hello from custom component
end
```
### Full Component Template
A full custom component template looks like this (you can copy it from `/druid/templates/component_full.template.lua`):
```lua
local component = require("druid.component")
---@class component_name: druid.base_component
local M = component.create("component_name")
function M:init(template, nodes)
self.druid = self:get_druid(template, nodes)
self.root = self:get_node("root")
end
function M:update(dt) end
function M:on_input(action_id, action) return false end
function M:on_style_change(style) end
function M:on_message(message_id, message, sender) end
function M:on_language_change() end
function M:on_layout_change() end
function M:on_window_resized() end
function M:on_input_interrupt() end
function M:on_focus_lost() end
function M:on_focus_gained() end
function M:on_remove() end
return M
```
### Spawning a Custom Component
After creating your custom component, you can spawn it in your code. For example, if you have a component named `my_component`, you can create it like this:
```lua
local druid = require("druid.druid")
local my_component = require("my.amazing.component")
function init(self)
self.druid = druid.new(self)
self.druid:new(my_component, "template_name")
end
```
In the code above, `template_name` refers to the name of the GUI template file if you're using it in your custom component. `nodes` is a table obtained from `gui.clone_tree(node)`. If you're spawning multiple nodes for the component, pass the table to the component constructor. Inside the component, you need to set the template and nodes using `self:set_template(template)` and `self:set_nodes(nodes)`.
### Registering a Custom Component
You can register your custom component to use it without requiring the component module in every file. Registering components is convenient for very basic components in your game. Here's how you can register a custom component in Druid:
```lua
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
```
Once the component is registered, a new function will be available with the name "new_{component_name}". In our example, it will be `druid:new_my_component()`. With the component registered, you can create an instance of it using the following code:
```lua
local druid = require("druid.druid")
local my_component = require("my.amazing.component")
function init(self)
self.druid = druid.new(self)
self.my_component = self.druid:new_my_component(template, nodes)
end
```
## Create Druid Component Editor Script
Druid provides an editor script to assist you in creating Lua files for your GUI scenes. You can find the commands under the menu `Edit -> Create Druid Component` when working with *.gui scenes.
The script analyzes the current GUI scene and generates a Lua file with stubs for all Druid components found. The output file is named after the current GUI scene and placed in the same directory. Note that the script does not override any existing *.lua files. If you want to regenerate a file, delete the previous version first.
The script requires `python` with `deftree` installed. If `deftree` is not installed, the instructions will be displayed in the console.
### Auto-Layout Components
The generator script also checks the current GUI scene for Druid components and creates stubs for them. If a node name starts with a specific keyword, the script generates component stubs in the Lua file. For example, nodes named `button` and `button_exit` will result in the generation of two Druid Button components with callback stubs.
Available keywords:
- `button`: Adds a [Druid Button](01-components.md#button) component and generates the callback stub.
- `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.
- `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.
- `progress`: Adds a [Druid Progress](01-components.md#progress) component.
- `timer`: Adds a [Druid Timer](01-components.md#timer) component.
## 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.

59
wiki/03-styles.md Normal file
View File

@@ -0,0 +1,59 @@
# Styles
## Overview
Styles - set of functions and parameters for components to customize their behavior.
Styles is a table, where key is name of component, and value is style table for this component.
In component API documentation, you can find the style API for this component. Or just lookup for existing styles and modify them.
## Usage
Setup default druid style for all druid instances via `druid.set_default_style`
You can pass _nil_ or _empty_table_ to use default values for all components (no styles)
```lua
local druid = require("druid.druid")
local my_style = require("my.amazing.style")
function init(self)
druid.set_default_style(my_style)
end
```
Setup custom style to specific druid instance:
```lua
local druid = require("druid.druid")
local my_style = require("my.amazing.style")
function init(self)
-- This druid instance will be use my_style as default
self.druid = druid.new(self, my_style)
end
```
Change component style with _set_style_ function
```lua
local druid = require("druid.druid")
local my_style = require("my.amazing.style")
function init(self)
self.druid = druid.new(self)
self.button = self.druid:new_button("node", function() end)
-- Setup custom style for specific component
self.button:set_style(my_style)
end
```
## Create your own styles
The most components have their styles. You can explore it on [Druid API](https://insality.github.io/druid/) in table style section ([button example](https://insality.github.io/druid/modules/Button.html#style)). Or you can see, what fields component uses in code in function `on_style_change`
To create you style, create lua module, what return <_component_name_, _component_style_> table
Example: [default druid style](https://github.com/Insality/druid/blob/master/druid/styles/default/style.lua)
Override all fields you want and set your style with one of next ways:
- Set your style as global via `druid.set_default_style`
- Set style for concrete druid instance via `druid = druid.new(self, style)`
- Set style for concrete instance via `component:set_style(style)`

36
wiki/FAQ.md Normal file
View File

@@ -0,0 +1,36 @@
# Druid FAQ
Welcome to the Druid FAQ! Here are answers to some common questions you may have:
### Q: How do I remove a Druid component instance?
**A:** To remove a created Druid component, use the `druid:remove` function. You can find more information in the [API reference](https://insality.github.io/druid/modules/druid_instance.html#druid:remove).
### Q: How does Druid process input?
**A:** Input processing in Druid follows a Last-In-First-Out (LIFO) queue. Buttons added later have higher priority than those added earlier. To ensure correct button behavior, place your buttons from back to front in most cases.
### Q: What is the purpose of the Blocker component?
**A:** The Blocker component is used to block input in a specific zone. It is useful for creating unclickable zones within buttons or for creating a panel of buttons on top of another button (e.g., closing windows by clicking on the window background). You can find more information about the Blocker component [here](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#notes-2).
### Q: What can I do with custom components?
**A:** With custom components in Druid, the possibilities are endless! Custom components allow you to separate component placement and game logic from other elements, making them reusable and easier to test and develop. Custom components can be used for scroll elements with buttons, custom GUI widgets, or even components with custom game logic. Templates often accompany custom components, allowing you to create multiple visual variations for a single component module. You can find some examples of custom components [here](https://github.com/Insality/druid-assets).
### Q: How does `self:get_node()` work?
**A:** The `self:get_node()` function in a Druid component searches for nodes in the GUI directly or in cloned nodes created using `gui.clone_tree()`. It also considers nodes placed as templates, with the full node ID composed of the template name and node name (including cloned nodes). To ensure correct usage of `self:get_node()`, set up the component nodes using `self:set_template()` and `self:set_component_nodes()` before calling `self:get_node()`. It's best to pass the string name of the node, rather than the GUI node itself.
### Q: My button in a scroll is clickable outside the stencil node. How can I fix this?
**A:** When using Druid, the stencil node does not prevent buttons from being clickable outside its bounds. To address this, you can set up an additional click zone on your buttons using the `button:set_click_zone()` function. After adding a button to the scroll, you can use the following code:
```lua
-- Assuming the scroll view node is the stencil node
button:set_click_zone(scroll.view_node)
```
### Q: How do I use EmmyLua annotations? (from Druid 0.6.0)
**A:** EmmyLua annotations are used for better autocompletion and type inference in editors. To use the generated EmmyLua annotations, copy the `druid/annotations.lua` file to your project. After copying, you may need to restart the EmmyLua server to ensure the changes take effect. Once the annotations are processed, you can specify the type of Druid in your code:
```lua
---@type druid
local druid = require("druid.druid")
-- Autocomplete and type information should now work
```
Feel free to ask any additional questions you have about Druid!

118
wiki/advanced-setup.md Normal file
View File

@@ -0,0 +1,118 @@
# Advanced Druid Setup
## Input Bindings
By default, **Druid** uses all key names from Defold's default `/builtins/input/all.input_binding` for input bindings.
**Druid** requires the following input bindings:
- Mouse trigger: `Button 1` -> `touch` (for basic input components)
- Mouse trigger: `Wheel up` -> `mouse_wheel_up` (for Scroll component)
- Mouse trigger: `Wheel down` -> `mouse_wheel_down` (for Scroll component)
- Key trigger: `Backspace` -> `key_backspace` (for BackHandler component, input component)
- Key trigger: `Back` -> `key_back` (for BackHandler component, Android back button, input component)
- Key trigger: `Enter` -> `key_enter` (for Input component, optional)
- Key trigger: `Esc` -> `key_esc` (for Input component, optional)
- Key trigger: `Left` -> `key_left` (for Rich Input component, optional)
- Key trigger: `Right` -> `key_right` (for Rich Input component, optional)
- Key trigger: `Shift` -> `key_lshift` (for Rich Input component, optional)
- Key trigger: `Ctrl` -> `key_lctrl` (for Rich Input component, optional)
- Key trigger: `Super` -> `key_lsuper` (for Rich Input component, optional)
- Touch triggers: `Touch multi` -> `touch_multi` (for Scroll component)
![](../media/input_binding_2.png)
![](../media/input_binding_1.png)
## Changing Key Bindings
If you need to use your own key bindings or key names, you can modify them in your *game.project* file.
Here are the default values for key bindings:
```
[druid]
input_text = text
input_touch = touch
input_marked_text = marked_text
input_key_esc = key_esc
input_key_back = key_back
input_key_enter = key_enter
input_key_backspace = key_backspace
input_multitouch = touch_multi
input_scroll_up = mouse_wheel_up
input_scroll_down = mouse_wheel_down
input_key_left = key_left
input_key_right = key_right
input_key_lshift = key_lshift
input_key_lctrl = key_lctrl
input_key_lsuper = key_lsuper
```
## Input Capturing
By default, **Druid** automatically captures input focus if any input component is created. Therefore, you do not need to call `msg.post(".", "acquire_input_focus")`.
If you do not require this behavior, you can disable it by setting the `druid.no_auto_input` field in the _game.project_ file:
```
[druid]
no_auto_input = 1
```
## Code Bindings
Adjust **Druid** settings as needed:
```lua
local druid = require("druid.druid")
-- Used for button component and custom components
-- The callback should play the sound by name: function(sound_id) ... end
druid.set_sound_function(function(sound_id)
-- sound_system.play(sound_id)
end)
-- Used for lang_text component
-- The callback should return the localized string by locale ID: function(locale_id) ... end
druid.set_text_function(function(locale_id)
-- return lang.get(locale_id)
end)
-- Used to change the default Druid style
druid.set_default_style(your_style)
-- Call this function when the language changes in the game,
-- to retranslate all lang_text components:
local function on_language_change()
druid.on_language_change()
end
-- Call this function inside window.set_listener
-- to capture game focus lost/gained callbacks:
-- window.set_listener(function(self, event, data) druid.on_window_callback(event, data) end))
local function on_window_callback(self, event, data)
druid.on_window_callback(event)
end
window.set_listener(on_window_callback)
```
## 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).
Since dependencies cannot be processed by external editors, to use the EmmyLua annotations, you should copy the _druid/annotations.lua_ file to your project.
Remember that you can restart the EmmyLua server to refresh the changes if something goes wrong.
After the annotations are processed, you should specify the type of "Druid" in the "require" statement:
```lua
---@type druid
local druid = require("druid.druid")
-- Now the autocomplete is working
```
<img src="../media/emmy_lua_preview.png" width="700">

617
wiki/changelog.md Normal file
View File

@@ -0,0 +1,617 @@
### Druid 0.3.0:
- `Druid:final()` now is important function for correct working
- Add _swipe_ basic component
- Swipe component handle simple swipe gestures on node. It has single callback with direction on swipe. You can adjust a several parameters of swipe in druid style.
- Swipe can be triggered on action.released or while user is make swiping (in process)
- Add swipe example at main Druid example. Try swipe left/right to switch example pages.
- Add _input_ basic component
- Input component handle user text input. Input contains from button and text components. Button needed for selecting/unselecting input field
- Long click on input field for clear and select input field (clearing can be disable via styles)
- Click outside of button to unselect input field
- On focus lost (game minimized) input field will be unselected
- You can setup max length of the text
- You can setup allowed characters. On add not allowed characters `on_input_wrong` will be called. By default it cause simple shake animation
- The keyboard for input will not show on mobile HTML5. So input field in mobile HTML5 is not working now
- To make work different keyboard type, make sure value in game.project Android:InputMethod set to HiddenInputField (https://defold.com/manuals/project-settings/#input-method)
- Add two functions to basic component: `increase_input_priority` and `reset_input_priority`. It used to process component input first in current input stack (there is two input stacks now: INPUT and INPUT_HIGH). Example: on selecting input field, it increase input self priority until it be unselected
- Add two new component interests: `on_focus_gain` and `on_focus_lost`
- Add global druid events:
- on_window_callback: call `druid.on_window_callback(event)` for on_focus_gain/lost correct work
- on_language_change: call `druid.on_language_change()` (#38) for update all druid instances lang components
- on_layout_change: call `druid.on_layout_change()` (#37) for update all gui layouts (unimplemented now)
- Add button `on_click_outside` event. You can subscribe on this event in button. Was needed for Input component (click outside to deselect input field)
- Add _start_pos_ field to button component
- Changed input binding settings. Add esc, enter, text and marked_text. Backspace now is different from android back button event. Check the README setup section
- Renamed _on_change_language_ -> _on_language_change_ component interest
- Add several examples to druid-assets respository (see live example here): https://insality.github.io/druid-assets/)
- Known issues:
- Adjusting text size by height works wrong. Adjusting single line texting works fine
- Space is not working in HTML5
### Druid 0.4.0:
- Add _Drag_ basic component
- Drag component allow you detect dragging on GUI node
- Drag will be processed even the cursor is outside of node, if drag is already started
- Drag provides correct handle of several touches. Drag can switch between them (no more scroll gliches with position)
- Drag have next events:
- on_touch_start (self)
- on_touch_end (self)
- on_drag_start (self)
- on_drag (self, dx, dy)
- on_drag_end (self)
- You can restriction side of dragging by changing _drag.can_x_ and _drag.can_y_ fields
- You can setup drag deadzone to detect, when dragging is started (_by default 10 pixels_)
- [Breaking changes] Druid _Scroll_ component fully reworked. Input logic moved to _Drag_ component
- Update scroll documentation
- Change constructor order params
- Change _scroll:set_border_ to _scroll:set_size_
- Scroll now contains from view and content node
- _View node_ - static node, which size determine the "camera" zone
- _Content node_ - dynamic node, moving by _Scroll_ component
- Scroll will be disabled only if content size equals to view size (by width or height separatly)
- You can adjust start scroll size via _.gui_ scene. Just setup correct node size
- Different anchoring is supported (for easier layout)
- Function _scroll_to_ now accept position relative to _content node_. It's more easier for handling. _Example:_ if you have children node of _content_node_, you can pass this node position to scroll to this.
- **Resolve #52**: _Content node size_ now can be less than _view node size_. In this case, content will be scrolled only inside _view size_ (can be disabled via style field: _SMALL_CONTENT_SCROLL_)
- **Fix #50**: If style.SOFT_ZONE_SIZE equals to [0..1], scroll can be disappeared
- Druid _Grid_ Update
- Anchor by default equals to node pivot (so, more component settings in _.gui_ settings) (#51)
- Function `grid:clear` now don't delete any GUI nodes. Druid will not care about `gui.delete_node` logic anymore (#56)
- Druid _Hover_ component now have two _hover_ events (#49):
- _on_hover_ is usual hover event. Trigger only if touch or mouse action_id pressed on node
- _on_mouse_hover_ action on node without action_id (desktop mouse over). Works only on desktop platform
- Styles update:
- Styles table now can be empty, every component have their default style values
- Remove `component:get_style` function. Now you can only set styles
- To get style values in component, add `component:on_style_change` function. It's invoked on `component:set_style` function
- You can look up default values inside `component:on_style_change` function or style component API on [Druid API](https://insality.github.io/druid/index.html)
- Druid update:
- Now function `druid:remove` remove instance and all instance children components. No more manual deleting child components (#41)
- **Fix:** Blocker component bug (blocker had very high priority, so it's block even button components, created after blocker)
- **Fix #58:** Bug, when druid instance should be always named `druid` (ex: `self.druid = druid.new(self)`)
- **Fix #53:** Bug with final _Druid instance_ without any components
### Druid 0.5.0:
Besides a lot of fixes (thanks for feedback!) two components was add: _StaticGrid_ and _DynamicGrid_ instead of usual _Grid_ component (it is deprecated now).
Add _component:set_input_enabled_ for basic component class. So you can enable/disable user input for any component.
Finaly implemented _on_layout_changed_ support. Druid components now will try keep their data between layout changing! You also can use this callback in your custom components.
Also check _component.template.lua_ what you can use for your own custom components!
- **#77** Grid update:
- The _grid_ component now is __deprecated__. Use _static_grid_ instead. Druid will show you deprecated message, if you still using _grid_ component
- __[BREAKING]__ Remove the _grid:set_offset_ grid functions. To adjust the distance between nodes inside grid - setup correct node sizes
- Add _static_grid_ component
- The behaviour like previous _grid_ component
- Have constant element size, so have ability to precalculate positions, indexes and size of content
- By default, not shifting elements on removing element. Add _is_shift_ flag to _static_grid:remove_ function
- This grid can spawn elements with several rows and columns
- Add _dynamic_grid_ component
- Can have different element size. So have no ability to precalculate stuff like _static_grid_
- This grid can't have gaps between elements. You will get the error, if spawn element far away from other elements
- The grid can spawn elements only in row or in column
- The grid node should have __West__, __East__, __South__ or __North__ pivot (vertical or horizontal element placement)
- Able to shift nodes left or right on _grid:add_ / _grid:remove_ functions
- Scroll update:
- Add _scroll:set_vertical_scroll_ and _scroll:set_horizontal_scroll_ for disable scroll sides
- Add _scroll:bind_grid_ function. Now is possible to bind Druid Grid component (Static or Dynamic) to the scroll for auto refresh the scroll size on grid nodes changing
- **#37** Add _on_layout_change_ support. Druid will keep and restore GUI component data between changing game layout. Override function _on_layout_change_ in your custom components to do stuff you need.
- **#85** Move several components from `base` folder to `extended`. In future to use them, you have to register them manually. This is done for decrease build size by excluding unused components
- **Fix #61:** Button component: fix button animation node creation
- **Fix #64:** Hover component: wrong mouse_hover default state
- **Fix #71:** Blocker: blocker now correct block mouse hover event
- **Fix #72:** Fix `return nil` in some `on_input` functions
- **Fix #74:** __[BREAKING]__ Fix typo: strech -> stretch. Scroll function `set_extra_stretch_size` renamed
- **Fix #76:** Add params for lang text localization component
- **Fix #79:** Fix _druid:remove_ inside on_input callback
- **Fix #80:** Fix _hover:set_enable_ typo function call
- **Fix #88:** Add _component:set_input_enabled_ function to enable/disable input for druid component. Now you can disable input of any druid component, even complex (with other components inside)
- Add `component.template.lua` as template for Druid custom component
- Update the example app
### Druid 0.6.0:
Hey! Are you tired from **Druid** updates? _(It's a joke)_
Finally, got a time to release component to process huge amount of data. So introducing: **DataList** component. It can help solve your problem with `GUI nodes limit reached` and helps with scroll optimization. Give feedback about it!
The next important stuff is **EmmyLua** docs. I'm implemented EmmyLua doc generator from LuaDoc and Protofiles, so now you can use EmmyLua annotations inside your IDE instead of website API looking or source code scanning.
Also the **Druid examples** is reworked, so each example will be in separate collection. Now it's become a much easier to learn Druid via examples. A lot of stuff in progress now, but you already can see on it!
Input priority got reworked too. Now instead of two input stacks: usual and high, Druid use simple input priority value.
And I should note here are several breaking changes, take a look in changelogs.
Wanna something more? [Add an issues!](https://github.com/Insality/druid/issues)
Have a good day.
**Changelog 0.6.0**
---
- **#43** Add **DataList** Druid extended component. Component used to manage huge amount of data to make stuff like _infinity_ scroll.
- This versions is first basic implementation. But it should be enough for almost all basic stuff.
- Create Data List with `druid:new_data_list(scroll, grid, create_function)`.
- _scroll_ - already created __Scroll__ component
- _grid_ - already created __StaticGrid__ or __DynamicGrid__ component
- _create_function_ - your function to create node instances. This callback have next parameters: `fun(self, data, index, data_list)`
- _self_ - Script/Druid context
- _data_- your element data
- _index_ - element index
- _data_list_ - current __DataList__ component
- Create function should return root node and optionaly, _Druid_ component. It's required to manage create/remove lifecycle
- Set data with `data_list:set_data({...})`
- In current version there is no `add/remove` functions
- Add EmmyLua annotations (_ta-daaa_). See how to [use it FAQ](https://github.com/Insality/druid/blob/develop/docs_md/FAQ.md)!
- Add context argument to Druid Event. You can pass this argument to forward it first in your callbacks (for example - object context)
- Add _SHIFT_POLICY_ for _Static_ and _Dynamic_ Grids. It mean how nodes will be shifted if you append data between nodes. There are `const.SHIFT.RIGHT`, `const.SHIFT.LEFT` and `const.SHIFT.NO_SHIFT`.
- __[BREAKING]__ Please check your `StaticGrid:remove` and `DynamicGrid:remove` functions
- **#102** __[BREAKING]__ Removed `component:increase_input_priority` component function. Use `component:set_input_priority` function instead. The bigger priority value processed first. The value 10 is default for Druid components, the 100 value is maximum priority for acquire input in _drag_ and _input_ components
- Add constants for priorities: _const.PRIORITY_INPUT_, _const.PRIORITY_INPUT_HIGH_, _const.PRIORITY_INPUT_MAX_.
- __[BREAKING]__ If you use in you custom components interest: `component.ON_INPUT_HIGH` you should replace it with `component.ON_INPUT` and add `const.PRIORITY_INPUT_HIGH` as third param. For example:
_before:_
```lua
local Drag = component.create("drag", { component.ON_INPUT_HIGH })
```
_after:_
```lua
local Drag = component.create("drag", { component.ON_INPUT }, const.PRIORITY_INPUT_HIGH)
```
- Lang text now can be initialized without default locale id
- Input component: rename field _selected_ to _is_selected_ (according to the docs)
- **#92** Setup repo for CI and unit tests. (Yea, successful build and tests badges!)
- **#86** Fix a lot of event triggers on scroll inertia moving
- **#101** Fix scroll to other node instead of swipe direction with scroll's points of interest (without inert settings)
- **#103** Add `helper.centate_nodes` function. It can horizontal align several Box and Text nodes
- **#105** Add `Input:select` and `Input:unselect` function.
- **#106** Add `Input.style.IS_UNSELECT_ON_RESELECT` style param. If true, it will be unselect input on click on input box, not only on outside click.
- **#108** Add component interests const to `component.lua`
- **#116** You can pass Text component in Input component instead of text node
- **#117** Move each Druid example in separate collection. It's a lot of easier now to learn via examples, check it!
- Examples in progress, so a lot of stuff are locked now, stay tuned!
- **#118** Druid.scroll freezes if held in one place for a long time
- **#123** Add scroll for Scroll component via mouse wheel or touchpad:
- Added Scroll style params: `WHEEL_SCROLL_SPEED`, `WHEEL_SCROLL_INVERTED`
- Mouse scroll working when cursor is hover on scroll view node
- Vertical scroll have more priority than horizontal
- Fix: When Hover component node became disabled, reset hover state (throw on_hover and on_mouse_hover events)
- By default mouse scroll is disabled
- This is basic implementation, it is work not perfect
- **#124** Add `Scroll:set_click_zone` function. This is just link to `Drag:set_click_zone` function inside scroll component.
- **#127** The `druid:create` is deprecated. Use `druid:new` for creating custom components
### Druid 0.7.0:
Hello! Here I'm again with new Druid stuff for you!
The feature I want a long time to deliver for you: the different Text size adjust modes. Druid use the text node sizes to fit the text into this box.
There are new adjust modes such as Trim, Scroll, Downscale with restrictions and Downscale + Scroll. You can change default adjust mode via text style table, but by default there is no changes - it's downscale adjust mode as before.
I'll hope it can be useful for you for in different cases and now it will be much easy to fit all your texts for different languages!
The next features is made for add more control for availability of user input. So meet the whitelists, blacklists and custom check functions for Buttons. Now you can easily choose the more suitable way to enable/disable/restrict input for you users. I'm sure it can be useful for you tutorials.
Another small, but cool feature on my mind is `druid.stencil_check`. If you did interactive elements inside the Scroll, probably you used `component:set_click_zone` to restrict input zone by stencil scroll view node. With this feature, Druid will do it automaticaly for you! You can enable this feature in your `game.project`. It will not override you existing `set_click_zone`.
Now you even able to remap default input keys! Also there are several bugfixes with Scroll, Text, Grids.
Wanna something more? [Add an issues!](https://github.com/Insality/druid/issues)
Good luck!
**Changelog 0.7.0**
---
- **#78** [Text] Update Text component:
- Add text adjust type instead of _no_adjust_ param.
- const.TEXT_ADJUST.DOWNSCALE - Change text's scale to fit in the text node size
- const.TEXT_ADJUST.TRIM - Trim the text with postfix (default - "...", override in styles) to fit in the text node size
- const.TEXT_ADJUST.NO_ADJUST - No any adjust, like default Defold text node
- const.TEXT_ADJUST.DOWNSCALE_LIMITED - Change text's scale list downscale, but there is limit for text's scale
- const.TEXT_ADJUST.SCROLL - Change text's pivot to imitate scrolling in the text box. Use with stencil node for better effect.
- const.TEXT_ADJUST.SCALE_THEN_SCROLL - Combine two modes: first limited downscale, then scroll
- **#110** [Button] Add `Button:set_check_function(check_function, failure_callback)` function to add your custom click condition to button.
- `Button:set_enabled` has more priority than this to check button availability
- The `check_function` should return _true_ of _false_. If true - button can be clicked by user
- The `failure_callback` will be called if `check_function` will return false
- Example with `set_check_function` in general:buttons example collection
- **#66** Add `druid:set_whitelist()` and `druid.set_blacklist()` functions. It's affects only on input process step, you can allow/forbid interact with list of specific components
- You can pass array of components, single component or nil in these functions
- **#111** Add autocheck for input and stencil nodes. To enable this feature, add `druid.stencil_check = 1` to your _game.project_ file.
- This feature is using for auto setup `component:set_click_zone` to restrict clicks outside scrolls zone for example. Now you can don't think about click zone and let Druid do it instead of you!
- Add `helper.get_closest_stencil_node` function to get closest parent of non inverted stencil node
- Add `component.ON_LATE_INIT` interest. Component with this will call `component.on_late_init` function once after component init on update step. This can be used to do something after all gui components are inited
- **#81** Add ability to interact with Druid input via messages:
- Currently add for Button and Text component only:
- Send to _gui.script_ message: `druid_const.ON_MESSAGE_INPUT`. The message table params:
- `node_id` - the name of the node with component on it
- `action` - value from `druid_const.MESSAGE_INPUT`. Available values:
- **BUTTON_CLICK** - usual button click callback
- **BUTTON_LONG_CLICK** - button long click callback
- **BUTTON_DOUBLE_CLICK** - button double click callback
- **BUTTON_REPEATED_CLICK** - button repeated click callback
- **TEXT_SET** - set text for Text component
- `value` - optional field for several actions. For example value is text for **TEXT_SET**
- Add Druid component interest: `component.ON_MESSAGE_INPUT`
- Implement new interest via function `component:on_message_input(node_id, message)`
- See **System: Message input** example
- **#131** [Static Grid] Add style param: `IS_DYNAMIC_NODE_POSES` (default: false). Always align by content size with node anchor.
- If true - Static Grid will by always align to content anchor.
- If false (currently behaviour) - all poses for static grid is predefined and not depends on element's count (see example: static grid and static grid with dynamic poses)
- **#125** Now `component:set_input_priority()` affects on all component's children too
- **#143** Update all lang components on `druid.set_text_function` call
- **#112** Allow remap default Druid input bindings via `game.project`
- **#107** [Text] Better scale text adjust by height for multiline text nodes (but still not perfect)
- **#144** [Scroll] Fix some glitches with scroll Points of Interest. Remove false detection of scroll stopped.
- **#142** [Scroll] Add Scroll style param `WHEEL_SCROLL_BY_INERTION` (default - false). If true - mouse wheel will add inertion to scroll, if false - set position directly per mouse wheel event.
- This fix caused because Mac trackpad seems have additional mouse wheel events for simulate inertion. If you uncomfortable with this, you can disable `WHEEL_SCROLL_BY_INERTION` for more controllable scroll by mouse wheel.
- **#132** Add example with grid add/remove with animations
### Druid 0.8.0
Hello!
In this Druid update no any huge special features. Mostly the bug fixes and reworking the component's interest points. If you used interests in your custom components, you should remove it from `component.create` and all should works as before.
Also added last row allignment in Static Grid component with "dynamic content poses" style enabled. You can look how it is work here: https://insality.github.io/druid/druid/?example=grid_static_grid_dynamic_pos
You can say thanks to me via stars on GitHub! :wink:
Wanna something more? [Add an issues!](https://github.com/Insality/druid/issues)
Have a nice day!
**Changelog 0.8.0**
---
- **#160** __[BREAKING]__ Remove component interests list
- The component interests now setup via function declaration inside your components. The functions are still the same.
- Now `component.create` function have next signature: _create(component_name, input_priority)_
- Your should remove interests list from your custom components if exists
- From `component.create("custom", { component.ON_INPUT, component.ON_LATE_INIT }, const.PRIORITY_INPUT_HIGH)` to
`component.create("custom", const.PRIORITY_INPUT_HIGH)`
- **#166** [Input] Fix issue with Cyrillic symbols in range "[А-я]"
- **#162** [Static Grid] Add last row alignment with dynamic content poses enabled
- Add style param: _static_grid.IS_ALIGN_LAST_ROW_, true by default. Works only if _static_grid.IS_DYNAMIC_NODE_POSES_ enabled. See the "Static grid with dynamic poses" example.
- **#163** [Lang Text] Set default locale_id value from text node
- **#147** [Lang Text] Remove `...` from lang_text to fixed arguments, add _format_ function to change only string format arguments
- There are some issues with `...`. Now Lang Text will support up to 7 _string.format_ arguments
- [Lang Text] Add more self chaining to Lang text component (_set_to_, _translate_ and _format_ functions)
- **#151** [Text] Fix text adjust by height
- It still have not perfect fitting, but it's good enough!
- **#164 #150** [Scroll] Fix `scroll:scroll_to_percent` by Y position
- **#148** [Scroll] Remove scroll inertion after scroll `animate` or `set_to` functions
- [Input] Add current text argument to _on_input_unselect_ event
- **#152** [Checkbox] Add _is_instant_ argument to `set_state` function
- Add _initial_state_ argument to Checkbox component constructor
- Update Checkbox style, add _is_instant_ param
- **#149** [Button] Call button style functions after actual callback
- **#153** System: Mode Druid acquire input to late_init step
- Required to solve issues, when go input acquire can be later, when gui input acquire (on init step)
- **#154** System: Change text adjust const to strings
- **#155** Fix: Add margin to total width calculation in `helper.centrate_nodes`
### Druid 0.9.0
_Custom components update_
Hello!
Here is the long awaited update! Finally I implemented some ideas how to make easier creating custom components. There is a bunch of improvements you can be interested in.
I wanna make a point that Druid is not only set of defined components to place buttons, scroll, etc. But mostly it's a way how to handle all your GUI elements in general. Custom components is most powerful way to separate logic and make higher abstraction in your code.
Usually - custom components is set of GUI template and lua code for this template. I've added editor script, that can make a lua component file from your GUI scene (all boilerplate and usage code, also some component that can be defined right in GUI scene).
Auto layout from GUI script should be a powerful tool too! Also it's brings some code structure and style across all your files. Auto layout works from node names. If its starts or equal to some string, it will add code to generated lua file. For example, if you have in your scene node with name "button_start", it will create the Druid button, stub function and annotations to this. Sounds good!
For more information see [Create custom components](docs_md/02-creating_custom_components.md) documentations.
Also this update have some breaking changes: you should no more pass full tempalte name in inner components and the second one is renaming `text:get_text_width` to `text:get_text_size`.
The Defold 1.3.0 solves the old my issue with slider component. Now you can define input zone (not only the slider pin node) to interact with slider. It's because of inroduction `gui.screen_to_local` and `gui.set_screen_position` in default GUI api. If you using previuos Defold releases, this piece of logic will be ignored.
The Druid Assets repository will be closed and I move some components right in Druid repository. You can now use custom components in your game if your need. Right now it's Rich Input (input field with cursor and placegolder) and Pin Knob (Rotating node for set value). And slowly working on adding new examples and improvements of existing ones.
You can say thanks to me via stars on GitHub! :wink:
Also you can help with testing new functions and leave feedback.
Wanna something more? [Add an issues!](https://github.com/Insality/druid/issues)
Take care of yourself
**Changelog 0.9.0**
---
- **#119** Add **Create Druid Component** editor script *(python3 with deftree required)*
- The headliner of current update. This editor scripts allows you to create Custom component lua script from you *.gui* scene file. It will create component file with the same name as GUI scene and place it nearby. Inside this generated file you will find the instructions how to start usage this (require and create code).
- This code contains GUI scheme, basic component boilerplace and generated code for components, used in this GUI scene (see #159)
- See [Create custom components](docs_md/02-creating_custom_components.md) for more info
- **#159** Add auto layout custom components by node naming
- The **Create Druid Component** script will check the node names to create Druid components stubs inside generated code
- The generator will check the node name, if it's starts from special prefix, it will create component code for you
- Currently support the next components: `button`, `text`, `lang_text`, `grid`, `static_grid`, `dynamic_grid`, `scroll_view`, `blocker`, `slider`, `progress` and `timer`
- **#158** **[BREAKING]** Auto `get_node` inside inner components with template/nodes
- Before this update, if your component with template using another component with template, you had to pass full template name (`current_template .. "/" .. inner_component_template`). From this update you should pass only the `inner_component_template` name. It's will auto check the parent component template name to build full template path
- If you don't want migrate code for this, this option can be disabled via `druid.no_auto_template` in your _game.project_ file. By default it's enabled now
- **#171** Add `component:get_template()` function to **Druid** Base Component
- Now it's able to get full component template name. This function has "protected" scope, so you should use it only inside your component code.
- **#173** Fix GUI nodes usage inside inner templates
- Now you can pass the node name instead of node itself to Druid components inside your Custom Components. Before this update Druid didn't check the nodes in parent component (usually for basic components such as button, text inside your components)
- So you can use now `self.druid:new_button(SCHEME.BUTTON)` instead of `self.druid:new_button(self:get_node(SCHEME.BUTTON))` inside your custom components
- **#174** Add assert to nil node on `self:get_node()`
- It's required for easier debuging components, when GUI node path is wrong and your got the nil. The error with node path will appear in console.
- **#169** [System] Fix on_layout_change event
- It was broken, now the on_layout_change event is working as expected
- **#165** [StaticGrid] Add `static_grid:set_in_row(amount)` function
- **#44** [Slider] Click zone on all slider node, not only pin node
- Finally! Added the `slider:set_input_node(node)` function. Now slider can be interacted not only with slider pin node, but with any zone you will define.
- It will work only from Defold 1.3.0. If you use earlier version, nothing is happened. It using new `gui.screen_to_local` and `gui.set_screen_position` functions.
- **#178** **[BREAKING][Text]** Rename `text:get_text_width` to `text:get_text_size`. Now it return two numbers: width and height
- **#114** Add default component templates
- Added templates for fast prototyping. Use GUI templates to place buttons, checkbox, input and sliders on your GUI scene. You still have to create component with `druid:new` functions inside gui_script.
- **#168** Add button to open code of current example
- Inside every example added button to open code of this example on GitHub
- **#140** Better documentation for custom components
- **#172** Update documentation with links to components
- The docs in (https://insality.github.io/druid/) now have cross links for every custom type
- **#175** Remove Druid Assets repository, move to Druid library
- Added folder `druid/custom`. It will have the complex custom components. Usually you should to use default GUI template or create your own with similar GUI scheme. Currently add `RichInput` and `PinKnob` components from druid-assets repository.
- Usually to use custom component you have to require lua file first and create it's via `druid:new(Component, template_name, [nodes])`. See component docs to see constructor params.
- This components will be included in build only if used
- **#176** Keep last scene and scroll position in Druid example
- Probably, it's useful for faster debug, but anyway. The example now keep the last scene and scroll position.
- Add new examples: Checkboxes, Swipe, Grid, Rich input, Pin knob
- Now editor scripts are available in Druid as dependency
- Move emmylua annotations inside Druid dependency folder. You can copy it from Defold Editor outline
- Optimize different stuff(Scroll, Druid Event, Druid instance and Base component)
- Force Data List component to `IS_DYNAMIC_NODE_POSES = false` style
### Druid 0.10.0
Hello! Here is new Druid update. It's brings to you two new components: Layout and Hotkey. Both components are "extended", so to use it, you should register it first (when you try to use it, in console will be prompt with code you should use)
In general:
```
local layout = require("druid.extended.layout")
druid.register("layout", layout)
```
The Drag component now knows about window scaling, so now it have more accuracy dx/dy values depends on the screen size. The scroll and other components should work better :)
Now you can change the input priority of components temporary. For example while you interact with them (input fields, drag on select etc).
Also the update brings several bug fixes and now **Druid** have stencil_check mode enabled by default. It should be more easy to use input components with stencil nodes without manual `set_click_zone` functions.
And yeah, the new **Druid** logo is here!
**Changelog 0.10.0**
---
- **#133** [Hotkey] Add new extended component: Hotkey
- It's allow you set hotkeys to call callbacks
- You should pass one action key and several modificator keys (left shift, right ctrl etc)
- List of modificator keys ids setup via component style (you can change it)
- You can add several hotkeys on one callback via `hotkey:add_hotkey` with additional params
- **#98** [Layout] Add new extended component: Layout
- It's allow you to extend standart Defold node adjust modes
- Layout mode can be next:
- `const.LAYOUT_MODE.STRETCH_X` - Stretch node only by X
- `const.LAYOUT_MODE.STRETCH_Y` - Stretch node only by Y
- `const.LAYOUT_MODE.ZOOM_MIN` - Zoom node by minimal stretch multiplier
- `const.LAYOUT_MODE.ZOOM_MAX` - Zoom node by maximum stretch multiplier
- `const.LAYOUT_MODE.FIT` - Usual Defold Fit mode
- `const.LAYOUT_MODE.STRETCH` - Usual Defold Stretch Mode
- The Layout changes the node size property. So it's look much better if you use 9slice nodes
- Works even the node parent is have Fit adjust mode
- **#200** [Scroll] Glitch if content size equals to scroll view size in runtime
- **#201** [DataList] Update DataList:
- Add two events: `on_element_add` and `on_element_remove`
- Add `data_list:get_data()` to access all current data in DataList
- Add `data_list:get_created_nodes()` to access currently visual nodes in DataList
- Add `data_list:get_created_components()` to access currenly visual component in DataList (if created)
- **#190** [Progress] Add `progress:set_max_size` function to change max size of progress bar
- **#188** [Drag] Add two values passed to on_drag callback. Now it is `on_drag(self, dx, dy, total_x, total_y)` to check the overral drag distance
- **#195** [Drag] Add `drag:is_enabled` and `drag:set_enabled` to enable/disable drag input component
- **#186** [Grid] Fix: Method `set_in_row` works incorrectly with IS_DYNAMIC_NODE_POSES style option
- **#185** [System] Add `on_window_resized` component interest. It will called on game window size changes
- **#189** [System] Add optional flag to `component:set_input_priority` to mark it as temporary. It will reset to default input priority after the `component:reset_input_priority`
- **#204** [System] Fix: wrong code example link, if open example from direct URL
- **#202** [System] Enabled stencil check to true by default. To disable this, use `druid.no_stencil_check` in game.project settings
- [Examples] Add layout, layout fit, progress bar, data list + component examples
### Druid 0.11.0
Hello! What a wonderful day for the new **Druid** update!
Alright, let's get straight to the point. Look at what I have for you!
**Druid Rich Text** has finally been released. The main difference from the existing **Bjorn's** Rich Text is that all visual parameters are customizable directly in the GUI. This allows you to integrate **Rich Text** more accurately and quickly. Additionally, this **Rich Text** aligns pixel perfect (well, [almost](https://github.com/defold/defold/issues/7197)) with regular GUI text node.
This version is the most basic one. Honestly, just wanna to publish current version for your and polish it later. Read [RichText API here](https://insality.github.io/druid/modules/RichText.html)
Another addition is the ability to enable the "HTML mode" for the Button component. In this mode, the button's action occurs in the context of `user action`, allowing operations like "copy and paste text" "show the keyboard" and more. However, in this mode, the button only responds to regular clicks due to the technical implementation of it (so no double clicks or long taps for this button).
The huge work was done on documentation. Now it's more clear and have more examples. All documentation now moved to the API section. The separate `componentd.md` manual will be deleted soon as all documentation will be moved to the API section.
The API section now filled with overview and usage examples. I've started with the basic modules, in future I will add more examples for all modules.
Also, I've added the **Unit Tests**. It's not cover all **Druid** code, but it's a good start! 🎉
Have a good day!
**Changelog 0.11.0**
---
- **#191**: [RichText] Finally add **Druid [Rich Text](https://insality.github.io/druid/modules/RichText.html)** custom component. Component is used to make formatted text in your's GUI. This Rich Text mostly adjusted visually in GUI and have almost pixel-perfect match with similar GUI text node
- **#39**: [System] Finally add **Unit Tests**. Yeah, it cover not all **Druid** code, but it's a good start! 🎉
- **#219**: [System] UTF-8 performance optimization. Now Druid will try to use *utf8* native extension over lua utf8 library if exists. If you wanna use native utf8, just [add the extension](https://github.com/d954mas/defold-utf8) in your `game.project` dependency.
- **#156**: [Button] Now button can work in HTML5 with `html5.set_interaction_listener`.
- The API is `button:set_web_user_interaction(true)`. In HTML5 mode button have several restrictions. Basically, only the single tap event will work.
- **#227**: [System] Update current URL in HTML5 example
- Now if you will open the example from direct URL, it will be updated to the current URL in your browser. So now it's much easier to share the example link with each other.
- **#183**: [Docs] Documentation about [GUI in World Space](https://forum.defold.com/t/how-to-gui-in-defold/73256#gui-in-world-coordinates-49)
- Also not only the GUI in World Space, but overall How to GUI in Defold article.
- **#234**: [**BREAKING**] [Blocker] Now `blocker:set_enabled` and `blocker:is_enabled` affects only inner state of component. To consume input, the blocker component should be enabled and the node itself should be enabled.
- Breaking due the changes can affect your current logic. Please if use this re-check Blocker component usage.
- **#235**: [Drag] Fix Drag coordinates on streched screen.
- **#236**: [Hover] Fix nil return in `hover:on_input`.
- **#237**: [Layout] Add `layout:set_max_gui_upscale` function.
- This functions will scale down element, if current GUI scale is bigger than `max_gui_upscale` value. It can be useful for adapt mobile device to desktop screen.
- **#238**: [System] Add Helper documentation.
- [System] Now the documentation contains the **Druid** size. The current size as dependency is around **67KB**. It counted without extended components, which is not included by default in the build.
Thanks to the my supporters:
- [Defold Foundation](https://defold.com)
- [Ragetto](https://forum.defold.com/u/ragetto)
❤️ Support ❤️
Please support me if you like this project! It will help me keep engaged to update **Druid** and make it even better!
[![Github-sponsors](https://img.shields.io/badge/sponsor-30363D?style=for-the-badge&logo=GitHub-Sponsors&logoColor=#EA4AAA)](https://github.com/sponsors/insality) [![Ko-Fi](https://img.shields.io/badge/Ko--fi-F16061?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/insality) [![BuyMeACoffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/insality)
### Druid 1.0
**Description**
Hello! The long-awaited update for Druid is finally here! The Druid now is finally 1.0 version! 🎉 This was a long path to achieve current state!
This update brings a lot of improvements, so let's dive in:
**New Example Page**
Ive updated Druid's main examples page. Since Druid has become quite popular, I wanted to ensure the examples meet high standards of quality and aesthetics. The examples are now clearer and provide more information. Ive also added many new examples. Check them out! Share your feedback and suggestions for new examples. Now it's much easier to learn Druid!
Play right here - https://insality.github.io/druid/druid
**Component Reworking**
Several components have been reworked. While I generally avoid introducing breaking changes, sometimes they are necessary for progress.
- **Rich Text** is now applied directly to the text node, rather than using a `Rich Text Template`. This makes setup and usage much easier! Im still working on figuring out how to apply this approach to Rich Input.
- The **Layout** component has been completely replaced. It now functions similarly to **Dynamic Grid** but with more settings and modes. This layout now works similarly to Figma Auto Layout, allowing you to arrange nodes in various ways.
- **Dynamic Grid** will be deprecated in the future, with the new Layout component serving as its replacement.
- **Data List** now works exclusively with **Static Grid**, making it more stable and optimized. Additionally, a new `cached` version is available, which optimizes node reuse. However, the cached version requires using `on_add_element` and `on_remove_element` events to properly set up nodes.
**Code Cleanup**
Ive finally removed `middleclass` from Druid. If you were using it for some reason, youll need to copy the "middleclass" code into your project.
**Annotations**
Druids annotations were originally created when there were no Lua language servers. These annotations are of the older LDoc type and not EmmyLua. In the future, I aim to get rid of annotations altogether and rely on annotated code, which is easier to read, maintain, and feature-rich. The Defold will support the LLS (Lua Language Server) as well as VSCode with amazing Defold-Kit extension.
---
**Milestone**: https://github.com/Insality/druid/milestone/12
**Changelog 1.0**
- New Druid logo!
- **[Example]** New Example Page with 40+ examples.
- **BREAKING** **[Rich Text]** Now applied directly to the text node instead of using a Rich Text Template (which previously contained three nodes: root, text, and image prefabs). This simplifies usage in the GUI.
- **BREAKING** **[System]** Removed `middleclass.lua`. If you were using it, youll need to copy the code into your project.
- **BREAKING** **[System]** Removed: `checkbox`, `checkbox_group`, and `radio_button` components. These components can be easily created using the Button component. Check the examples for implementation.
- **BREAKING** **[Layout]** Reworked the Layout component. The new version allows arranging nodes in various modes (vertical, horizontal, horizontal wrap) and includes more settings (margins, padding, justification, pivots, and content hugging options). This will replace Dynamic Grid in the future.
- **[Data List]** Reworked Data List to work only with **Static Grid**. Its now more stable and has an extended API.
- Added a **Cached Data List** option, which uses less memory (highly optimized) but requires `on_add_element` and `on_remove_element` events for node setup. All components must be of the same class in this case.
- **[Rich Input]** Updated with new features such as selection and cursor navigation. New input keys can be configured in Druid (arrows, ctrl, shift).
- **[Input]** Users can now switch between text input areas with a single click, instead of needing to tap twice (once to close the focus and once to open the new input).
- **[Dynamic Grid]** Deprecated in favor of the new Layout component.
- **[Drag]** Added a touch parameter to Drag callbacks, making it easier to add custom logic with input action data.
- **[Scroll]** Added `scroll.view_size`, `scroll:set_view_size(size)`, and `scroll:update_view_size()` functions for better management of the scroll input area and visible part.
- **[Static Grid]** Added `grid:set_item_size(size)` and `grid:sort_nodes(comparator)` functions.
- **[Text]** Adjustments for multiline text height seem to be working correctly now.
- **[Progress Bar]** Improved accuracy when scaling progress bars for images with slice9 parameters.
- **[Slider]** Fixed several slider setup issues.
- **[System]** Updated and fixed annotations.
- **[System]** Removed: `pin_knob` custom component. It mostly was created as an example and now is not needed.
- **[System]** Added `self:get_druid(template, nodes)` to replace `self:set_template(template)` and `self:set_nodes(nodes)` calls in custom components.
- Various improvements and fixes.
A big thanks to the my Github supporters:
- [Defold Foundation](https://defold.com)
- [Ragetto](https://forum.defold.com/u/ragetto)
- [Pawel](https://forum.defold.com/u/pawel/summary)
And all my other supporters! Very appreciated!
❤️ Support ❤️
Please support me if you like this project! It will help me keep engaged to update **Druid** and make it even better!
[![Github-sponsors](https://img.shields.io/badge/sponsor-30363D?style=for-the-badge&logo=GitHub-Sponsors&logoColor=#EA4AAA)](https://github.com/sponsors/insality) [![Ko-Fi](https://img.shields.io/badge/Ko--fi-F16061?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/insality) [![BuyMeACoffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/insality)
### Druid 1.1
Hello! Druid 1.1 is here! It's brings a lot of new features and improvements. Let's dive in!
---
**Milestone**:
**Changelog 1.1**
- Remove external annotations, remove old HTML API page
- Fully annotated code and new API readme page (hope more comfortable to use)
- Widgets here!
- A replacement for `custom_component`. Basically it's the same, but widgets contains no boilerplate code and more convinient to use.
- Now I can include a kind of `widgets` with Druid and you can use it almost instantly in your project.
- Removed `druid.register()`. Now all components are available by default and available with `self.druid:new_*` functions
- This means the druid will be bigger in size, but it's much comfortable to use
- In case you want to delete components you not using, you can do it in fork in `druid.lua` file
- Any additional widgets, color library will be not included until you use it
- Remove `druid.event`, replaced with defold-event library. Now it required to double dependency to use Druid.
- Add Druid UI kit, contains atlas so now you can use Druid GUI files in your projects.
- Contains mostly basic shapes for my UI and can contains several icons. Atlas is a small, only `128x128` size and will be included in build only if you use it.
- [Text]: Add `trim_left` and `scale_then_trim_left` text adjust modes
- [Text]: Add `set_text` function instead `set_to` (now it deprecated)
- Add `druid.bindings` module to handle cross-context widgets
- Add `druid.color` module to work with colors and palettes
- Add `container` component to handle more complex adaptive layouts
- [Shaders] Add repeat, hard image stencil and world gui materials
- [Widget] Add widget `mini_graph`
- [Widget] Add widget `memory_panel`
- [Widget] Add widget `fps_panel`
- [Widget] Add widget `properties_panel`
- Include `property_button` widget
- Include `property_checkbox` widget
- Include `property_input` widget
- Include `property_left_right_selector` widget
- Include `property_slider` widget
- Include `property_text` widget
- Include `property_vector3` widget
- Removed old `druid.no_stencil_check` and `druid.no_auto_template` flags. Now it's always disabled

View File

@@ -0,0 +1,54 @@
# Optimize Druid Size
From the Druid 1.1 release now all components are included by default. This was done to simplify the usage of Druid and remove the `druid.register` function which raised a lot of questions about using the non-included components.
This also means that the size of the Druid library for the build now is slightly larger than before. While this will be a not issue for most users, for those who want to redure the build size for a kind of ~50kb in case not using a lot of `extended` components, you can strip these components from the build.
## Stripping Components
You need to download and modify the Druid library inside your project. To strip the not used components you need to edit the `druid/system/druid_instance.lua` file.
For example, if you want to strip the `hotkey` component, you need to remove the `new_hotkey` function from the `M` table.
You need to delete these lines:
```lua
local hotkey = require("druid.extended.hotkey")
---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|nil The callback function
---@param callback_argument any|nil The argument to pass into the callback function
---@return druid.hotkey component Hotkey component
function M:new_hotkey(keys_array, callback, callback_argument)
return self:new(hotkey, keys_array, callback, callback_argument)
end
```
Thats all. Now the Druid library will have no any links to the `hotkey` component and the size of the build will be reduced by size of this component.
## Component Sizes
Here is a table with the size of the components in the Druid library at the state of the Druid 1.1 release. Just for information.
| Component | Size (Desktop) | Size (Mobile) |
|-----------------|----------------|---------------|
| `button` | 4.36 kb | 2.42 kb |
| `text` | 5.31 kb | 2.90 kb |
| `scroll` | 8.27 kb | 4.73 kb |
| `grid` | 5.97 kb | 2.87 kb |
| `blocker` | 0.66 kb | 0.45 kb |
| `back_handler` | 0.57 kb | 0.42 kb |
| `hover` | 2.31 kb | 1.34 kb |
| `drag` | 3.73 kb | 2.17 kb |
| `progress` | 2.76 kb | 1.64 kb |
| `slider` | 2.67 kb | 1.66 kb |
| `swipe` | 2.02 kb | 1.23 kb |
| `input` | 5.59 kb | 3.38 kb |
| `timer` | 1.47 kb | 0.94 kb |
| `layout` | 4.96 kb | 2.83 kb |
| `lang_text` | 1.10 kb | 0.63 kb |
| `hotkey` | 2.29 kb | 1.46 kb |
| `data_list` | 3.24 kb | 1.81 kb |
| `container` | 6.86 kb | 3.75 kb |
| `rich_text` | 13.24 kb | 8.27 kb |
| `rich_input` | 4.16 kb | 2.38 kb |

111
wiki/widgets.md Normal file
View File

@@ -0,0 +1,111 @@
# Widgets
What are widgets
## What is widget
Before widget, there are a "custom components". Widgets goes to replace custom components. Basically, it's totally the same thing, only the difference to initialize it.
Let's see at basic custom component template:
```lua
local component = require("druid.component")
local M = component.create("my_component")
function M:init(template, nodes, output_string)
self:set_template(template)
self:set_nodes(nodes)
self.druid = self:get_druid()
self.druid:new_button("button_node_name", print, output_string)
end
```
So the basic components we created with `druid:new()` function.
```lua
local my_component = druid:new("my_component", template, nodes, "Hello world!")
```
Now, let's see how to do it with widgets:
```lua
---@type my_widget: druid.widget
local M = {}
function M:init(output_string)
self.druid:new_button("button_node_name", print, output_string)
end
return M
```
That's all! The same thing, but no any boilerplate code, just a lua table. The druid instance, the templates and nodes are already created.
And you can create your own widgets like this:
```lua
local druid = require("druid.druid")
local my_widget = require("widgets.my_widget.my_widget")
function init(self)
self.druid = druid.new(self)
self.my_widget = self.druid:new_widget(my_widget, template, nodes, "Hello world!")
end
```
So now the creation of "custom components" called as widgets is much easier and cleaner.
## Create a new widget
Let's start from beginning. Widgets usually consist from 2 parts:
1. GUI scene
2. Widget lua module
Make a GUI scene of your widget (user portrait avatar panel, shop window, game panel menu, etc). Make it as you wish, but recomment to add a one `root` node with `name` `root` and make all your nodes as children of this node. This will make much easier to work with the widget.
Let's create a new widget by creating a new file nearby the our GUI scene file.
```lua
-- my_widget.lua
local M = {}
function M:init()
self.root = self:get_node("root")
self.button = self.druid:new_button("button_open", self.open_widget, self)
end
function M:open_widget()
print("Open widget pressed")
end
return M
```
that's a basic about creation. Now we have a widget, where we ask for the root node and use node "button_open" as a button.
Now, let's create a widget inside you game scene.
Place a widget (GUI template) on your main scene. Then you need to import druid and create a new widget instance over this GUI template placed on the scene.
```lua
local druid = require("druid.druid")
local my_widget = require("widgets.my_widget.my_widget")
function init(self)
self.druid = druid.new(self)
self.my_widget = self.druid:new_widget(my_widget, "my_widget")
-- In case we want to clone it and use several times we can pass the nodes table
local array_of_widgets = {}
for index = 1, 10 do
local nodes = gui.clone_tree(self.my_widget.root)
local widget = self.druid:new_widget(my_widget, "my_widget", nodes)
table.insert(array_of_widgets, widget)
end
end
```