Gtk4 Preference of Composition over Inheritence

Hello,
Gtk4 decided that some Classes/Object should be final and can’t be subclassed/inherited.

After a question on irc, @ebassi has responded that the proper way to construct custom object is to compose it from Gtk4 Classes.

For the following scenario, what would be the proper process:

I want to create a custom Image, so I inherit my class form GtkWidget, add GtkImage as a member variable and construct it the way I want, implement the measure function, set the image.parent to the class, the question is:

how to deleget the snapshot virtual function to the image? If I just call image.snapshot (snapshot); from inside the virtual function, I get nothing drawn to the surface?
How would one achieve this?
Thanks

I don’t know Vala enough to answer a Vala-specific question. In C, if you want to create your custom widget that contains a GtkImage, you need to:

You don’t need measure() or snapshot() overrides: the size negotiation and allocation are going to be handled by the GtkBinLayout layout manager, and GtkWidget's default implementation of the snapshot() virtual function automatically recurses through all the children of a widget.

If you only override measure() and don’t override size_allocate(), your child widget will have a size of 0×0, unless you also set the expand/align flags on the child widget to always expand and fill. You should use a layout manager, though; it’s easier.

@ebassi Thank you very much for answering our questions.
one notice though.
Adding

this.set_layout_manager_type (typeof (Gtk.BinLayout));

gave me always a warning

Gtk-WARNING **: 19:28:27.151: Trying to snapshot GtkImage 0x560d9806d790 without a current allocation

The solution was to create a new layout manager and pass it to the widget

var layout = new Gtk.BinLayout ();
this.set_layout_manager (layout);

This is done by GtkWidget automatically at instance initialization type, if you called gtk_widget_class_set_layout_manager_type() when initializing the class.

I have no idea what this maps to in Vala; is this the class pointer in a class constructor?

that’s right. this refers to the class instance. but after a few trials, I’ve discovered that this warning occurs only on the first instance. Creating more than one instance works well for the second and thereafter. For example:

box.append (new CustomWidget (param));  // emits warning, don't paint
box.append (new CustomWidget (param2)); // works
box.append (new CustomWidget (param3)); // works

I don’t know the reason!

You’ll have to write a version in C that I can review; otherwise, I won’t be able to say if it’s a GTK issue or a Vala one.

This is the Vala version. Although I don’t know much C, I would try it.

public class Gtk4Demo.CssButton : Gtk.Widget {
    private Gtk.Image image;
    private string css_class;

    construct {
        // this.set_layout_manager_type (typeof (Gtk.BinLayout)); // Don't work
        var layout = new Gtk.BinLayout ();
        this.set_layout_manager (layout);

        image = new Gtk.Image ();
        image.set_size_request (48, 32);

        var source = new Gtk.DragSource ();
        source.prepare.connect (drag_prepare);
        image.add_controller (source);

        image.set_parent (this);
    }

    public CssButton (string css_class) {
        this.css_class = css_class;
        image.add_css_class (css_class);
    }

    ~CssButton () {
        image.unparent ();
    }

    Gdk.ContentProvider drag_prepare (Gtk.DragSource source, double x, double y) {
        var paintable = new Gtk.WidgetPaintable (image);
        source.set_icon (paintable, 0, 0);
        return new Gdk.ContentProvider.for_value (css_class);
    }
}

Completely untested but I think you want:

class Demo : GtkWidget {
    static construct {
        set_layout_manager_type (typeof (Gtk.BinLayout));
    }
}
  1. The set_layout_manager_type (typeof (Gtk.BinLayout)); should go into class construct rather than construct.
  2. The destructor in vala is the finalize method in C rather than dispose. You should just override dispose() (defined in GLib.Object)

@zbrown Thanks. That was it. Initializing the setting the layout manager in the static construct without this has the expected result.

@akitouni I’ve already tried it both in class constructor and construct. both give the same error.

@akitouni I didn’t notice any difference between both.

~CssButton () {
       // image.unparent ();
}

protected override void dispose () {
    image.unparent ();
}

Has it any subtle difference?

Yes

Object destruction

Again, it is often difficult to figure out which mechanism to use to hook into the object’s destruction process: when the last g_object_unref function call is made, a lot of things happen as described in Table 5, “g_object_unref”.

The destruction process of your object is in two phases: dispose and finalize. This split is necessary to handle potential cycles due to the nature of the reference counting mechanism used by GObject, as well as dealing with temporary revival of instances in case of signal emission during the destruction sequence. See the section called “Reference counts and cycles” for more information.

1 Like

@zbrown Thank you for clarifying this subject.