Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
ce49c3b
Fix helper.get_screen_aspect_koef() for gui layouts.
May 15, 2025
52659a9
Add button to drag in drag_to_node example
Insality May 15, 2025
488e78c
Update layout annotations
Insality May 15, 2025
d939d01
Add loading palette to color module
Insality May 15, 2025
b982bc8
Add dirty function to make node visible in scroll
Insality May 15, 2025
8ddb6e4
Open button_* functions to call a button callbacks directly
Insality May 15, 2025
9a0f341
Merge pull request #311 from rigo128/fix_get_screen_aspect_koef
Insality May 15, 2025
22c4954
Add scenes to properties panel
Insality May 17, 2025
2133492
Update properties panel, add refresh button
Insality May 17, 2025
fe955b6
Update properties for panel
Insality May 18, 2025
2e1f280
Blocker by default is enabled, update color palette, add rich text sp…
Insality May 27, 2025
0cf5ba3
Update properties panel
Insality May 27, 2025
9794296
Remove colors from rich text
Insality May 28, 2025
350770b
Fix for Druid containers mode
Insality May 28, 2025
16a5d99
Update for queue events
Insality May 28, 2025
8fc5f4d
Merge branch 'properties_panel' into develop
Insality May 28, 2025
7b5264a
Update changelog
Insality May 28, 2025
8fde964
Revert "Merge pull request #311 from rigo128/fix_get_screen_aspect_koef"
Insality May 28, 2025
a8aea0a
Merge branch 'master' into develop
Insality May 28, 2025
3606c9f
Fix color check
Insality Jun 19, 2025
b35b584
Merge branch 'master' into develop
Insality Jun 19, 2025
633bb8e
Fix page
Insality Jun 29, 2025
5dac7ed
Merge branch 'properties_panel' into develop
Insality Jul 2, 2025
4c8796e
Merge branch 'master' into develop
Insality Jul 2, 2025
379c9ac
Add memory fps panel manual
Insality Jul 5, 2025
7dd29f1
fix: scroll jitter fix in extro zone on Android 60Hz
KassiaL Jul 31, 2025
574f764
Add layout set_position_function
Insality Aug 8, 2025
097963b
Annotations update
Insality Aug 9, 2025
a94121a
Update properties panel
Insality Aug 9, 2025
b6a7df5
Update
Insality Aug 11, 2025
9ee084c
Update
Insality Sep 14, 2025
9bf4d32
Update
Insality Sep 27, 2025
2a754c4
Use editor script to set gui script path
Insality Sep 27, 2025
9e29c0e
Update editor script linking messages
Insality Sep 27, 2025
181a0aa
Update docs and changelog
Insality Sep 27, 2025
134468d
Add image component
Insality Sep 28, 2025
19d8422
Merge branch 'master' into develop
Insality Sep 28, 2025
66f671c
Added navigation handler for gamepad/keyboard navigation.
NaakkaDev Sep 29, 2025
7fd91d1
Take in account that sliders can be vertical too.
NaakkaDev Sep 30, 2025
9e51fa6
Removed changes from druid_instance.lua. Moved navigation handler to …
NaakkaDev Oct 4, 2025
4d58a6d
Removed arguments from the constructor. Cleaned the pprint that someh…
NaakkaDev Oct 4, 2025
a5e4b95
Moved the two lil helper methods down and adopted them into the model…
NaakkaDev Oct 4, 2025
f99fb5c
Merge pull request #328 from NaakkaDev/navigation
Insality Oct 4, 2025
6890a12
Fix: clear rich text default node on layout changed
Insality Oct 2, 2025
1578dc9
Update
Insality Oct 4, 2025
13374d6
Merge pull request #318 from KassiaL/fix/scroll-jitter-extrozone-andr…
Insality Oct 13, 2025
4341b5b
Scroll style update, code style update
Insality Oct 13, 2025
2a7d255
Solve #329 Allow numeric characters in RichText tags
Insality Oct 13, 2025
948e133
Update
Insality Oct 13, 2025
403d1e0
Update
Insality Oct 17, 2025
8786f6e
Asset store WIP
Insality Oct 18, 2025
1f0075d
Update
Insality Oct 25, 2025
9fba189
Update
Insality Oct 25, 2025
1f1438b
Update
Insality Oct 26, 2025
25f02fd
Update property panel, add small button for more text
Insality Oct 26, 2025
3e0d898
Update default state of property button small
Insality Oct 26, 2025
f0f90ce
Update property panels layers
Insality Oct 26, 2025
40dbca0
Update
Insality Oct 26, 2025
c43356d
Update
Insality Oct 26, 2025
f3f1933
Update
Insality Oct 26, 2025
1601d6d
Update
Insality Oct 26, 2025
1b2cdab
Update
Insality Oct 27, 2025
2a169da
Add Druid logger
Insality Oct 27, 2025
808b5e3
Merge branch 'develop' into core
Insality Oct 27, 2025
15c9bd5
Update
Insality Oct 28, 2025
b7f34ca
Update
Insality Oct 28, 2025
b9e2102
Up
Insality Nov 9, 2025
54bf904
Up
Insality Nov 9, 2025
788fc66
Up
Insality Nov 9, 2025
e130e39
Up
Insality Nov 9, 2025
9b8ff94
up
Insality Nov 10, 2025
ed24fc3
Remove widgets from druid repo
Insality Nov 10, 2025
6782a7f
Up
Insality Nov 10, 2025
a28b15c
Up
Insality Nov 10, 2025
138b261
Up
Insality Nov 10, 2025
3ec9364
Up
Insality Nov 11, 2025
5f2d4bd
Up
Insality Nov 12, 2025
817387b
Move core to sep repo
Insality Nov 15, 2025
5a36fa0
Clear editor script from core
Insality Nov 15, 2025
4e2c28e
Merge pull request #331 from Insality/core
Insality Nov 18, 2025
07422e9
Update color
Insality Nov 4, 2025
97a702a
Update README
Insality Nov 23, 2025
972bc89
Add create Druid collection script
Insality Nov 24, 2025
9498864
Update asset store
Insality Nov 25, 2025
d0866a5
Update with latest libs
Insality Nov 26, 2025
93b2d3f
Move Rich Input to widget asset store, move rich text folder
Insality Nov 26, 2025
4e8c071
Update with rich input changes
Insality Nov 26, 2025
26badbb
Revert "Update with rich input changes"
Insality Nov 26, 2025
e7c265c
Revert "Move Rich Input to widget asset store, move rich text folder"
Insality Nov 26, 2025
772216d
Update changelog
Insality Nov 29, 2025
aef4cb6
Fix #327 component name uid issue
Insality Nov 29, 2025
92ef114
Update
Insality Nov 29, 2025
fb62b4b
Add logger warn if color id is not exists
Insality Dec 5, 2025
a1bd1e8
Fix #332 Add scroll slider to the examples list
Insality Dec 6, 2025
b6181d9
Fix logger message
Insality Dec 6, 2025
85b0b21
Adjust scroll pin size
Insality Dec 6, 2025
c2e0256
Fix color return type
Insality Dec 7, 2025
730559a
Fix #333 Add settings for the Druid editor scripts
Insality Dec 9, 2025
f39c23f
Add tests for color
Insality Dec 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ manifest.private.der
manifest.public.der
/.editor_settings
/.deployer_cache

deployer_version_settings.ini
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,16 @@ In this example you can inspect a variety of **Druid** components and see how th
Open your `game.project` file and add the following lines to the dependencies field under the project section:


**[Druid](https://github.com/Insality/druid/)**
**[Defold Event](https://github.com/Insality/defold-event)**

```
https://github.com/Insality/druid/archive/refs/tags/1.1.6.zip
https://github.com/Insality/defold-event/archive/refs/tags/12.zip
```

**[Defold Event](https://github.com/Insality/defold-event)**
**[Druid](https://github.com/Insality/druid/)**

```
https://github.com/Insality/defold-event/archive/refs/tags/12.zip
https://github.com/Insality/druid/archive/refs/tags/1.1.6.zip
```

After that, select `Project ▸ Fetch Libraries` to update [library dependencies]((https://defold.com/manuals/libraries/#setting-up-library-dependencies)). This happens automatically whenever you open a project so you will only need to do this if the dependencies change without re-opening the project.
Expand Down Expand Up @@ -191,6 +191,7 @@ You can find the full **Druid** functions at [Quick API Reference](api/quick_api
To better understand **Druid**, read the following documentation:

- [How To GUI in Defold](https://forum.defold.com/t/how-to-gui-in-defold/73256)
- [Druid Workshop](https://youtu.be/qF19qpjZe9c)
- [Widgets](wiki/widgets.md)
- [Druid styles](wiki/styles.md)
- [Advanced Setup](wiki/advanced-setup.md)
Expand Down Expand Up @@ -228,6 +229,10 @@ Read the [CONTRIBUTING.md](CONTRIBUTING.md) file for more information.

## ❤️ Support project ❤️

Your donation helps me stay engaged in creating valuable projects for **Defold**. If you appreciate what I'm doing, please consider supporting me!
Druid is developed and maintained by a single developer in his free time.

If this library helps your project, consider supporting my work ❤️

It really makes a difference.

[![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)
2 changes: 1 addition & 1 deletion druid/base/blocker.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ local M = component.create("blocker")
---@param node node|string The node to use as a blocker
function M:init(node)
self.node = self:get_node(node)
self._is_enabled = gui.is_enabled(self.node, true)
self._is_enabled = true
end


Expand Down
30 changes: 15 additions & 15 deletions druid/base/button.lua
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ function M:init(node_or_node_id, callback, custom_args, anim_node)
self.start_scale = gui.get_scale(self.anim_node)
self.start_pos = gui.get_position(self.anim_node)
self.params = custom_args
self.hover = self.druid:new_hover(node_or_node_id, self._on_button_hover)
self.hover.on_mouse_hover:subscribe(self._on_button_mouse_hover)
self.hover = self.druid:new_hover(node_or_node_id, self.button_hover)
self.hover.on_mouse_hover:subscribe(self.button_mouse_hover)
self.click_zone = nil
self.is_repeated_started = false
self.last_pressed_time = 0
Expand Down Expand Up @@ -184,7 +184,7 @@ function M:on_input(action_id, action)
if self._is_html5_mode then
self._is_html5_listener_set = true
html5.set_interaction_listener(function()
self:_on_button_click()
self:button_click()
end)
end
return is_consume
Expand All @@ -193,7 +193,7 @@ function M:on_input(action_id, action)
-- While hold button, repeat rate pick from input.repeat_interval
if action.repeated then
if not self.on_repeated_click:is_empty() and self.can_action then
self:_on_button_repeated_click()
self:button_repeated_click()
return is_consume
end
end
Expand All @@ -211,7 +211,7 @@ function M:on_input(action_id, action)
end

if press_time >= self.style.LONGTAP_TIME then
self:_on_button_hold(press_time)
self:button_hold(press_time)
return is_consume
end
end
Expand Down Expand Up @@ -326,18 +326,18 @@ end


---@param hover_state boolean True if the hover state is active
function M:_on_button_hover(hover_state)
function M:button_hover(hover_state)
self.style.on_hover(self, self.anim_node, hover_state)
end


---@param hover_state boolean True if the hover state is active
function M:_on_button_mouse_hover(hover_state)
function M:button_mouse_hover(hover_state)
self.style.on_mouse_hover(self, self.anim_node, hover_state)
end


function M:_on_button_click()
function M:button_click()
if self._is_html5_mode then
self._is_html5_listener_set = false
html5.set_interaction_listener(nil)
Expand All @@ -348,7 +348,7 @@ function M:_on_button_click()
end


function M:_on_button_repeated_click()
function M:button_repeated_click()
if not self.is_repeated_started then
self.click_in_row = 0
self.is_repeated_started = true
Expand All @@ -360,23 +360,23 @@ function M:_on_button_repeated_click()
end


function M:_on_button_long_click()
function M:button_long_click()
self.click_in_row = 1
local time = socket.gettime() - self.last_pressed_time
self.on_long_click:trigger(self:get_context(), self.params, self, time)
self.style.on_click(self, self.anim_node)
end


function M:_on_button_double_click()
function M:button_double_click()
self.click_in_row = self.click_in_row + 1
self.on_double_click:trigger(self:get_context(), self.params, self, self.click_in_row)
self.style.on_click(self, self.anim_node)
end


---@param press_time number Amount of time the button was held
function M:_on_button_hold(press_time)
function M:button_hold(press_time)
self.on_hold_callback:trigger(self:get_context(), self.params, self, press_time)
end

Expand Down Expand Up @@ -413,14 +413,14 @@ function M:_on_button_release()
if is_long_click then
local is_hold_complete = (time - self.last_pressed_time) >= self.style.AUTOHOLD_TRIGGER
if is_hold_complete then
self:_on_button_long_click()
self:button_long_click()
else
self.on_click_outside:trigger(self:get_context(), self.params, self)
end
elseif is_double_click then
self:_on_button_double_click()
self:button_double_click()
else
self:_on_button_click()
self:button_click()
end

self.last_released_time = time
Expand Down
66 changes: 65 additions & 1 deletion druid/base/scroll.lua
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,59 @@ function M:scroll_to(point, is_instant)
end


function M:scroll_to_make_node_visible(node, is_instant)
-- Can be any node not only directly at scroll content
local screen_position = gui.get_screen_position(node)
local local_position = gui.screen_to_local(self.content_node, screen_position)

-- Get the node borders in content node space
local node_border = helper.get_border(node, local_position)

-- Calculate how much we need to scroll to make the node visible
local scroll_position = vmath.vector3(self.position)
local view_border = self.view_border

-- Convert content position to view position
local node_in_view_x = node_border.x + scroll_position.x
local node_in_view_y = node_border.y + scroll_position.y
local node_in_view_z = node_border.z + scroll_position.x
local node_in_view_w = node_border.w + scroll_position.y

local target_position = vmath.vector3(scroll_position)

-- Check if node is outside view horizontally
if self._is_horizontal_scroll then
-- If the node is too far to the right (left side not visible)
if node_in_view_x < view_border.x then
target_position.x = scroll_position.x + (view_border.x - node_in_view_x)
end
-- If the node is too far to the left (right side not visible)
if node_in_view_z > view_border.z then
target_position.x = scroll_position.x - (node_in_view_z - view_border.z)
end
end

-- Check if node is outside view vertically
if self._is_vertical_scroll then
-- If the node is too far up (bottom side not visible)
if node_in_view_w < view_border.w then
target_position.y = scroll_position.y + (view_border.w - node_in_view_w)
end
-- If the node is too far down (top side not visible)
if node_in_view_y > view_border.y then
target_position.y = scroll_position.y - (node_in_view_y - view_border.y)
end
end

-- If we need to scroll, do it
if target_position.x ~= scroll_position.x or target_position.y ~= scroll_position.y then
-- Convert to scroll_to expected format (content position, not scroll position)
local scroll_to_position = vmath.vector3(-target_position.x, -target_position.y, 0)
self:scroll_to(scroll_to_position, is_instant)
end
end


---Scroll to item in scroll by point index.
---@param index number Point index
---@param skip_cb boolean|nil If true, skip the point callback
Expand Down Expand Up @@ -525,27 +578,34 @@ function M:_check_soft_zone()
local target = self.target_position
local border = self.available_pos
local speed = self.style.BACK_SPEED
local is_changed = false

-- Right border (minimum x)
if target.x < border.x then
local step = math.max(math.abs(target.x - border.x) * speed, 1)
target.x = helper.step(target.x, border.x, step)
is_changed = true
end
-- Left border (maximum x)
if target.x > border.z then
local step = math.max(math.abs(target.x - border.z) * speed, 1)
target.x = helper.step(target.x, border.z, step)
is_changed = true
end
-- Top border (maximum y)
if target.y < border.y then
local step = math.max(math.abs(target.y - border.y) * speed, 1)
target.y = helper.step(target.y, border.y, step)
is_changed = true
end
-- Bot border (minimum y)
if target.y > border.w then
local step = math.max(math.abs(target.y - border.w) * speed, 1)
target.y = helper.step(target.y, border.w, step)
is_changed = true
end

return is_changed
end


Expand Down Expand Up @@ -675,7 +735,11 @@ function M:_update_free_scroll(dt)
-- Inertion friction
self.inertion = self.inertion * self.style.FRICT

self:_check_soft_zone()
local is_changed = self:_check_soft_zone()
if is_changed then
self.inertion.x = 0
self.inertion.y = 0
end
if self.position.x ~= target.x or self.position.y ~= target.y then
self:_set_scroll_position(target.x, target.y)
end
Expand Down
4 changes: 2 additions & 2 deletions druid/base/text.lua
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ local utf8 = utf8 or utf8_lua --[[@as utf8]]
---@field on_update_text_scale event fun(self: druid.text, scale: vector3, metrics: table) The event triggered when the text scale is updated
---@field on_set_pivot event fun(self: druid.text, pivot: userdata) The event triggered when the text pivot is set
---@field style druid.text.style The style of the text
---@field start_pivot userdata The start pivot of the text
---@field start_pivot number The start pivot of the text
---@field start_scale vector3 The start scale of the text
---@field scale vector3 The current scale of the text
local M = component.create("text")
Expand Down Expand Up @@ -224,7 +224,7 @@ end


---Set text pivot. Text will re-anchor inside text area
---@param pivot userdata The gui.PIVOT_* constant
---@param pivot number The gui.PIVOT_* constant
---@return druid.text self Current text instance
function M:set_pivot(pivot)
local prev_pivot = gui.get_pivot(self.node)
Expand Down
Loading