How to get a list of children for a `Gtk.Box` in GTK4 GJS?

,

I’ve a subclassed Gtk.Box and want to add an additional property children to hold the children added to that Gtk.Box. Is there any method to get the current list of children?

Since a GtkBox is a GtkWidget I think you should be able to:

export class MyBox extends Gtk.Box {
    get children () {
        let child = this.get_first_child();
        while(child) {
            yield child;
            child = child.get_next_sibling();
        }
    }
}
GObject.registerClass(MyBox);

But it’s soon 10 years ago that I worked with Gtk and JavaScript so it’s likely that I’m wrong. But you could test the above while waiting for replies from more knowledgeable people. :slight_smile:

1 Like

Iterating the children of a scaffolding widget like GtkBox is usually a code smell. The only time you should be iterating widgets is if you’re implementing a custom widget. There’s little to no reason why you should want to do it on a box.

What are you actually trying to achieve?

1 Like

Instead of appending a widget everytime, I’m trying to add a children property to Gtk.Box. Anytime if I want to list all the children I can use <instance>.children or set the children with <instance>.children = [new Gtk.Label(), new Gtk.Label].

But why would you do that? It’s incredibly inefficient to get the list of children and then append or replace it, compared to using the GtkBox API.

Why would you get the list of children (a linear operation), then append a child to it, instead of just appending the child to the box?

# compare this
box.append(new_child)

# to this
children = box.children
children.push(new_child);

Why would you replace the list of children instead removing the box and replacing it?

new_box = new Gtk.Box();
new_box.append(new Gtk.Label());
new_box.append(new Gtk.Label());
parent.set_child(new_box);

Also, GtkBox is a scaffolding widget. If you keep adding/replacing widgets to it, then you’re probably using the wrong widget. You probably want to switch to a Gtk.ListBox or a Gtk.FlowBox, and bind a model to them.

1 Like

Yeah, I think I was using the wrong widget. What my intuition is,

#widgets to add
widgets = [<child_1>, <child_2>, <child_3>]

#to add to a Gtk.Box
box = new Gtk.Box()
widgets.forEach(child => box.append(child))

#later
box.remove(widgets[1])

Now Instead if I do,

box = new Gtk.Box()

#add widgets 
box.append(<child_1>)
box.append(<child_2>)
box.append(<child_3>)

How am I supposed to remove <child_2> from box now?

So I thought if there was a way to get list of children and add getter, setter for this (i.e. children property) then I can do box.remove(box.children[1])

There is still the question of what you want to actually achieve. Like mentioned before, Gtk.Box is for UI layout, not for dynamic content.

So, depending on your goal I see two options:

Number 1: You have a set of UI elements which are visible depending on context. Like, you have an element with multiple buttons for actions, and you want to hide actions that are not possible.
In this case, you could just hide the element in question by setting Gtk.Widget::visible to false. Though, then it would make sense to consider Gtk.Widget::sensitive to just make it non-interactable instead.

Number 2: You have some content which is dynamically shown on the UI. So, you want one UI element for one content element.
Well, in this case, you want to look at using Gtk.ListBox or Gtk.ListView. These widgets are designed for dynamic content. Ideally, you would crate a list model for your content (which you can easily implement). There, you can use your method for sorting/adding/removing content. And then, you bind the ListBox or ListView to said list, and when your list model is updated, the widgets will update their content for you.
This is highly recommended. Not only separates this your business logic and the UI code better. When dealing with large amount of content, Gtk.ListView will be far more performant than a Gtk.Box.

I hope this helps you.
If there are still things unclear, it might help if you were to tell what use-case you want to implement, so we can find the best solution for it.

1 Like

I wasn’t sure about Gtk.ListBox but i.g. it’s the right option for what I was trying to do. Basically if I do this :

const box = new Gtk.Box()
box.append(new Gtk.Label())

Now I can’t delete the child inside box because I’ve no name reference (i.e. const or let) on this child. So I thought if there was a way to get a list of children, then I can easily do:
box.remove(<child_name>).

My plan is to make a notification center and I think Gtk.ListBox or Gtk.ListView is the right widget.

Also if I want to make something similar with horizontal packing what should I use. Afaik Gtk.ListView only shows items vertically.

Well, for something like a notification center, where you might have a lot of content, Gtk.ListView is likely the right choice.

Well, this sounds like a atypical design pattern, so its not that surprising Gtk does not support that out of the box.

1 Like