diff --git a/README.md b/README.md index b11089e..c2fd370 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,7 @@ [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/insality/druid/ci-workflow.yml?branch=master)](https://github.com/Insality/druid/actions) [![codecov](https://codecov.io/gh/Insality/druid/branch/master/graph/badge.svg)](https://codecov.io/gh/Insality/druid) -**Druid** - powerful Defold component UI library. Use basic and extended **Druid** components or make your own game-specific components to make amazing GUI in your games. - - -## Overview - +**Druid** - most powerful Defold component UI framework. Use basic and extended **Druid** components or make your own game-specific components with ease to make stunning and customizable GUI in your games. ## Setup @@ -26,89 +22,52 @@ You can use the **Druid** extension in your own project by adding this project a Here is a list of [all releases](https://github.com/Insality/druid/releases). ### Input Bindings -Druid uses `/builtins/input/all.input_binding` input bindins. For advanced setup see the Input Binding section in Advanced Setup. +**Druid** uses `/builtins/input/all.input_binding` input bindings. For custom input bindings see the Input Binding section in **_[Advanced Setup](docs_md/advanced-setup.md)_**. + +### Advanced Setup +In case you want to adjust **Druid** to your needs, you can use **_[Advanced Setup](docs_md/advanced-setup.md)_** section. ## Usage -Here only basic usage. -How to read this doc. -Annotations. -Example of advanced usage - different doc. -Example of custom components - different doc. +### Basic usage -## Components +To use **Druid**, first you should create a **Druid** instance to spawn components and add Druids main functions: *update*, *final*, *on_message* and *on_input*. -Here is full **Druid** components list: - -### Basic Components - -| Name | Description | Example |
Preview
| -|------|-------------|---------|---------| -| **[Button](https://insality.github.io/druid/modules/Button.html)** | Basic input component. Handles all types of interactions: click, long click, hold click, double click, etc | [Button Example](https://insality.github.io/druid/druid/?example=general_buttons) | | -| **[Text](https://insality.github.io/druid/modules/Text.html)** | Wrap on GUI text node, handle different text size adjusting, providing additional text API | [Text Example](https://insality.github.io/druid/druid/?example=texts_general) | | -| **[Scroll](https://insality.github.io/druid/modules/Scroll.html)** | Scroll component | [Scroll Example](https://insality.github.io/druid/druid/?example=general_scroll) | | -| **[Blocker](https://insality.github.io/druid/modules/Blocker.html)** | Block user input in node zone area | ❌ | | -| **[Back Handler](https://insality.github.io/druid/modules/BackHandler.html)** | Handle back button (Android back button, backspace key) | ❌ | | -| **[Static Grid](https://insality.github.io/druid/modules/StaticGrid.html)** | Component to manage node positions with equal sizes | [Static Gid Example](https://insality.github.io/druid/druid/?example=general_grid) | | -| **[Hover](https://insality.github.io/druid/modules/Hover.html)** | Handle hover node state on node | ❌ | | -| **[Swipe](https://insality.github.io/druid/modules/Swipe.html)** | Handle swipe gestures on node | [Swipe Example](https://insality.github.io/druid/druid/?example=general_swipe) | | -| **[Drag](https://insality.github.io/druid/modules/Drag.html)** | Handle drag input on node | [Drag Example](https://insality.github.io/druid/druid/?example=general_drag) | | - - -### Extended components -> Extended components before usage should be registered in **Druid** with `druid.register()` function. - -| Name | Description | Example |
Preview
| -|------|-------------|---------|---------| -| **[Checkbox](https://insality.github.io/druid/modules/Checkbox.html)** | Checkbox component | [Checkbox Example](https://insality.github.io/druid/druid/?example=general_checkboxes) | | -| **[Checkbox group](https://insality.github.io/druid/modules/CheckboxGroup.html)** | Several checkboxes in one group | [Checkbox group Example](https://insality.github.io/druid/druid/?example=general_checkboxes) | | -| **[Radio group](https://insality.github.io/druid/modules/RadioGroup.html)** | Several checkboxes in one group with a single choice | [Radio Group Example](https://insality.github.io/druid/druid/?example=general_checkboxes) | | -| **[Dynamic Grid](https://insality.github.io/druid/modules/DynamicGrid.html)** | Component to manage node positions with different sizes. Only in one row or column | [Dynamic Grid Example](https://insality.github.io/druid/druid/?example=general_grid) | | -| **[Data List](https://insality.github.io/druid/modules/DataList.html)** | Component to manage data for huge datasets in scroll | [Data List Example](https://insality.github.io/druid/druid/?example=general_data_list) | | -| **[Input](https://insality.github.io/druid/modules/Input.html)** | User text input component | [Input Example](https://insality.github.io/druid/druid/?example=general_input) | | -| **[Lang text](https://insality.github.io/druid/modules/LangText.html)** | Wrap on Text component to handle localization | ❌ | | -| **[Progress](https://insality.github.io/druid/modules/Progress.html)** | Progress bar component | [Progress Example](https://insality.github.io/druid/druid/?example=general_progress_bar) | | -| **[Slider](https://insality.github.io/druid/modules/Slider.html)** | Slider component | [Slider Example]() | | -| **[Timer](https://insality.github.io/druid/modules/Timer.html)** | Handle timers on GUI text node | ❌ | | -| **[Hotkey](https://insality.github.io/druid/modules/Hotkey.html)** | Handle keyboard hotkeys with key modificators | [Hotkey Example](https://insality.github.io/druid/druid/?example=general_hokey) | | -| **[Layout](https://insality.github.io/druid/modules/Layout.html)** | Handle node size depends on layout mode and screen aspect ratio | [Layout Example](https://insality.github.io/druid/druid/?example=general_layout) | | - -For a complete overview, see: **_[components.md](docs_md/01-components.md)_**. - - -## Basic usage - -To use **Druid**, first you should create a Druid instance to spawn components and add Druids main engine functions: *update*, *final*, *on_message* and *on_input*. - -All **Druid** components take node name string as arguments, don't do `gui.get_node()` before. +All **Druid** components take node name string as argument. In in some cases you don't have the node name you can pass the `gui.get_node()` instead. All **Druid** and component methods are called with `:` like `self.druid:new_button()`. ```lua local druid = require("druid.druid") -local function button_callback(self) - print("Button was clicked!") +-- All component callbacks pass "self" as first argument +-- This self is a context data passed in `druid.new(context)` +local function on_button_callback(self) + print("The button clicked!") end function init(self) self.druid = druid.new(self) - self.druid:new_button("button_node_name", button_callback) + self.button = self.druid:new_button("button_node_name", on_button_callback) end +-- Final is a required function for a correct Druid workflow function final(self) self.druid:final() end +-- The update used in progress bar, scroll and timer basic components function update(self, dt) self.druid:update(dt) end +-- The on_message used for specific Druid events, like language change or layout change function on_message(self, message_id, message, sender) self.druid:on_message(message_id, message, sender) end +-- The on_input used in almost all Druid components function on_input(self, action_id, action) return self.druid:on_input(action_id, action) end @@ -117,9 +76,89 @@ end For all **Druid** instance functions, [see here](https://insality.github.io/druid/modules/DruidInstance.html). + +### API Documentation + +**Druid** has a lot of components and functions. To make it easier to use, **Druid** has a full API documentation with examples and annotations. + +Start read the API documentation [here](hhttps://insality.github.io/druid/modules/Druid.html). + +### EmmyLua Annotations [optional] + +[EmmyLua](https://emmylua.github.io/annotation.html) - annotations for Lua. It's a great tool for Lua code autocompletion in editors like [VSCode](https://github.com/EmmyLua/VSCode-EmmyLua), [IntelliJ IDEA](https://github.com/EmmyLua/IntelliJ-EmmyLua). + +Since the dependencies can't be processed by external editors, for use generated EmmyLua annotations you should copy the _druid/annotations.lua_ to your project. + +For EmmyLua it will be enough. Remember you can _restart emmylua server_ for refresh the changes, if something goes wrong. + +After the annotations is processed, you should point the type of Druid in requires: +```lua +---@type druid +local druid = require("druid.druid") + +-- Now the autocomplete is working +``` + + + +### Advanced Usage + +If you looking for more advanced usage, see the [Advanced Usage](docs_md/advanced-usage.md) section. + + +### Create custom components + +If you want to create your own components, see the [Create Custom Components](docs_md/create-custom-components.md) section. + +The custom components is the most powerful feature of **Druid**. You can create your own components with ease and use it in your game. + + +## Druid Components + +Here is full **Druid** components list. + +### Basic Components + +> Basic components always included in the build and available for use. + +| Name | Description | Example |
Preview
| +|------|-------------|---------|---------| +| **[Button](https://insality.github.io/druid/modules/Button.html)** | Logic over GUI Node. Handle the user click interactions: click, long click, double click, etc. | [Button Example](https://insality.github.io/druid/druid/?example=general_buttons) | | +| **[Text](https://insality.github.io/druid/modules/Text.html)** | Logic over GUI Text. By default Text component fit the text inside text node size area with different adjust modes. | [Text Example](https://insality.github.io/druid/druid/?example=texts_general) | | +| **[Scroll](https://insality.github.io/druid/modules/Scroll.html)** | Logic over two GUI Nodes: input and content. Provides basic behaviour for scrollable content. | [Scroll Example](https://insality.github.io/druid/druid/?example=general_scroll) | | +| **[Blocker](https://insality.github.io/druid/modules/Blocker.html)** | Logic over GUI Node. Don't pass any user input below node area size. | ❌ | | +| **[Back Handler](https://insality.github.io/druid/modules/BackHandler.html)** | Call callback on user "Back" action. It's a Android back button or keyboard backspace key | ❌ | | +| **[Static Grid](https://insality.github.io/druid/modules/StaticGrid.html)** | Logic over GUI Node. Component to manage node positions with all equal node sizes. | [Static Gid Example](https://insality.github.io/druid/druid/?example=general_grid) | | +| **[Hover](https://insality.github.io/druid/modules/Hover.html)** | Logic over GUI Node. Handle hover action over node. For both: mobile touch and mouse cursor. | ❌ | | +| **[Swipe](https://insality.github.io/druid/modules/Swipe.html)** | Logic over GUI Node. Handle swipe gestures over node. | [Swipe Example](https://insality.github.io/druid/druid/?example=general_swipe) | | +| **[Drag](https://insality.github.io/druid/modules/Drag.html)** | Logic over GUI Node. Handle drag input actions. Can be useful to make on screen controlls. | [Drag Example](https://insality.github.io/druid/druid/?example=general_drag) | | + + +### Extended components + +> Extended components before usage should be registered in **Druid** with `druid.register()` function. + +| Name | Description | Example |
Preview
| +|------|-------------|---------|---------| +| **[Checkbox](https://insality.github.io/druid/modules/Checkbox.html)** | Switch node state on click event. | [Checkbox Example](https://insality.github.io/druid/druid/?example=general_checkboxes) | | +| **[Checkbox group](https://insality.github.io/druid/modules/CheckboxGroup.html)** | Group of checkbox components. | [Checkbox group Example](https://insality.github.io/druid/druid/?example=general_checkboxes) | | +| **[Radio group](https://insality.github.io/druid/modules/RadioGroup.html)** | Like checkbox group but with single choise only. | [Radio Group Example](https://insality.github.io/druid/druid/?example=general_checkboxes) | | +| **[Dynamic Grid](https://insality.github.io/druid/modules/DynamicGrid.html)** | Logic over GUI Node. Component to manage node positions with all different node sizes. Only one direction: horizontal or vertical. | [Dynamic Grid Example](https://insality.github.io/druid/druid/?example=general_grid) | | +| **[Data List](https://insality.github.io/druid/modules/DataList.html)** | Logic over Scroll and Grid components. Create only visible GUI nodes or components to make "infinity" scroll befaviour | [Data List Example](https://insality.github.io/druid/druid/?example=general_data_list) | | +| **[Input](https://insality.github.io/druid/modules/Input.html)** | Logic over GUI Node and GUI Text (or Text component). Provides basic user text input. | [Input Example](https://insality.github.io/druid/druid/?example=general_input) | | +| **[Lang text](https://insality.github.io/druid/modules/LangText.html)** | Logic over Text component to handle localization. Can be translated in real-time with `druid.on_language_change` | ❌ | | +| **[Progress](https://insality.github.io/druid/modules/Progress.html)** | Logic over GUI Node. Handle node size and scale to handle progress node size. | [Progress Example](https://insality.github.io/druid/druid/?example=general_progress_bar) | | +| **[Slider](https://insality.github.io/druid/modules/Slider.html)** | Logic over GUI Node. Handle draggable node with position restrictions. | [Slider Example]() | | +| **[Timer](https://insality.github.io/druid/modules/Timer.html)** | Logic over GUI Text. Handle basic timer functions. | ❌ | | +| **[Hotkey](https://insality.github.io/druid/modules/Hotkey.html)** | Allow to set callbacks for keyboard hotkeys with key modificators. | [Hotkey Example](https://insality.github.io/druid/druid/?example=general_hokey) | | +| **[Layout](https://insality.github.io/druid/modules/Layout.html)** | Logic over GUI Node. Handle node size depends on layout mode and screen aspect ratio. Contains helpers to build more complex UI layout. | [Layout Example](https://insality.github.io/druid/druid/?example=general_layout) | | + +For a complete overview, see: **_[components.md](docs_md/01-components.md)_**. + + ## Druid Events -Any **Druid** components as callbacks use [Druid Events](https://insality.github.io/druid/modules/DruidEvent.html). In component API ([button example](https://insality.github.io/druid/modules/Button.html#on_click)) pointed list of component events. You can manually subscribe to those events with the following API: +Any **Druid** components as callbacks use [Druid Events](https://insality.github.io/druid/modules/DruidEvent.html). In component API ([button example](https://insality.github.io/druid/modules/Button.html#on_click)) pointed list of component events. You can manually subscribe to these events with the following API: - **event:subscribe**(callback) @@ -129,57 +168,29 @@ Any **Druid** components as callbacks use [Druid Events](https://insality.github You can subscribe several callbacks to a single event. -## Druid Lifecycle - -Here is full Druid lifecycle setup for your ***.gui_script** file: -```lua -local druid = require("druid.druid") - -function init(self) - self.druid = druid.new(self) -end - -function final(self) - self.druid:final() -end - -function update(self, dt) - self.druid:update(dt) -end - -function on_input(self, action_id, action) - return self.druid:on_input(action_id, action) -end - -function on_message(self, message_id, message, sender) - self.druid:on_message(message_id, message, sender) -end -``` - -- *final* is a **required** function for a correct Druid lifecycle -- *on_input* is used in almost all Druid components -- *update* in used in progress bar, scroll and timer base components -- *on_message* is used for specific Druid events, like language change or layout change - -It is recommended to fully integrate all **Druid** lifecycles functions. - - ## Details -- Druid input goes as stack. Last created button will checked first. So create your GUI from back -- Don't forget about `return` in `on_input`: `return self.druid:on_input()`. It is needed if you have more than 1 acquire inputs (several Druid, other input system, etc) -- By default, Druid will automatically _acquire_input_focus_. So you don't need do it manually. But only if you have components which require _on_input_ -- If you want to delete a node which has a Druid component, don't forget to remove it via `druid:remove(component)` +- **Druid** input goes as stack. Last created button will be checked first. Create your input GUI component from back to front. +- Don't forget about `return` in `on_input`: `return self.druid:on_input()`. It is required if you have more than 1 acquire inputs (several Druid, other input system, etc) +- Druid automatically call _acquire_input_focus_ if you have input components. So you don't required to call it manually. +- If you want to delete a **Druid** component node, don't forget to remove it via `druid:remove(component)` [See full FAQ here](docs_md/FAQ.md) ## Examples -See the [**example folder**](https://github.com/Insality/druid/tree/develop/example) for examples of how to use **Druid** +### HTML5 Live Examples Try the [**HTML5 version**](https://insality.github.io/druid/druid/) of the **Druid** example app +Each example page has a link to the example code directly, so it will help you to faster understand how to use **Druid** + + +### Code examples + +See the [**example folder**](https://github.com/Insality/druid/tree/develop/example) for examples of how to use **Druid** + ## Documentation diff --git a/deployer_build_stats.csv b/deployer_build_stats.csv index 09bbddc..d42a7dd 100644 --- a/deployer_build_stats.csv +++ b/deployer_build_stats.csv @@ -9,3 +9,4 @@ date,sha,version,build_size,build_time,platform,mode,is_cache_using,commits_coun 2022-08-29T18:46:47Z,13003e472169cbd261e703eca7b133adf64a24f7,0.9.592,2316,40,js-web,release,true,592 2022-09-09T17:55:42Z,072507cc9e715541bdee7636d2e5eeeb3c22a57d,0.10.603,2340,43,js-web,release,true,603 2022-09-09T18:00:07Z,072507cc9e715541bdee7636d2e5eeeb3c22a57d,0.10.603,2340,5,js-web,release,true,603 +2023-07-05T20:19:22Z,d0062c2a78e618871ebb4c8ee66b1509b763f069,0.10.671,3292,12,x86_64-linux,headless,true,671 diff --git a/docs_md/advanced-setup.md b/docs_md/advanced-setup.md index 0704e26..1c1393b 100644 --- a/docs_md/advanced-setup.md +++ b/docs_md/advanced-setup.md @@ -1,5 +1,10 @@ +# Advanced Druid setup + + ## Input bindings +As default input bindings **Druid** uses the `/builtins/input/all.input_binding`. + **Druid** requires the following input bindings: - Mouse trigger - `Button 1` -> `touch` _For basic input components_ @@ -14,6 +19,7 @@ ![](media/input_binding_2.png) ![](media/input_binding_1.png) + ## Change key bindings [optional] If you have to use your own key bindings (and key name), you can change it in your *game.project* file. @@ -59,6 +65,8 @@ no_auto_template = 1 ## Stencil check [optional] When creating input components inside stencil nodes, **Druid** automatically setup `component:set_click_zone()` on _late_init_ component step to restrict input clicks outside this stencil zone. +For example: button inside scroll stencil nodes. + To disable this feature add next field in your _game.project_ file ``` [druid] @@ -66,7 +74,7 @@ no_stencil_check = 1 ``` -## Code [optional] +## Code bindings [optional] Adjust **Druid** settings, if needed: ```lua @@ -74,21 +82,30 @@ local druid = require("druid.druid") -- Used for button component and custom components -- Callback should play sound by name: function(sound_id) ... end -druid.set_sound_function(callback) +druid.set_sound_function(function(sound_id) + -- sound_system.play(sound_id) +end) -- Used for lang_text component -- Callback should return localized string by locale id: function(locale_id) ... end -druid.set_text_function(callback) +druid.set_text_function(function(locale_id) + -- return lang.get(locale_id) +end) -- Used for change default Druid style druid.set_default_style(your_style) -- Call this function on language changing in the game, -- to retranslate all lang_text components: -druid.on_language_change() +local function on_language_change() + druid.on_language_change() +end -- Call this function inside window.set_listener -- to catch game focus lost/gained callbacks: -- window.set_listener(function(self, event, data) druid.on_window_callback(event, data) end)) -druid.on_window_callback(event) +local function on_window_callback(self, event, data) + druid.on_window_callback(event) +end +window.set_listener(on_window_callback) ``` diff --git a/druid/annotations.lua b/druid/annotations.lua index e9fd95f..159a221 100644 --- a/druid/annotations.lua +++ b/druid/annotations.lua @@ -20,7 +20,7 @@ function druid.on_language_change() end function druid.on_window_callback(event) end --- Register a new external Druid component. ---- You can register your own components by creating them with the druid:new_{name} function. For example, if you want to register a component called "my_component", you can create it using druid:new_my_component(...). This can be useful if you have your own "basic" components that you don't want to re-create each time. +--- You can register your own components to make new alias: the druid:new_{name} function. For example, if you want to register a component called "my_component", you can create it using druid:new_my_component(...). This can be useful if you have your own "basic" components that you don't want to re-create each time. ---@param name string module name ---@param module table lua table with component function druid.register(name, module) end @@ -125,7 +125,7 @@ function druid__base_component.component:set_input_enabled(self, state) end function druid__base_component.component:set_input_priority(self, value, is_temporary) end --- Set current component nodes ---- Used if your component nodes was cloned with `gui.clone_tree` +--- Use if your component nodes was cloned with `gui.clone_tree` and you got the node tree. ---@param self druid.base_component @{BaseComponent} ---@param nodes table BaseComponent nodes table ---@return druid.base_component @{BaseComponent} @@ -162,31 +162,28 @@ function druid__blocker.set_enabled(self, state) end ---@class druid.button : druid.base_component ----@field anim_node node Animation node ----@field click_zone node Restriction zone ----@field hash node_id The hash of trigger node ----@field hover druid.hover Druid hover logic component ----@field node node Trigger node ----@field on_click druid.event On release button callback(self, params, button_instance) ----@field on_click_outside druid.event On click outside of button(self, params, button_instance) ----@field on_double_click druid.event On double tap button callback(self, params, button_instance, click_amount) ----@field on_hold_callback druid.event On button hold before long_click callback(self, params, button_instance, time) ----@field on_long_click druid.event On long tap button callback(self, params, button_instance, time) ----@field on_pressed druid.event On pressed button callback(self, params, button_instance) ----@field on_repeated_click druid.event On repeated action button callback(self, params, button_instance, click_amount) ----@field params any Params to click callbacks ----@field pos vector3 Initial pos of anim_node ----@field start_pos vector3 Initial pos of anim_node ----@field start_scale vector3 Initial scale of anim_node +---@field anim_node node Button animation node. +---@field click_zone node Additional button click area, defined by another GUI Node +---@field hash node_id The GUI node id from button node +---@field hover druid.hover @{Hover}: Button Hover component +---@field node node Button clickable node +---@field on_click druid.event @{DruidEvent}: Event on successful release action over button. +---@field on_click_outside druid.event @{DruidEvent}: Event calls if click event was outside of button. +---@field on_double_click druid.event @{DruidEvent}: Event on double tap action over button. +---@field on_hold_callback druid.event @{DruidEvent}: Event calls every frame before on_long_click event. +---@field on_long_click druid.event @{DruidEvent}: Event on long tap action over button. +---@field on_pressed druid.event @{DruidEvent}: Event triggered if button was pressed by user. +---@field on_repeated_click druid.event @{DruidEvent}: Event on repeated action over button. +---@field params any Custom args for any Button event. ---@field style druid.button.style Component style params. local druid__button = {} ---- Get key-code to trigger this button +--- Get current key name to trigger this button. ---@param self druid.button ----@return hash The action_id of the key +---@return hash The action_id of the input key function druid__button.get_key_trigger(self) end ---- Component init function +--- Button component constructor ---@param self druid.button @{Button} ---@param node node Gui node ---@param callback function Button callback @@ -194,9 +191,9 @@ function druid__button.get_key_trigger(self) end ---@param anim_node node Button anim node (node, if not provided) function druid__button.init(self, node, callback, params, anim_node) end ---- Return button enabled state +--- Get button enabled state. ---@param self druid.button @{Button} ----@return bool True, if button is enabled +---@return bool True, if button is enabled now, False overwise function druid__button.is_enabled(self) end --- Set function for additional check for button click availability @@ -206,29 +203,30 @@ function druid__button.is_enabled(self) end ---@return druid.button Current button instance function druid__button.set_check_function(self, check_function, failure_callback) end ---- Strict button click area. ---- Useful for no click events outside stencil node +--- Set additional button click area. +--- Useful to restrict click outside out stencil node or scrollable content. This functions calls automatically if you don't disable it in game.project: druid.no_stencil_check ---@param self druid.button @{Button} ---@param zone node Gui node ---@return druid.button Current button instance function druid__button.set_click_zone(self, zone) end ---- Set enabled button component state +--- Set button enabled state. +--- The style.on_set_enabled will be triggered. Disabled button is not clickable. ---@param self druid.button @{Button} ---@param state bool Enabled state ---@return druid.button Current button instance function druid__button.set_enabled(self, state) end ---- Set buttom click mode to call itself inside html5 callback in user interaction event It required to do protected stuff like copy/paste text, show html keyboard, etc The HTML5 button don't call any events except on_click +--- Set buttom click mode to call itself inside html5 callback in user interaction event It required to do protected stuff like copy/paste text, show html keyboard, etc The HTML5 button doesn't call any events except on_click event ---@protected ---@param self druid.button ---@param is_html_mode boolean If true - button will be called inside html5 callback ---@return druid.button Current button instance function druid__button.set_html5_user_interaction(self, is_html_mode) end ---- Set key-code to trigger this button +--- Set key name to trigger this button by keyboard. ---@param self druid.button @{Button} ----@param key hash The action_id of the key +---@param key hash The action_id of the input key ---@return druid.button Current button instance function druid__button.set_key_trigger(self, key) end @@ -647,7 +645,7 @@ function druid__input.get_text(self) end --- Component init function ---@param self druid.input @{Input} ----@param click_node node Button node to enabled input component +---@param click_node node Node to enabled input component ---@param text_node node|druid.text Text node what will be changed on user input. You can pass text component instead of text node name @{Text} ---@param keyboard_type number Gui keyboard type for input field function druid__input.init(self, click_node, text_node, keyboard_type) end @@ -942,6 +940,31 @@ function druid__rich_input.set_placeholder(self, placeholder_text) end ---@field component field The component druid instance local druid__rich_text = {} +--- Clear all created words. +function druid__rich_text.clean() end + +--- Get all current words. +---@return table Words +function druid__rich_text.get_words() end + +--- Rich Text component 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 + +--- Set text for Rich Text +---@param self druid.rich_text @{RichText} +---@param text string The text to set +---@return table words +---@return table line_metrics +function druid__rich_text.set_text(self, text) end + +--- Get all words, which has a passed tag +---@param tag string +---@return table Words +function druid__rich_text.tagged(tag) end + ---@class druid.scroll : druid.base_component ---@field available_pos vector4 Available position for content node: (min_x, max_y, max_x, min_y) @@ -1578,7 +1601,7 @@ function druid_instance.on_input(self, action_id, action) end ---@param sender hash Sender from on_message function druid_instance.on_message(self, message_id, message, sender) end ---- Remove component from Druid instance. +--- Remove created component from Druid instance. --- Component `on_remove` function will be invoked, if exist. ---@param self druid_instance ---@param component Component Component instance diff --git a/druid/base/button.lua b/druid/base/button.lua index c295480..d13bed5 100755 --- a/druid/base/button.lua +++ b/druid/base/button.lua @@ -1,56 +1,131 @@ -- Copyright (c) 2021 Maksim Tuprikov . This code is licensed under MIT license ---- Component to handle basic GUI button +--- Druid Component to handle the user click interactions: click, long click, double click, etc. +-- # Overview # +-- +-- The most generic and useful component you can use. Set any GUI node clickable and providing different callbacks. +-- +-- # Notes # +-- +-- • The click callback will not trigger if between pressed and released state cursor was outside of node zone +-- +-- • If button have double click event subscriber and it is triggered, usual callback will be not triggered +-- +-- • 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` +-- +-- @usage +-- local function on_button_click(self, args, button) +-- print("Button has clicked with params: " .. args) +-- print("Also the button component is passed in callback params") +-- end +-- +-- local custom_args = "Any variable to pass inside callback" +-- local button = self.druid:new_button("button_name", on_button_click, custom_args) +-- -- @module Button -- @within BaseComponent -- @alias druid.button ---- On release button callback(self, params, button_instance) + +--- @{DruidEvent}: Event on successful release action over button. +-- @usage +-- -- Custom args passed in Button constructor +-- button.on_click:subscribe(function(self, custom_args, button_instance) +-- print("On button click!") +-- end) -- @tfield DruidEvent on_click @{DruidEvent} ---- On repeated action button callback(self, params, button_instance, click_amount) + +--- @{DruidEvent}: Event on repeated action over button. +-- +-- This callback will be triggered if user hold the button. The repeat rate pick from `input.repeat_interval` in game.project +-- @usage +-- -- Custom args passed in Button constructor +-- button.on_repeated_click:subscribe(function(self, custom_args, button_instance, click_count) +-- print("On repeated Button click!") +-- end) -- @tfield DruidEvent on_repeated_click @{DruidEvent} ----On long tap button callback(self, params, button_instance, time) + +--- @{DruidEvent}: Event on long tap action over button. +-- +-- This callback will be triggered if user pressed the button and hold the some amount of time. +-- The amount of time picked from button style param: LONGTAP_TIME +-- @usage +-- -- Custom args passed in Button constructor +-- button.on_long_click:subscribe(function(self, custom_args, button_instance, hold_time) +-- print("On long Button click!") +-- end) -- @tfield DruidEvent on_long_click @{DruidEvent} ----On double tap button callback(self, params, button_instance, click_amount) + +--- @{DruidEvent}: Event on double tap action over button. +-- +-- If secondary click was too fast after previous one, the double +-- click will be called instead usual click (if on_double_click subscriber exists) +-- @usage +-- -- Custom args passed in Button constructor +-- button.on_double_click:subscribe(function(self, custom_args, button_instance, click_amount) +-- print("On double Button click!") +-- end) -- @tfield DruidEvent on_double_click @{DruidEvent} ----On button hold before long_click callback(self, params, button_instance, time) + +--- @{DruidEvent}: Event calls every frame before on_long_click event. +-- +-- If long_click subscriber exists, the on_hold_callback will be called before long_click trigger. +-- +-- Usecase: Animate button progress of long tap +-- @usage +-- -- Custom args passed in Button constructor +-- button.on_double_click:subscribe(function(self, custom_args, button_instance, time) +-- print("On hold Button callback!") +-- end) -- @tfield DruidEvent on_hold_callback @{DruidEvent} ----On click outside of button(self, params, button_instance) + +--- @{DruidEvent}: Event calls if click event was outside of button. +-- +-- This event will be triggered for each button what was not clicked on user click action +-- +-- Usecase: Hide the popup when click outside +-- @usage +-- -- Custom args passed in Button constructor +-- button.on_click_outside:subscribe(function(self, custom_args, button_instance) +-- print("On click Button outside!") +-- end) -- @tfield DruidEvent on_click_outside @{DruidEvent} ----On pressed button callback(self, params, button_instance) + +--- @{DruidEvent}: Event triggered if button was pressed by user. +-- @usage +-- -- Custom args passed in Button constructor +-- button.on_pressed:subscribe(function(self, custom_args, button_instance) +-- print("On Button pressed!") +-- end) -- @tfield DruidEvent on_pressed @{DruidEvent} ----Trigger node +--- Button clickable node -- @tfield node node ----The hash of trigger node +---The GUI node id from button node -- @tfield node_id hash ----Animation node +--- Button animation node. +-- In default case equals to clickable node. +-- +-- Usecase: You have the big clickable panel, but want to animate only one small icon on it. -- @tfield[opt=node] node anim_node ----Initial scale of anim_node --- @tfield vector3 start_scale - ----Initial pos of anim_node --- @tfield vector3 start_pos - ----Initial pos of anim_node --- @tfield vector3 pos - ----Params to click callbacks +---Custom args for any Button event. Setup in Button constructor -- @tfield any params ----Druid hover logic component +--- @{Hover}: Button Hover component -- @tfield Hover hover @{Hover} ----Restriction zone +--- Additional button click area, defined by another GUI Node -- @tfield[opt] node click_zone --- @@ -195,7 +270,7 @@ function Button.on_style_change(self, style) end ---- Component init function +--- Button component constructor -- @tparam Button self @{Button} -- @tparam node node Gui node -- @tparam function callback Button callback @@ -357,10 +432,15 @@ function Button.on_message_input(self, node_id, message) end ---- Set enabled button component state +--- Set button enabled state. +-- The style.on_set_enabled will be triggered. +-- Disabled button is not clickable. -- @tparam Button self @{Button} -- @tparam bool state Enabled state -- @treturn Button Current button instance +-- @usage +-- button:set_enabled(false) +-- button:set_enabled(true) function Button.set_enabled(self, state) self.disabled = not state self.hover:set_enabled(state) @@ -370,19 +450,25 @@ function Button.set_enabled(self, state) end ---- Return button enabled state +--- Get button enabled state. -- @tparam Button self @{Button} --- @treturn bool True, if button is enabled +-- @treturn bool True, if button is enabled now, False overwise +-- @usage +-- local is_enabled = button:is_enabled() function Button.is_enabled(self) return not self.disabled end ---- Strict button click area. Useful for --- no click events outside stencil node +--- Set additional button click area. +-- Useful to restrict click outside out stencil node or scrollable content. +-- +-- This functions calls automatically if you don't disable it in game.project: druid.no_stencil_check -- @tparam Button self @{Button} -- @tparam node zone Gui node -- @treturn Button Current button instance +-- @usage +-- button:set_click_zone("stencil_node") function Button.set_click_zone(self, zone) self.click_zone = self:get_node(zone) self.hover:set_click_zone(zone) @@ -391,10 +477,12 @@ function Button.set_click_zone(self, zone) end ---- Set key-code to trigger this button +--- Set key name to trigger this button by keyboard. -- @tparam Button self @{Button} --- @tparam hash key The action_id of the key +-- @tparam hash key The action_id of the input key -- @treturn Button Current button instance +-- @usage +-- button:set_key_trigger("key_space") function Button.set_key_trigger(self, key) self.key_trigger = hash(key) @@ -402,9 +490,11 @@ function Button.set_key_trigger(self, key) end ---- Get key-code to trigger this button +--- Get current key name to trigger this button. -- @tparam Button self --- @treturn hash The action_id of the key +-- @treturn hash The action_id of the input key +-- @usage +-- local key_hash = button:get_key_trigger() function Button.get_key_trigger(self) return self.key_trigger end @@ -423,10 +513,12 @@ end --- Set buttom click mode to call itself inside html5 callback in user interaction event -- It required to do protected stuff like copy/paste text, show html keyboard, etc --- The HTML5 button don't call any events except on_click +-- The HTML5 button doesn't call any events except on_click event -- @tparam Button self -- @tparam[opt] boolean is_html_mode If true - button will be called inside html5 callback -- @treturn Button Current button instance +-- @usage +-- button:set_html5_user_interaction(true) function Button.set_html5_user_interaction(self, is_html_mode) self._is_html5_mode = is_html_mode and html5 return self diff --git a/druid/component.lua b/druid/component.lua index 2e1e1b6..fb29b40 100644 --- a/druid/component.lua +++ b/druid/component.lua @@ -64,7 +64,7 @@ BaseComponent.SPECIFIC_UI_MESSAGES = { local uid = 0 -function BaseComponent.static.get_uid() +function BaseComponent.create_uid() uid = uid + 1 return uid end @@ -128,7 +128,7 @@ end --- Set current component nodes -- --- Used if your component nodes was cloned with `gui.clone_tree` +-- Use if your component nodes was cloned with `gui.clone_tree` and you got the node tree. -- @function component:set_nodes -- @tparam BaseComponent self @{BaseComponent} -- @tparam table nodes BaseComponent nodes table @@ -226,7 +226,7 @@ end -- @tparam BaseComponent self @{BaseComponent} -- @treturn string The component name function BaseComponent.get_name(self) - return self._component.name .. self:get_uid() + return self._component.name .. BaseComponent.create_uid() end @@ -362,7 +362,7 @@ end --- Basic constructor of component. It will call automaticaly --- by `BaseComponent.static.create` +-- by `BaseComponent.create` -- @function component:initialize -- @tparam BaseComponent self @{BaseComponent} -- @tparam string name BaseComponent name @@ -375,7 +375,7 @@ function BaseComponent.initialize(self, name, input_priority) default_input_priority = input_priority or const.PRIORITY_INPUT, is_debug = false, _is_input_priority_changed = true, -- Default true for sort once time after GUI init - _uid = BaseComponent.get_uid() + _uid = BaseComponent.create_uid() } end @@ -512,11 +512,11 @@ end --- Create new component. It will inheritance from basic Druid component. --- @function BaseComponent.static.create +-- @function BaseComponent.create -- @tparam string name BaseComponent name -- @tparam[opt=DEFAULT] number input_priority The input priority. The bigger number processed first -- @local -function BaseComponent.static.create(name, input_priority) +function BaseComponent.create(name, input_priority) -- Yea, inheritance here local new_class = class(name, BaseComponent) diff --git a/druid/custom/rich_text/rich_text.lua b/druid/custom/rich_text/rich_text.lua index 88bedea..b84191f 100644 --- a/druid/custom/rich_text/rich_text.lua +++ b/druid/custom/rich_text/rich_text.lua @@ -1,6 +1,18 @@ -- Copyright (c) 2022 Maksim Tuprikov . This code is licensed under MIT license --- Druid Rich Text custom component. +-- # Overview # +-- +-- +-- +-- # Notes # +-- +-- @usage +-- local RichText = require("druid.custom.rich_text.rich_text") +-- ... +-- self.rich_text = self.druid:new(RichText, "rich_text") +-- self.rich_text:set_text("Hello, Druid Rich Text!") +-- -- @module RichText -- @within BaseComponent -- @alias druid.rich_text @@ -21,7 +33,11 @@ local SCHEME = { } -function RichText:init(template, nodes) +--- Rich Text component 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() +function RichText.init(self, template, nodes) self:set_template(template) self:set_nodes(nodes) @@ -38,7 +54,12 @@ function RichText:init(template, nodes) end -function RichText:set_text(text) +--- Set text for Rich Text +-- @tparam RichText self @{RichText} +-- @tparam string text The text to set +-- @treturn table words +-- @treturn table line_metrics +function RichText.set_text(self, text) self:clean() local words, settings, line_metrics = rich_text.create(text, self._settings) @@ -56,6 +77,9 @@ function RichText:on_remove() end +--- Get all words, which has a passed tag +-- @tparam string tag +-- @treturn table Words function RichText:tagged(tag) if not self._words then return @@ -65,6 +89,8 @@ function RichText:tagged(tag) end +--- Get all current words. +-- @treturn table Words function RichText:get_words() return self._words end @@ -99,6 +125,7 @@ function RichText:_create_settings() end +--- Clear all created words. function RichText:clean() if self._words then rich_text.remove(self._words) diff --git a/druid/extended/input.lua b/druid/extended/input.lua index 2178d46..da1b220 100755 --- a/druid/extended/input.lua +++ b/druid/extended/input.lua @@ -115,7 +115,7 @@ end --- Component init function -- @tparam Input self @{Input} --- @tparam node click_node Button node to enabled input component +-- @tparam node click_node Node to enabled input component -- @tparam node|Text text_node Text node what will be changed on user input. You can pass text component instead of text node name @{Text} -- @tparam[opt] number keyboard_type Gui keyboard type for input field function Input.init(self, click_node, text_node, keyboard_type) diff --git a/druid/system/druid_instance.lua b/druid/system/druid_instance.lua index ece0568..7f1a45b 100755 --- a/druid/system/druid_instance.lua +++ b/druid/system/druid_instance.lua @@ -288,7 +288,7 @@ function DruidInstance.final(self) end ---- Remove component from Druid instance. +--- Remove created component from Druid instance. -- -- Component `on_remove` function will be invoked, if exist. -- @tparam DruidInstance self diff --git a/media/emmy_lua_preview.png b/media/emmy_lua_preview.png new file mode 100644 index 0000000..eb138a4 Binary files /dev/null and b/media/emmy_lua_preview.png differ