I'm in the process of trying out the different layout managers of GTK4. I was able to successfully test GtkBinLayout, GtkBoxLayout, GtkCenterLayout and GtkContstraintLayout (see script). However, I can't get any further with the GtkCustomLayout. Does anyone have an idea how I could run the script below with the GtkCustomLayout manager?
#include "my_widget.h"
struct _MyWidget
{
GtkWidget parent_instance;
GtkWidget *label;
GtkWidget *entry;
GtkWidget *button;
};
struct _MyWidgetClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (MyWidget, my_widget, GTK_TYPE_WIDGET)
//Clear the template children when disposing the widget instance
static void
my_widget_dispose (GObject *gobject)
{
gtk_widget_dispose_template (GTK_WIDGET (gobject), MY_TYPE_WIDGET);
G_OBJECT_CLASS (my_widget_parent_class)->dispose (gobject);
}
static void
build_constraints (MyWidget *self, GtkConstraintLayout *manager)
{
//Width of the label
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new_constant(GTK_CONSTRAINT_TARGET (MY_WIDGET(self)->label),
GTK_CONSTRAINT_ATTRIBUTE_WIDTH,GTK_CONSTRAINT_RELATION_EQ,400.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED)); // 400
// Height of the label
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new_constant(GTK_CONSTRAINT_TARGET (MY_WIDGET(self)->label),
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT,GTK_CONSTRAINT_RELATION_EQ,50.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED)); // 50
//Width of Entry
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new_constant(GTK_CONSTRAINT_TARGET (MY_WIDGET(self)->entry),
GTK_CONSTRAINT_ATTRIBUTE_WIDTH,GTK_CONSTRAINT_RELATION_EQ,300.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED)); // 300
// Label's starting position
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new(NULL, GTK_CONSTRAINT_ATTRIBUTE_START,GTK_CONSTRAINT_RELATION_EQ,
MY_WIDGET(self)->label, GTK_CONSTRAINT_ATTRIBUTE_START,1.0, -10.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED)); // Starting edges (left)
//Horizontal Label Position
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new(NULL, GTK_CONSTRAINT_ATTRIBUTE_TOP,GTK_CONSTRAINT_RELATION_EQ,
MY_WIDGET(self)->label, GTK_CONSTRAINT_ATTRIBUTE_TOP,1.0, -10.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
// Horizontal Entry Position
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new(MY_WIDGET(self)->label, GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,GTK_CONSTRAINT_RELATION_EQ,
MY_WIDGET(self)->entry, GTK_CONSTRAINT_ATTRIBUTE_TOP,1.0, 0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
// Entry starting position
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new(NULL, GTK_CONSTRAINT_ATTRIBUTE_START,GTK_CONSTRAINT_RELATION_EQ,
MY_WIDGET(self)->entry, GTK_CONSTRAINT_ATTRIBUTE_START,1.0, -10.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED)); // Starting edges (left)
// Horizontal position of the button
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new(MY_WIDGET(self)->label, GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,GTK_CONSTRAINT_RELATION_EQ,
MY_WIDGET(self)->button, GTK_CONSTRAINT_ATTRIBUTE_TOP,1.0, 0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
// Starting position of the button
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new(MY_WIDGET(self)->entry, GTK_CONSTRAINT_ATTRIBUTE_END,GTK_CONSTRAINT_RELATION_EQ,
MY_WIDGET(self)->button, GTK_CONSTRAINT_ATTRIBUTE_START,1.0, -5.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED)); // Starting edges (left)
}
static void
my_widget_init (MyWidget *self)
{
GtkLayoutManager *constraint_layout;
constraint_layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
gtk_widget_init_template (GTK_WIDGET (self));
build_constraints (self, GTK_CONSTRAINT_LAYOUT(constraint_layout));
}
static void
my_widget_class_init (MyWidgetClass *class)
{
G_OBJECT_CLASS (class)->dispose = my_widget_dispose;
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
gtk_widget_class_set_template_from_resource (widget_class,"/Prog/widget.ui");
//Bind the widgets defined inside the template file to the corresponding
//members of the widget’s instance data structure
gtk_widget_class_bind_template_child (widget_class, MyWidget, entry);
gtk_widget_class_bind_template_child (widget_class, MyWidget, button);
gtk_widget_class_bind_template_child (widget_class, MyWidget, label);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_CONSTRAINT_LAYOUT);
}
GtkWidget *
my_widget_new ()
{
MyWidget *self;
self = g_object_new(MY_TYPE_WIDGET,NULL);
return GTK_WIDGET (self);
}
/****************************************************************/
static void activate (GtkApplication *app, gpointer user_data)
{
GtkWidget *window;
window = gtk_application_window_new(app);
gtk_window_set_resizable (GTK_WINDOW( window),FALSE);
GtkWidget * widget = my_widget_new();
gtk_window_set_child(GTK_WINDOW(window),GTK_WIDGET(widget));
gtk_window_present(GTK_WINDOW (window));
}
int main (int argc, char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gtk.mywidget", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect(app, "activate",G_CALLBACK(activate),NULL);
status = g_application_run (G_APPLICATION(app), argc, argv);
g_object_unref(app);
return status;
}
While I was hoping for an answer, I kept trying myself. I have now found a variant that works at least. However, it remains unclear whether it will be recommended in this way.
#include "my_widget.h"
struct _MyWidget
{
GtkWidget parent_instance;
GtkWidget *label;
GtkWidget *entry;
GtkWidget *button;
};
struct _MyWidgetClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (MyWidget, my_widget, GTK_TYPE_WIDGET)
//Clear the template children when disposing the widget instance
static void
my_widget_dispose (GObject *gobject)
{
gtk_widget_dispose_template (GTK_WIDGET (gobject), MY_TYPE_WIDGET);
G_OBJECT_CLASS (my_widget_parent_class)->dispose (gobject);
}
// childs position
static void custom_allocate(GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkRequisition label_req;
GtkRequisition entry_req;
GtkRequisition button_req;
gtk_widget_get_preferred_size(MY_WIDGET(widget)->button, &button_req,NULL);
gtk_widget_get_preferred_size(MY_WIDGET(widget)->label, &label_req,NULL);
gtk_widget_get_preferred_size(MY_WIDGET(widget)->entry, &entry_req,NULL);
gtk_widget_size_allocate(MY_WIDGET(widget)->label,
&(const GtkAllocation){width/2-(label_req.width/2),20,
label_req.width,label_req.height},-1);
gtk_widget_size_allocate(MY_WIDGET(widget)->entry,
&(const GtkAllocation){10,60,200,entry_req.height},-1);
gtk_widget_size_allocate(MY_WIDGET(widget)->button,
&(const GtkAllocation){220,60,button_req.width,button_req.height},-1);
}
void custom_mesure( GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimus,
int *natural,
int *minimal_baseline,
int *natural_baseline)
{
}
static GtkSizeRequestMode custom_requestmode (GtkWidget *widget)
{
return GTK_SIZE_REQUEST_CONSTANT_SIZE;
}
static void
my_widget_class_init (MyWidgetClass *class)
{
G_OBJECT_CLASS (class)->dispose = my_widget_dispose;
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
gtk_widget_class_set_template_from_resource (widget_class,"/Prog/widget.ui");
//Bind the widgets defined inside the template file to the corresponding
//members of the widget’s instance data structure
gtk_widget_class_bind_template_child (widget_class, MyWidget, entry);
gtk_widget_class_bind_template_child (widget_class, MyWidget, button);
gtk_widget_class_bind_template_child (widget_class, MyWidget, label);
}
static void
my_widget_init (MyWidget *self)
{
// Custom-Layout-Manager
GtkLayoutManager *custom_layout;
custom_layout = gtk_custom_layout_new(custom_requestmode,
custom_mesure,
custom_allocate);
gtk_widget_set_layout_manager(GTK_WIDGET(self),custom_layout);
// init template
gtk_widget_init_template (GTK_WIDGET (self));
}
GtkWidget *
my_widget_new ()
{
MyWidget *self;
self = g_object_new(MY_TYPE_WIDGET,NULL);
return GTK_WIDGET (self);
}
/****************************************************************/
static void activate (GtkApplication *app, gpointer user_data)
{
GtkWidget *window;
window = gtk_application_window_new(app);
gtk_window_set_resizable (GTK_WINDOW( window),FALSE);
GtkWidget * widget = my_widget_new();
gtk_window_set_child(GTK_WINDOW(window),GTK_WIDGET(widget));
gtk_window_present(GTK_WINDOW (window));
}
int main (int argc, char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gtk.mywidget", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect(app, "activate",G_CALLBACK(activate),NULL);
status = g_application_run (G_APPLICATION(app), argc, argv);
g_object_unref(app);
return status;
}
For those who want to try it out, here are the other necessary files.
my_widget.h
#pragma once
#include <gtk/gtk.h>
#define MY_TYPE_WIDGET (my_widget_get_type ())
G_DECLARE_FINAL_TYPE (MyWidget, my_widget, MY, WIDGET, GtkWidget)
GtkWidget * my_widget_new ();
resource.xml
<gresources>
<gresource prefix="Prog">
<file preprocess= "xml-stripblanks">widget.ui</file>
</gresource>
</gresources>
widget.ui
<interface>
<template class="MyWidget" parent="GtkWidget">
<property name="width-request">410</property>
<property name="height-request">200</property>
<child>
<object class="GtkLabel" id="label">
<property name="label">Hello</property>
</object>
</child>
<child>
<object class="GtkEntry" id="entry">
</object>
</child>
<child>
<object class="GtkButton" id="button">
<property name="label">Click my</property>
</object>
</child>
</template>
</interface>
compile.sh
#!/bin/bash
glib-compile-resources --target=ProgResource.c --generate-source resource.xml
gcc -Wno-format -o _my_Widget mywidget-layout.c ProgResource.c `pkg-config --cflags gtk4` `pkg-config --libs gtk4`
rm ProgResource.c
Regards.