Search code examples
cgtkgtk4

Layout Manager GtkCustomLayout with GTK4 and C


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;
}

enter image description here


Solution

  • 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.