Druid - Component GUI Framework

The hover component don’t have that logic, so should find different way to implement this. Here some possible ideas:

  • change hover component and re-register them with new logic (should be not so hard enough)
  • you can rely on button.can_action boolean field. it’s true if button was pressed and continue pressed without leave the node pick zone
  • you can use drag component since they have events like touch_start/end to handle it in code way instead of style
2 Likes

could anyone tell me how can I remove a button from the blacklist?

I am stupid, I know how this worked, great.

Hello, i tried using Create Druid Component inside the edit menu but I got this error:
Im on Windows 11

ERROR:EXT: ./build/run_python_script_on_gui.sh: line 8: pip3: command not found
The python deftree is not installed. Please install it via
pip3 install deftree

I tried installing python and deftree but it didn’t work too, any ideas?

1 Like

Hello!

Yea, the run python script uses python3 and pip3 instead of regular python and pip

As a current solution you can copy the Druid folder and modify this script to python and pip by your own

You know what’s weird?
I installed Python by using scoop.sh, and it also added python3 and pip3 to the PATH, but it still didn’t work. I’ll try modifying the script, thanks!

1 Like

Yea, definitely :upside_down_face:

I will look into this on the next update

1 Like

Will new components be added to this lib like drop-downs?

Probably this kind components can be made as an example in Druid, but not as included components. Which other components do you looking for?

That was all I think, I wish the text-input component would have more functionality, like movable cursor with arrow keys or touch, select/delete, but I know how hard it is to make ui with Defold. The main thing I would have wanted from Druid would be focus functionality. To be able to interact with components with just arrow keys, enter and tab… Would make ui more accessible for joysticks.

4 Likes

Druid Release 1.0 Release Druid Release 1.0 · Insality/druid · GitHub

Description

Hello! The long-awaited update for Druid is finally here! The Druid now is finally 1.0 version! :tada: This was a long path to achieve current state!

This update brings a lot of improvements, so let’s dive in:

New Example Page

I’ve 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. I’ve 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! I’m 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

I’ve finally removed middleclass from Druid. If you were using it for some reason, you’ll need to copy the “middleclass” code into your project.

Annotations

Druid’s 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: 1.0 Milestone · GitHub

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, you’ll 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. It’s 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:

And all my other supporters! Very appreciated!

:heart: Support :heart:

Please support me if you like this project! It will help me keep engaged to update Druid and make it even better!

Github-sponsors Ko-Fi BuyMeACoffee

24 Likes

Fantastic work! I haven’t used it, but I’m eager to dig in to it and see how you have architectured it all.

I had one minor issue with the Scroll Grid Points example. At first, I thought it was broken, as scrolling up or down with the mouse wheel would most of the time snap me back to the original item. I figured out what was going on, though, and that I could drag the list. Anyway, if I were to make a suggestion, it would be to have each mouse up and down input event scroll the list one whole step to the next item.

2 Likes

Thanks!

Yeah, there are can be some issues, but it also clearly shows how it’s work in this situation :smiley:

Now new example project became more easier to edit and play with, so more people can helps with issues while changing & testing their fixes in more isolated examples.

3 Likes

Very nice! The new example looks awesome too.

I am currently poking around the new example and noticed a bug. When I was clicking the input field (multiple times) in the input password example the whole viewport became blue-ish… like it somehow selected the whole root node (if there is one). Clicking the input field again (or a few times) makes the blue tint go away but it comes back after choosing another example. Requires a page refresh to fully fix it.

2 Likes

Congratulations! This is a fantastic framework!

2 Likes

I’m surprised to see it’s v1, as it’s one of the libraries I always include in my projects! It’s solid, stable and lovely. Glad to see it’s still being worked on and improved. :fire:

2 Likes

Druid Postmortem

Hello! I want to share some thoughts about Druid. It started as a small pet project, but over time, Druid’s approach to GUI in Defold has gained popularity. I’ve received a lot of feedback, issues, questions, and suggestions. I’m thrilled to see how Druid is being used in different projects and how it helps make GUI in Defold easier and clearer.

However, Druid still lacks tutorials, especially video tutorials. I hope that with new examples in the future it will become more accessible, and more users will learn it and share their experiences and knowledge with others.

This was almost my first open-source project that I followed from the very beginning. But since the project is widely used, I can’t freely refactor it as I’d like. I have to maintain backward compatibility to avoid breaking existing projects. Right now, I have several ideas that would need to be approached differently.

One of the most frustrating aspects for me was the annotations. When the project started, there weren’t widely adopted standards for annotations, so Druid used LDoc (or LuaDoc) annotations to generate an external API site. Compared to current code annotation practices, LDoc is much harder to read and maintain. I hope to refactor all the annotations in the future to align with the Lua Language Server. The biggest benefit of this change will be easier navigation within the editor, such as using “Go to definition” and “Find all references.” My advice: use the Lua Language Server annotations. They are much easier to read, maintain, and automatically assist with code navigation and validation. Now I annotate code like in this example Panthera file: https://github.com/Insality/panthera/blob/main/panthera/panthera.lua.

Another controversial feature is Druid Styles. They were designed to adjust inner component configurations like scroll speed, animations, and other settings. However, I’ve received a lot of questions about them, and I now believe there are better ways to customize components. Most of the time, I end up overriding the styles directly where they are created, which isn’t perfect, but it gives more control and can be integrated into custom components (https://github.com/Insality/druid/blob/master/example/examples/basic/button/basic_button_hold.lua#L22-L24). This current solution is not very memory-efficient, but in most cases—especially for games, not large UI applications—it’s not a big issue.

About all Druid events: I’m still unsure about the components’ event parameters, especially the self parameter. Each component’s events say they pass the self parameter as the first argument, but it’s not always clear which self. For example, if I attach logic to a hover event in a Button component:

self.button = self.druid:new_button("button", self.on_click)
self.button.hover.on_hover:subscribe(self.on_hover)

---Here self is the button component, not the script. The "self" context of hover is the button component.
---@param self druid.button
function M.on_hover(self, is_hover)
	if is_hover then
		print("Hovered!")
	end
end

I now think the self context should always be passed as an explicit argument, and callbacks should be simpler, containing only necessary arguments. For example, the on_hover event should look like this, but making this change would be breaking, so I’m holding off for now:

-- Pass self as an explicit argument
self.button.hover.on_hover:subscribe(self.on_hover, self)

---@param self current_script
---@param is_hover boolean
function M.on_hover(self, is_hover)
	if is_hover then
		print("Hovered!")
	end
end

About custom components. Another small point is how custom components are created. The current solution (in Druid 1.0, it has become slightly better due the self:get_druid(template, nodes) intead of directly self:set_template() and set:set_nodes()) looks like this:

local component = require("druid.component")

---@class my_amazing_component: druid.base_component
local M = component.create("my_amazing_component")

function M:init(template, nodes)
	self.druid = self:get_druid(template, nodes)

	-- Now we can use Druid as usual
	self.button = self.druid:new_button("button", self.on_click)
end

return M

What I’d prefer to have this approach instead:

---@class my_amazing_component: druid.component
local M = {}

function M:init()
	self.button = self.druid:new_button("button", self.on_click)
end

return M

This approach would require refactoring Druid’s base code to manage more on the Druid side, but it would also come with breaking changes. The Druid Editor Scripts, which help create custom components from the GUI scene, make it easier to handle this boilerplate code, but it’s still not perfect. You can read more about editor scripts here: https://github.com/Insality/druid/blob/master/docs_md/02-creating_custom_components.md#create-druid-component-editor-script.

In previous versions, I used a SCHEME table with a list of required nodes in the component, but over time, it became inconvenient. The current solution is to find all the required nodes in the init function, like this:

function M:init()
	self.button = self:get_node("button") -- instead of self:get_node(SCHEME.BUTTON) like before
	self.icon = self:get_node("icon")
	self.panel = self:get_node("panel")
	-- etc
end

The SCHEME table has been removed from all examples and custom components. These improvements reduce the amount of code and lines needed to write and maintain.

When developing a GUI with Druid, most of the time you’ll need to pass bindings between components. For example, if a node’s size changes, you may need to update or call functions in related components. For example, we have the scroll:bind_grid(grid) function to easily manage scroll node size. But I’m currently considering whether Druid components could handle these bindings automatically, using an inner node_property_changed event bus that reacts in a more declarative way. This is just an idea for now, but it would involve breaking changes and require some time to test.

Another area I care about is performance and memory consumption. When you spawn a component, a bunch of events and data are created, even if you don’t need them. For example, the button component creates events for click, double-click, etc. It’s not a lot of memory (~60 bytes per event), but ideally, these events would only be created if needed.

Well, these are just some ideas and thoughts. I hope if you’re interested in or using Druid, you found this interesting! Have a nice day, and take care!

17 Likes

Hello!

There are some difficulties with using lists and blocker together.

  1. If the blocker node is disabled BEFORE declaring the blocker (1 uncommented), then the blocker does not work
  2. If you do not set priorities for the blocker (3, 4 uncommented), then scrolling the list under the blocker still works (and sometimes allows you to click on the list element, video1)
  3. If you set a priority for the blocker (3 uncommented), then the elements created after do not work.
  4. If you set priorities for the blocker and elements (which is not very convenient) after the blocker (3, 4 uncommented), then scrolling the list on the blocker does not work (it does not scroll past the list element, video2).

Any ideas on how to make this structure correctly?

Video1:

Video2:

druid_test.zip (5.0 KB)

Druid covered by Mike!

5 Likes

Basically this problem is solvable via other collections with popus, so they fully gather input.

In this case, honestly I think the best solution will be on showing popup call the scroll:set_input_enabled(false). The reason that blocker takes all the input, but not for “continue” movement like scroll drag (it has more priority than default component)

So other ways is to increase input_priority over all popup elements, or make the custom component from it and use :set_input_priority over this instance

2 Likes