This commit is contained in:
Insality 2025-03-25 22:56:47 +02:00
parent fb9c80b284
commit e83e5a6c84
9 changed files with 116 additions and 75 deletions

37
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,37 @@
# Contribution Guidelines
Hello, Defolder! Thanks for your interest in contributing to the **Druid** project. It's a massive project that has been around for a long time, and it's still growing. This project has a lot of places where you can help!
Finally, there are set of instructions that will help you to contribute to the project.
Thanks for your help!
## Update Documentation
If you see any mistakes in the documentation, you can update it by yourself.
You can push changes to the `master` branch directly. In case of small fixes, please also update the relative API `md` files. If there is a lot of changes, I will regenerate them manually.
## Issue Reporting
If you find any bugs, please report them to the [issue tracker](https://github.com/druid-js/druid/issues).
## Pull Requests
Any pull requests are welcome!
Please, open PR against the `develop` branch. Very nice to have an issue, which this PR fixes.
You fix should contains only changes, which are related to the issue. Also please keep the code style the same!
Thanks <3
## Add or Update Examples
Examples contains a GUI scene, a Druid widget for this GUI. This GUI is included to the `examples.gui` and the information about examples are added in `examples_list.lua` file
You can add new examples or update existing ones.
To add new example, you need to create a new folder in the `examples` directory.

View File

@ -7,7 +7,7 @@
[![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) [![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** - a powerful, flexible and easy to use **Defold** component UI framework. Contains a wide range of components and features to create stunning and customizable GUIs. Provides a powerful way to create, compose and manage your custom components and scenes. **Druid** - a powerful, flexible and easy to use **Defold** component UI framework. Contains a wide range of UI components that you can use to create a beautiful, responsive and customizable GUIs. Provides a powerful way to create, compose and manage your custom components and scenes.
## Druid Example ## Druid Example
@ -15,6 +15,13 @@ Check the [**HTML5 version**](https://insality.github.io/druid/) of the **Druid*
In this example you can inspect a variety of **Druid** components and see how they work. Each example page provides a direct link to the corresponding example code, making it easier for you to understand how to use **Druid**. In this example you can inspect a variety of **Druid** components and see how they work. Each example page provides a direct link to the corresponding example code, making it easier for you to understand how to use **Druid**.
## Features
- **Components** - Provides a extensive set of components, from basic buttons to infinity data lists and rich texts
- **Customizable** - You can customize components appearance and behaviour
- **Widgets** - Powerful way to create your own reusable components
- **Input Handling** - Handles input in a stack-based manner and manage input priority
- **Event Based** - Uses [Defold Event](https://github.com/Insality/defold-event) for components callbacks and communication between components
## Setup ## Setup
@ -42,7 +49,7 @@ Here is a list of [all releases](https://github.com/Insality/druid/releases).
### Library Size ### Library Size
> **Note:** The library size is calculated based on the build report per platform. > **Note:** The library size is calculated based on the build report per platform. Full size contains all components, they can be stripped out in the build process if you don't need them.
| Platform | Full Size | | Platform | Full Size |
| ---------------- | ------------- | | ---------------- | ------------- |
@ -59,11 +66,41 @@ Here is a list of [all releases](https://github.com/Insality/druid/releases).
### Basic usage ### Basic usage
To utilize **Druid**, begin by creating a **Druid** instance to instantiate components and include the main functions of **Druid**: *update*, *final*, *on_message*, and *on_input*. To use **Druid**, begin by creating a **Druid** instance to instantiate components and include the main functions of **Druid**: *update*, *final*, *on_message*, and *on_input*.
When using **Druid** components, provide a node name string as an argument. If you don't have the node name available in some cases, you can pass `gui.get_node()` instead. Create a new `*.gui_script` file and add the following code:
All **Druid** and component methods are invoked using the `:` operator, such as `self.druid:new_button()`. ```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_message(self, message_id, message, sender)
self.druid:on_message(message_id, message, sender)
end
function on_input(self, action_id, action)
return self.druid:on_input(action_id, action)
end
```
Now add this `*.gui_script` as a script to your GUI scene and now you can start creating Druid components.
Always, when you need to pass a node to a component, you can pass a node name string instead of `gui.get_node()` function.
All functions of **Druid** are invoked using the `:` operator, such as `self.druid:new_button()`.
Here is a basic example with a com
```lua ```lua
local druid = require("druid.druid") local druid = require("druid.druid")
@ -71,48 +108,13 @@ local druid = require("druid.druid")
-- All component callbacks pass "self" as first argument -- All component callbacks pass "self" as first argument
-- This "self" is a context data passed in `druid.new(context)` -- This "self" is a context data passed in `druid.new(context)`
local function on_button_callback(self) local function on_button_callback(self)
print("The button clicked!") self.text:set_text("The button clicked!")
end end
function init(self) function init(self)
self.druid = druid.new(self) self.druid = druid.new(self)
self.button = self.druid:new_button("button_node_name", on_button_callback) self.button = self.druid:new_button("button_node_id", on_button_callback)
end self.text = self.druid:new_text("text_node_id", "Hello, Druid!")
-- "final" is a required function for the correct Druid workflow
function final(self)
self.druid:final()
end
-- "update" is used in progress bar, scroll, and timer components
function update(self, dt)
self.druid:update(dt)
end
-- "on_message" is 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
-- "on_input" is used in almost all Druid components
-- The return value from `druid:on_input` is required!
function on_input(self, action_id, action)
return self.druid:on_input(action_id, action)
end
```
For all **Druid** instance functions, [see here](https://insality.github.io/druid/modules/DruidInstance.html).
### Default GUI Script
Put the following code in your GUI script to start using **Druid**.
```lua
local druid = require("druid.druid")
function init(self)
self.druid = druid.new(self)
end end
function final(self) function final(self)

View File

@ -310,7 +310,6 @@ function M:set_web_user_interaction(is_web_mode)
end end
---@param action_id hash The action id ---@param action_id hash The action id
---@return boolean is_match True if the input matches the button ---@return boolean is_match True if the input matches the button
function M:_is_input_match(action_id) function M:_is_input_match(action_id)

View File

@ -9,7 +9,7 @@ local COLOR_Z = hash("color.z")
local M = {} local M = {}
---Get color color by id ---Get color color by string (hex or from palette)
---@param color_id string ---@param color_id string
---@return vector4 ---@return vector4
function M.get(color_id) function M.get(color_id)

View File

@ -135,8 +135,9 @@ end
---@param nodes table<hash, node>|nil ---@param nodes table<hash, node>|nil
---@return druid.instance ---@return druid.instance
function M:get_druid(template, nodes) function M:get_druid(template, nodes)
local context = { _context = self } local druid_instance = setmetatable({
local druid_instance = setmetatable(context, { __index = self._meta.druid }) _context = self
}, { __index = self._meta.druid })
if template then if template then
self:set_template(template) self:set_template(template)

View File

@ -112,12 +112,14 @@ end
---Get a binded widget to the current game object. ---Get a binded widget to the current game object.
--- msg.url(nil, nil, "go_widget") -- current game object
--- msg.url(nil, object_url, "go_widget") -- other game object
---@generic T: druid.widget ---@generic T: druid.widget
---@param widget_class T The class of the widget to return ---@param widget_class T The class of the widget to return
---@param gui_url_string string? GUI url, if nil current gui will be used ---@param gui_url url GUI url
---@return T ---@return T
function M.get_widget(widget_class, gui_url_string) function M.get_widget(widget_class, gui_url)
local gui_url = msg.url(gui_url_string) gui_url = gui_url or msg.url()
local guis = REGISTERED_GUI_WIDGETS[gui_url.socket] local guis = REGISTERED_GUI_WIDGETS[gui_url.socket]
if not guis then if not guis then
return nil return nil

View File

@ -34,6 +34,7 @@
---@field upper fun() ---@field upper fun()
---@field rep fun() ---@field rep fun()
-- This one should be a part of Defold annotations
---@class action ---@class action
---@field value number The amount of input given by the user. This is usually 1 for buttons and 0-1 for analogue inputs. This is not present for mouse movement. ---@field value number The amount of input given by the user. This is usually 1 for buttons and 0-1 for analogue inputs. This is not present for mouse movement.
---@field pressed boolean If the input was pressed this frame. This is not present for mouse movement. ---@field pressed boolean If the input was pressed this frame. This is not present for mouse movement.

View File

@ -8,31 +8,29 @@ local druid_component = require("druid.component")
---The Druid Factory used to create components ---The Druid Factory used to create components
---@class druid.instance ---@class druid.instance
---@field components_all druid.component[] All created components ---@field package input_inited boolean Used to check if input is initialized
---@field components_interest table<string, druid.component[]> All components sorted by interest ---@field package components_all druid.component[] All created components
---@field private _context table Druid context, usually a self of gui script ---@field package components_interest table<string, druid.component[]> All components sorted by interest
---@field private _style table Druid style table ---@field package _context table Druid context, usually a self of gui script
---@field private _is_late_remove_enabled boolean ---@field package _style table Druid style table
---@field private _late_remove druid.component[] ---@field package _late_init_timer_id number Timer id for late init
---@field private _input_blacklist druid.component[]|nil ---@field package _late_remove druid.component[] Components to be removed on late update
---@field private _input_whitelist druid.component[]|nil ---@field package _is_late_remove_enabled boolean Used to check if components should be removed on late update
---@field private input_inited boolean ---@field package _input_blacklist druid.component[]|nil Components that should not receive input
---@field private _late_init_timer_id number ---@field package _input_whitelist druid.component[]|nil Components that should receive input
---@field private _input_components druid.component[]
local M = {} local M = {}
local MSG_ADD_FOCUS = hash("acquire_input_focus")
local MSG_REMOVE_FOCUS = hash("release_input_focus")
local IS_NO_AUTO_INPUT = sys.get_config_int("druid.no_auto_input", 0) == 1 local IS_NO_AUTO_INPUT = sys.get_config_int("druid.no_auto_input", 0) == 1
local INTERESTS_CACHE = {} -- Cache interests per component class in runtime local INTERESTS_CACHE = {} -- Cache interests per component class in runtime
local function set_input_state(self, is_input_inited) local function set_input_state(self, is_input_inited)
if IS_NO_AUTO_INPUT or (self.input_inited == is_input_inited) then if IS_NO_AUTO_INPUT or (self.input_inited == is_input_inited) then
return return
end end
self.input_inited = is_input_inited self.input_inited = is_input_inited
msg.post(".", is_input_inited and MSG_ADD_FOCUS or MSG_REMOVE_FOCUS) msg.post(".", is_input_inited and "acquire_input_focus" or "release_input_focus")
end end
@ -98,7 +96,7 @@ local function register_interests(self, instance)
end end
-- Create the Druid component instance ---Create the Druid component instance
---@param self druid.instance ---@param self druid.instance
---@param instance_class druid.component ---@param instance_class druid.component
---@return druid.component ---@return druid.component
@ -148,8 +146,8 @@ end
---Check whitelists and blacklists for input components ---Check whitelists and blacklists for input components
---@param component druid.component ---@param component druid.component The component to check
---@return boolean ---@return boolean is_can_use True if component can be processed on input step
function M:_can_use_input_component(component) function M:_can_use_input_component(component)
local can_by_whitelist = true local can_by_whitelist = true
local can_by_blacklist = true local can_by_blacklist = true
@ -181,7 +179,7 @@ end
---Druid class constructor which used to create a Druid's components ---Druid class constructor which used to create a Druid's components
---@param context table Druid context. Usually it is self of gui script ---@param context table Druid context. Usually it is self of gui script
---@param style table? Druid style table ---@param style table? Druid style table
---@return druid.instance ---@return druid.instance instance The new Druid instance
function M.create_druid_instance(context, style) function M.create_druid_instance(context, style)
local self = setmetatable({}, { __index = M }) local self = setmetatable({}, { __index = M })
@ -244,8 +242,8 @@ end
---Remove created component from Druid instance. ---Remove created component from Druid instance.
-- ---
-- Component `on_remove` function will be invoked, if exist. ---Component `on_remove` function will be invoked, if exist.
---@generic T: druid.component ---@generic T: druid.component
---@param component T Component instance ---@param component T Component instance
---@return boolean is_removed True if component was removed ---@return boolean is_removed True if component was removed
@ -422,8 +420,8 @@ end
---Calls the on_language_change function in all related components ---Calls the on_language_change function in all related components
-- This one called by global druid.on_language_change, but can be ---This one called by global druid.on_language_change, but can be
-- call manualy to update all translations ---call manualy to update all translations
---@private ---@private
function M:on_language_change() function M:on_language_change()
local components = self.components_interest[const.ON_LANGUAGE_CHANGE] local components = self.components_interest[const.ON_LANGUAGE_CHANGE]
@ -540,7 +538,7 @@ end
local back_handler = require("druid.base.back_handler") local back_handler = require("druid.base.back_handler")
---Create BackHandler component ---Create BackHandler component
---@param callback function|nil The callback(self, custom_args) to call on back event ---@param callback function|event|nil The callback(self, custom_args) to call on back event
---@param params any|nil Callback argument ---@param params any|nil Callback argument
---@return druid.back_handler back_handler The new back handler component ---@return druid.back_handler back_handler The new back handler component
function M:new_back_handler(callback, params) function M:new_back_handler(callback, params)
@ -722,7 +720,7 @@ end
local rich_input = require("druid.custom.rich_input.rich_input") local rich_input = require("druid.custom.rich_input.rich_input")
---Create RichInput component. ---Create RichInput component.
-- As a template please check rich_input.gui layout. ---As a template please check rich_input.gui layout.
---@param template string The template string name ---@param template string The template string name
---@param nodes table|nil Nodes table from gui.clone_tree ---@param nodes table|nil Nodes table from gui.clone_tree
---@return druid.rich_input rich_input The new rich input component ---@return druid.rich_input rich_input The new rich input component

View File

@ -6,7 +6,8 @@ local druid = require("druid.druid")
local widget = require("example.other.go_bindings.go_widget") local widget = require("example.other.go_bindings.go_widget")
function init(self) function init(self)
self.go_widget = druid.get_widget(widget, "#go_widget") local gui_url = msg.url(nil, nil, "go_widget")
self.go_widget = druid.get_widget(widget, gui_url)
self.go_widget:play_animation() self.go_widget:play_animation()
self.go_widget:set_position(go.get_position()) self.go_widget:set_position(go.get_position())