Druid - Component GUI Framework

@Insality How did you manage to make an infinite data list with the limit of GUI nodes?

I see that you use gui.delete_node but I was under the impression that gui.delete_node didn’t decrease the overall GUI nodes counter. How do you bypass that limit? Took a look at the code but didn’t find anything magic, care to tell me where you do it? Thanks

1 Like

The way I did it in Gooey was to only create enough nodes to fill the screen and then reuse them when scrolled outside the visible area.

Yes that was my original thought, but I only see a create_node method, I don’t see any bind method that would reuse previously created nodes.

Hi! There is no any tricks, it’s really just create and delete nodes without any caching or reuse.

I think the statement about gui.delete_node is not decrease node counter probably wrong. It can be true only in one frame period. Druid’s Data list delete nodes which outside of scroll area view and create new nodes for this area if necessary

1 Like

OK that’s the info I was missing! I thought that limit was absolute during all of the GUI existence!
Thanks for your answer and great job with the library.

One suggestion for improvement is using caching and reusing previously created nodes (similar to what list adapters are in native Android), I think we would see great performance improvements.

1 Like

On my mind, there are some reasons to don’t implement this:

1- Defold is static engine, there is no big overhead to not reuse nodes. Delete and create nodes process are fast. To be clear, I think manual caching will have more overhead and complexity than current version.

2 - If we will use reusable nodes, we need to pass more functions, not only “create” callback. We need to use “clear” callback and be sure, that created new nodes will be the same as from not reusable one.

Did you get any performance issues? If true, let’s investigate this! :slight_smile:

3 Likes

Hello!

Any ideas how should I attempt to make a button/node that is drag-able and can be moved into other positions that fits the grid? A use case, where you have a list of available items and can drag them into another list/grid into empty positions - for example card games, where you can drag cards from deck onto a board? Is there something straightforward or helpful in Druid already? :wink:

1 Like

Hi!

If I do something like the card game you described, I’ll create custom component Card with different visuals (in hand and on board). It will have component Drag to handle pickup from one state to another with positions checks for transitions.

Probably Static Grid is not so good positioning for games like this, but anyway it’s possible to use it on both for hands and for game board.

So probably from Druid is most useful will be the Drag and Button components for this :smiley:
Other logic seems better to handle by yourself for more flexibility.

3 Likes

Hi @Insality !

First of all, thank you and congratulations for this great extension! (that I started to use… yesterday :baby:)

I struggled a little bit at first, but now that I understand the general principles, I see how it can/will be super useful.

I have a few noobish questions though:

1/ How to retrieve/build list information?
In order to manipulate the list, I created a data table to manage the “logics” part, directly in the “create” function:


Is this the correct method or does Druid provide some hmmm… “tools/functions” to easily get these information?
(just to make sure I didn’t reinvent the wheel but worse)

Note that this method seems to work fine, except for my second point.




2/ Is there a callback when deleting a node?
I’m struggling keeping my data table and the “physical” list synchronized because I don’t know when a node is deleted (when it moves outside the container).

Is there a way to know when a node is deleted? (so I can update my data table always know the state of the list)




3/ How to insert space between the elements of my list?
[SOLVED - ANSWERED RIGHT BELOW BY RAMA] :+1:
Currently, the elements of my list are “touching” each others.
image
In the “new_static_grid” function, there is a parameter for the nb of columns but nothing to manage the space between elements.

Possible workaround: my elements (ex: button) could contain the space, but in this case, this space would be clickable (as a part of the element). Is possible, I’d rather have the space not clickable.

=> is there a smarter method/workaround to do it?




4/ [BUG] Uncorrect repositioning of the list when not enough elements in the list
(at least fewer rows than what the container can contain - it works fine otherwise)

Is this a known issue or should I report it on github?

As a workaround, would you recommend to create “empty” (invisible/not clickable) elements to fill the holes? (so the system won’t know there are not enough rows)

2 Likes

→ 3/
you do this by changing the size of the root/parent node of the button node

btw @Insality
why does basic form of input needs the update function?
I spent the whole day trying to make a really simple custom component which reacts to input and in the end it was because i needed self.druid:update(dt) in my gui script.

3 Likes

Hello, @Ragetto! Thanks for your feedback and questions

I recommend you define all of this data inside your custom component and return it in create function like this: https://github.com/Insality/druid/blob/master/example/examples/general/data_list/data_list.gui_script#L22
Return instead button component from example your custom component with required data/logic. This custom component will have create/on_remove callbacks and also you will get logic inside it.

For now there is no ability to get current created component from DataList, but you can share your use case or make github issue and I’ll take a look :wink:

Druid removing nodes only in DataList and there is no callback currently (probably I should add create/remove events). If this callback is required - the current way is make the custom component and use on_remove function. But probably with custom component you not longer required to keep your “physical” list

Also I’m often using the events in this custom components. So it’s easy to bind functions from created components, something like this

local function create_element(self, data)
    local nodes = gui.clone_tree(self.character_slot_prefab)
    local character_slot = self.druid:new(CharacterSlot, template, nodes)
    character_slot:set_data(data)
    character_slot.on_click:subscribe(some_action)
    return character_slot.root, character_slot
end

Right, the right way is change size of root node with including margins between elements. Also, probably after the creating StaticGrid you able to tweak static_grid.node_size vector property, but need to check it :slight_smile:

If you required to have button less size than root, place button inside root with smaller size.

Not fully understand this bug. Can you describe it more detailed? Seems you have an issue than scroll content is less than scroll area and seems it align incorrect? Or it should not scrolling? Probably you can check the scroll style param scroll.style.SMALL_CONTENT_SCROLL.

3 Likes

Hello, @rama!

Ouch, I got it! Yea, update function now is required only for Scroll, Progress and Timer components. But the input capture goes in on_late_init step inside this update function. I’ll make the issue for this to see how I can handle this better. Thanks!

3 Likes

Would it be possible to skip the requirement to call update() by instead using a timer inside Druid?

Yeah it’s ok, thanks! :+1:

Hmm, let’s say that when the content is smaller that the container, the scrolling list doesn’t get back to its initial position when I release the mousen but is weirdly/slowly “centered” (cf the video)

But as you recommended, I turned this parameter to false and it “neutralized” the bug :slight_smile: (without fixing it, but no big deal since there is no point in scrolling a small content list… so all in all, it’s fine)

Here is what i’d like to achieve (without the crash when the nodes are deleted, at the end of the video)… Nothing extravagant, just a scrolling list where I can highlight the selected element etc.

I (really) tried to apply your exemple to my case, but didn’t succeed 🥲 :monkey:

Here is a minimal version of the test project above, with comments here and there (so if you can/want to take a quick look and see what I want to do and what I’m missing/doing wrong with Druid integration… maybe it’d be super obvious for you)

Cf right above.

Again, I tried to use the on_remove() function, but to be honest I just don’t know how… :see_no_evil:

get_length(), scroll_to_index(), get_index(), set_data(), subscribe() etc. return or do something, but I don’t get what on_remove() is supposed to do. The description in the API sounds pretty unclear to me (“Druid System on_remove function”)

I would have expected a “on_remove” field, like “on_scroll_progress_change” (but with a gui node as parameter), so I could use “subscribe” (?) to trigger a custom function/callback (this is how it’s done in the examples, right?).

But I probably don’t understand the purpose of this function… Is there an example somewhere? I digged into the example project and did not find any (but maybe I missed something).




OVERALL FEEDBACK (from an inexperienced developer perspective)

The general principle of Druid seems brilliant and even as an inexperienced developer I can see how convenient it can/will be to create my own custom components to build complex UIs. I’ll probably learn how to make the most of it with more practice and experience.

But for now, to be honest, sometimes I’m struggling using some of the API functions etc. (cf below) - The API documentation is probably great for experienced developers (and I’ve been toying with some functions myself), but sometimes I feel I’d really like to have… hum more context (ex: “how to use on_remove” :slight_smile: just a quick example of code (since I did not find it in the examples) etc. same for the “hover” thing, in my tests I use Defold functions (gui.pick_node) instead of Druid stuff because at first sight it seems easier… even though, as mentioned above, I’d need a “node deleted” callback -or anything similar- to do it properly).

That’s also why I tend to “extract” from Druid what I really need (ex: the scrolling list, the basic buttons etc.) without embracing the whole thing, which is quite a pity.

2 Likes

Hey!

Hmmm, so.… My previous post may have been unclear with too many useless comments (since it wasn’t super clear in my mind)… but this time I’ll try to keep it short and focus on the objective/use case.

I restarted my test project from the “scrolling list + checkbox” example, and here is a major difference with what I want to do:

1/ In the “checkboxes” example, the user can have several checkboxes checked (…) at the same time. Which means that selecting/unselecting a checkbox has no impact on the other checkbox nodes of the scrolling list. I don’t need to know if a node is deleted or not.

2/ In my case, I want to have only ONE node selected at a time. When clicking a new node, I also want to unselect (= change the animation of) the previously selected node. If I don’t, I’ll have to wait that the previous node is deleted and created again (like in the video below).

How to have only 1 node visually selected at a time?
Is there an example somewhere that I may have missed?

Note: I implemented the node animation update in the update() function during my tests, but as expected, if the previously selected node is already deleted (off-list), I get a “Deleted node” error message when trying to update its animation… And at this point, I don’t know how to detect if the element in question is deleted or not.

PS: as a feature, I feel like this is something really common/standard, but after many many tests (this message is the super short story), I’m still stuck so… Any help is welcome… @Insality , @britzl @AGulev … or anyone having worked with Druid… I can’t imagine being the first one to implement something like this with Druid.

1 Like

I’m currently without any laptop to add an example or fully answer on your question. I will answer to you soon as possible :wink:

Probably here are 2 ways: add events to components and react on some click event.
Or it’s really will be useful to iterate over all visible components in data list. (current its possible to iterate over data_list._data_visual field. I will add get_components function in next release.

It’s filled now in this way:

 self._data_visual[index] = {
 	node = node,
 	component = instance
  }
2 Likes

I’m not familiar with Druid, but with my own UI setup, when I wanted a single “selected” button like this, I used a group of radio buttons and themed the “checked” button to appear highlighted.

2 Likes

Hi @Insality !

I’d have a “quick question” about how to build scrolling lists containting “conditional” content. I’d like to have your opinion about which method I should choose.

(PS : I’m asking Insality but anyone can share their experience, of course… :pray:)


My use case:

Scrolling_list 1 (item categories) = the user clicks a button to select a category.
Scrolling_list 2 (items) = items are displayed consistently with the selected category.

image

In Defold, I have a table that contains all the items, for each category.

But technically, with Druid, what would be the best method to display the item list with the right items?


Option 1
When I click a category button, I destroy the previous item list and create a new one.

Option 2
I create all the item lists when the scene is launched.
And then, when I click a category button, I show only the right item list (and hide the others).

Option 3
I have only ONE big item list containing all the items (of all categories).
When I tap a category button, I refresh the “big” item list to display only the items that should be displayed (I don’t know how I would do that, but perhaps it’s possible).


At first sight, which approach would you recommend?

Maybe another one?

3 Likes

I believe there is a common term for this described technique, “Object Pooling.”
I’ve only read a little bit about it but perhaps looking into it and determining whether its pros and cons align with your goal can help you make a decision?

1 Like

Thank you for you answer!

I did a quick search and found this topic:

I understand the principle and it looks like Defold can handle it by default.
“This practice is not necessary in Defold. It’s automatically done for you.”

But in my case (option 2), I wouldn’t have a high number of “item lists” so this is not my core issue.

I’m pretty sure (and I hope) that I’m not the first one to create a screen with a “category selection” menu containing buttons (1 per category) that I click to “contextually” display such or such items (based on the selected category).

How do people design/create these “item lists” in Defold? (with Druid or not - not sure my question was really Druid-specific… Maybe there are more generic best practices)