CSS binding for some ListItems using a BuilderListItemFactory

Hi there! I’m working on an application that should “do things” with several (40+) usb storage devices (sticks) - backup them, format them, etc.

I created an UI (using Blueprint, GTK4 and Adw) where I have a GtkGridView which displays a cell for each (possible) stick. The cell is predefined as template ListItem inside a BuilderListItemFactory and updates a label and an icon according to the label: bind template.item as <$Stick>.name;.

However it seems that I cannot bind a CSS class to an item attribute?

I can add a styles [“red”] entry in my Blueprint file for elements of the ListItem, but when trying a binding like styles: bind template … (Gtk.Label does not have a property called styles) or styles bind template … (Unexpected tokens) I get errors from the Blueprint compiler.

Is there any way to change the background color of the ListItemWidget corresponding to the “stick status” (connected, copying, formatting, ejected, removed) either by binding a CSS class or by accessing the ListItemWidget from code and adding the CSS class per code (it doesn’t seem that I can access the Widget, only the Item and the Factory)?

I recommend making a class to be used as a child of the ListItem, and in that class you can programmatically add and remove style classes.

The reason a binding doesn’t work is because styles [] is syntactic sugar to run Gtk.Widget.add_css_class multiple times, for each style in the list.

The real property is css-classes, which you could add a binding for, but that can be dangerous as you’re overriding any styles other widgets add.

1 Like

I tried making a subclass of ListItem before, but the Blueprint compiler errors for the BuilderListItemFactory with “Only Gtk.ListItem is allowed as type here”.

I have a:

template $SticksGridView: Gtk.GridView {
  ...

  factory: BuilderListItemFactory {
    template: ListItem {
      child: Frame {
        Box {
          Label stick_name {
            label: bind template.item as <$Stick>.item;
          }
          Image {
            visible: true;
            icon-name: bind template.item as <$Stick>.icon;
          }
        }
      };
    }
  };
}

How can I extract the ListItem in it’s own class but still use the BuilderListItemFactory?

You’d want to subclass the Frame instead here.

I’d recommend putting a stick property on it, after which you can use it like so:

factory: BuilderListItemFactory {
  template ListItem {
    child: $StickItem {
      stick: bind template.item;
    };
  }
};

Then in StickItem’s Blueprint, you can do the rest of your bindings.

I tried that but it seems that I’m missing something: “Error building template for list item: .0:0 Invalid object type: ‘StickItem’”.

Which language bindings are you using? Make sure the class is registered as a GType, and also that the class you’re using it in knows about that GType.

Python. I’m not really sure what I changed but it seems to work now.

Is there any event I can register that is triggered, when the stick property is bound on the subclass? Currently I’m connecting the “notify::propertyname” signal but I’m not sure if that’s the best solution.

I’m not really sure what I changed but it seems to work now.

In PyGObject, it’s important that the module with the class loads, an import is enough for that. __gtype_name__ also has to be set. But those are just two guesses, I’m glad it works now :slight_smile:

Is there any event I can register that is triggered, when the stick property is bound on the subclass?

notify::propertyname is correct, but PyGObject also allows you to define a setter for your properties, which you might prefer:

@GObject.Property(type=Stick)
def stick(self) -> Stick:
    return self._stick

@stick.setter
def stick(self, stick: Stick) -> None:
    self._stick = stick
    # extra logic here
1 Like

Thank you so much for your help. It’s working now as expected. How can I sponsor some coffee?

I accept donations on Liberapay and GitHub Sponsors, thank you :black_heart: