Published 2023-11-19 00:00:00

The gtk4 port of my roobuilder is getting closer to a release.. well, good enough that it will replace the existing gtk3 version.

Porting has been quite an effort. The initial phase of switching to gtk4 libraries and fixing all the compiler errors took a few months (this is very much a pet, part time project.. so things take a while).

After having reached the point where there were no compiler errors (although it still has plenty of warnings). The next step was to see if it ran.. which obviously it failed at badly.

Its been a long road, and it started with issues around how gtk4 windows are more deeply tied to the application class. Been so long since i fixed that, so ive forgotten the details. In the gtk3 version, although it had a application class, it did not really do that much.

More recently, though, I've been going through the interface migration. Key to this has been the migration away from gtktreeview, which seems to be unusable now for drag and drop of outside elements, along with being depricated. So i had to migrate all the code to use columnview with treemodels.

On the positive side the use of an array of objects as the storage for trees, and the new method to render cells is a massive improvement on gtk3. And works like magic with vala objects. Especially clever is the methods to update cell content. Which you can create get/set properties on the vala object. And any change to the property instantly updates label text

This makes the code that manages node tree, the core to the ui builder, massively simpler. No need to keep calling refresh or deal with tree iteraters like gtk3 treeview.

this.el.bind.connect( (listitem) => { 
  var lb = (Gtk.Label) ((Gtk.ListItem)listitem).get_child();
  var item = (JsRender.NodeProp) ((Gtk.ListItem)listitem).get_item();
  item.bind_property("to_display_name_prop", lb, "label", GLib.BindingFlags.SYNC_CREATE);

The method for sorting these view is also nothing short of magical, when you finally find the code example for sorting.. its very easy.. but hunting down a good sample was difficult.

this.el.set_sorter( new Gtk.StringSorter(
  new Gtk.PropertyExpression(typeof(JsRender.NodeProp), null, "name")
// along with this (in the sorter
this.el.set_sorter(new Gtk.TreeListRowSorter(_this.view.el.sorter));

The process had not been without difficulty though, the new widgets seriously lack the ability to convert click events into cell row/column detection. Essential for drag drop, The only way to do it is to iterate through the child and siblings and use math to calculate which row was selected. This made more complicated as the recent update to the widget changed the structure of the widgets. Breaking all row detection code. Note to self... don't complain.. send a patch...

(double x,  double y, out string pos) {

 // from
    var  child = this.el.get_first_child(); 
	Gtk.Allocation alloc = { 0, 0, 0, 0 };
	var line_no = -1; 
	var reading_header = true;
	var curr_y = 0;
	var header_height  = 0;
	pos = "over";
	while (child != null) {
		//GLib.debug("Got %s", child.get_type().name());
	    if (reading_header) {
		    if (child.get_type().name() == "GtkColumnViewRowWidget") {
		        child.get_allocation(out alloc);
			if (child.get_type().name() != "GtkColumnListView") {
				child = child.get_next_sibling();
			child = child.get_first_child(); 
			header_height = alloc.y + alloc.height;
			curr_y = header_height; 
			reading_header = false;
	    if (child.get_type().name() != "GtkColumnViewRowWidget") {
		    child = child.get_next_sibling();

		child.get_allocation(out alloc);
		//GLib.debug("got cell xy = %d,%d  w,h= %d,%d", alloc.x, alloc.y, alloc.width, alloc.height);

	    if (y > curr_y && y <= header_height + alloc.height + alloc.y ) {
	    	if (y > (header_height + alloc.y + (alloc.height * 0.8))) {
	    		pos = "below";
    		} else if (y > (header_height + alloc.y + (alloc.height * 0.2))) {
    			pos = "over";
			} else {
				pos = "above";
	    	GLib.debug("getRowAt return : %d, %s", line_no, pos);
		    return line_no;
	    curr_y = header_height + alloc.height + alloc.y;

	    if (curr_y > y) {
	    //    return -1;
        child = child.get_next_sibling(); 
    return -1;


The other issue is that double clicking on the cells is a bit haphazard. Sometimes it triggers.. other times you feel like you are pressing a lift button multiple times hoping it will come faster.

The other bug i managed to find was putting dropdowns on popovers. Don't do this it will hang the application. At present I've used a small column view to replace it.. but it needs a better solution as the interface is very confusing.

The other hill that I've yet to climb is context menus. Gtk4’s menu system is heavily focused on application menus. And they have dropped menuitems completely.

Context menus are usually closely related to the widget that triggers them, and sending a signal to the application, that then sends it back to the widget, seems very unnatural. For the time being I've ended up with popovers with buttons.. not perfect, but usable

I also had the opportunity to change the object tree adding for objects that ar properties of the parent.

Previously, adding a model to a columnview was done by using the add child + next to the columview item in the tree. It wouls show a list of potential child object, including ones that are properties.

Which is only working for the Roo Library so far

I've removed those from that object list now, and put them in the properties dialog, which then lists all the potential implementations of the property, if you expand it the list, and double click to add it to the tree

Anyway, now to work out some good demo videos

Add Your Comment

Follow us on