Obtain Gtk.ListItem'position from its child button in Vala

Hello,

In a little Vala project am using a Gtk.ListView with a GLib.ListStore and I display in each ListItem a Button and a Label in a horizontal Box. I would like to obtain the position of the Item from which button is clicked. Unfortunately, in Vala it seem, in the item factory, I cannot pass the ListItem reference (who has a position property) as a user_data to the Button.clicked signal. This code fails to compile:

private void rem_click (Gtk.Button button, Gtk.ListItem li) {
        //do something with li position
}

private void setup_queue_listitem (Gtk.SignalListItemFactory f, Gtk.ListItem li) {
        Gtk.Box hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 2);
        Gtk.Button button = new Gtk.Button.from_icon_name ("list-remove");
        button.clicked.connect(rem_click, li);
        ...
}

However, If i do not pass any user_data, removing the second argument of the clicked callback, the code compiles, but trying to obtain button.parent.parent as a Gtk.ListItem fails, I don’t know why.

Can someone tell me the good way to obtain a reference to the ListItem from which the button has been clicked ?
Many thanks.

From: Projects/Vala/SignalsAndCallbacks - GNOME Wiki!

The answer is: Vala uses the user_data argument implicitly in the generated C code, either for the context of a closure or for the instance reference (this) of an instance method. So you can just access either the outer data from within a closure or any instance variable of the handler’s class.

So it is possible to pass custom user_data with a lambda:
button.clicked.connect((button) => { rem_click (button, user_data); });

The widgets you create in setup are reused. You should only connect signals in bind (and undo that in unbind).

In your bind callback, a lambda would work to capture the data you want, but you’d have to save the handler ID so you could disconnect it in unbind. You could probably use Object.set_data() for this purpose.

GtkListItem isn’t a widget. I don’t think there’s a any public API that would let you get it from the child widget (GtkBox in your case).

1 Like

I’m not sure I understand exactly what you want to do. Do you want that when the button in the row is clicked, it returns the position of the row in the ListView?

If so, you will have to create a property in your row setup widget (something like an “item” property in Gtk.Box) to maintain a reference to the object that is inserted into the ListStore. And make this assignment in your bind function.

So, the button’s callback function has to use ListStore.find() passing the object you stored in the “item” property to find out the position of this specific row in the list.

Yes.

To add an “item” property with an unowned reference to the item, in the Gtk.Box, I would have to subclass it or use Object.set_data (), right ?

But I could manage another way, by passing the “item” with a lambda as the “clicked” callback. As proposed @chrisaw, I used Object.set_data () to store the connection binding to remove in widget unbinding.

For reference, here is my code. Thanks to all for your comments, it works nice now.

    private void bind_queue_listitem (Gtk.SignalListItemFactory f, GLib.Object l
i) {
        Gtk.ListItem item = (Gtk.ListItem) li;
        Gtk.Box hbox = item.get_child () as Gtk.Box;

        Gtk.Button button = hbox.get_first_child () as Gtk.Button;
        ulong id = button.clicked.connect ((button) => { rem_click (button, item
); });
        li.set_data("click_id", id);

        Entity e = (Entity) item.get_item () as Entity;
        Gtk.Label l = hbox.get_last_child () as Gtk.Label;
        l.label = e.name;
    }
    private void unbind_queue_listitem (Gtk.SignalListItemFactory f, GLib.Object li) {
        Gtk.ListItem item = (Gtk.ListItem) li;
        Gtk.Box hbox = item.get_child () as Gtk.Box;
        Gtk.Button button = hbox.get_first_child () as Gtk.Button;
        ulong id = li.get_data ("click_id");
        button.disconnect(id);
    }
    
    var factory = new Gtk.SignalListItemFactory ();
    factory.setup.connect (setup_queue_listitem);
    factory.bind.connect (bind_queue_listitem);
    factory.unbind.connect (unbind_queue_listitem);
2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.