0

I am writing a custom widget which will be displayed within an AppWindow (AdwApplicationWindow) using AdwOverlaySplitView for the utility pane.

However, when I placed my custom widget which contains a GtkLabel itself under the 'content' property of AdwOverlaySplitView, it did not appear. But, when I used a GtkLabel instead, it was displayed correctly.

Here are my .ui and .c files

  • app-window.ui
?xml version="1.0" encoding="UTF-8"?>
<interface>
  <requires lib="gtk" version="4.0"/>
  <requires lib="Adw" version="1.0"/>
  <template class="AppWindow" parent="AdwApplicationWindow">
    <property name="content">
      <object class="AdwToolbarView">
        <child type="top">
          <object class="AdwHeaderBar" id="header_bar">
            <child type="start">
              <object class="GtkToggleButton" id="show_sidebar_button">
                <property name="icon-name">panel-left-symbolic</property>
                <property name="tooltip-text" translatable="yes">Toggle Sidebar</property>
                <property name="active">True</property>
              </object>
            </child>
            <child type="end">
              <object class="GtkMenuButton">
                <property name="primary">True</property>
                <property name="icon-name">open-menu-symbolic</property>
                <property name="tooltip-text" translatable="yes">Menu</property>
                <property name="menu-model">primary_menu</property>
              </object>
            </child>
          </object>
        </child>
        <property name="content">
          <object class="AdwOverlaySplitView" id="split_view">
          <property name="show-sidebar" bind-source="show_sidebar_button" bind-property="active" bind-flags="sync-create|bidirectional"/>
            <property name="sidebar">
                <!-- Sidebar content-->
            </property>
            <property name="content">
                <!-- Main Page content -->
                <object class="MyWidget">                   <!-- This is not getting displayed -->
                </object>
                <object class="GtkLabel">
                  <property name="label">Label</property>   <!-- This is getting displayed -->
              </object>
            </property>
          </object>
        </property>
      </object>
    </property>
  </template>
</interface>
  • my-widget.ui
<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <requires lib="gtk" version="4.0"/>
  <requires lib="Adw" version="1.0"/>
  <template class="MyWidget" parent="GtkWidget">
    <object class="GtkLabel">
      <property name="label">Label</property>
    </object>
  </template>
</interface>
  • my-widget.c
struct MyWidget
{
  GtkWidget parent_instance;
};


G_DEFINE_TYPE (MyWidget, my_widget, GTK_TYPE_WIDGET)

static void
my_widget_init (MyWidget *self)
{
  gtk_widget_init_template (GTK_WIDGET (self));
}

static void
my_widget_class_init (MyWidgetClass *klass)
{
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);

  gtk_widget_class_set_template_from_resource (widget_class, "<resource_path>");
}

I used the GTK Inspector, and it was able to idenity the widget. However, visually, there is no output.

Even though I've worked with .ui files before, they still confuse me. What am I doing wrong here?

2 Answers 2

1

When implementing a custom GtkWidget, your best bet is to use a GtkLayoutManager. Layout managers are delegate classes that can implement the layout details for a GtkWidget subtype, and are really convenient. You set them in the class_init() function:

static void
my_widget_class_init (MyWidgetClass *klass)
{
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);

  gtk_widget_class_set_template_from_resource (widget_class, "<resource_path>");

  /* Add this line */
  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
}

The box layout manager implements GtkOrientable so if you need to change the orientation or the spacing you can do the following in your init() function:

static void
my_widget_init (MyWidget *self)
{
  GtkLayoutManager *box_layout;

  box_layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
  gtk_orientable_set_orientation (GTK_ORIENTABLE (box_layout), GTK_ORIENTATION_VERTICAL);
  gtk_box_layout_set_spacing (GTK_BOX_LAYOUT (box_layout), 16);

  gtk_widget_init_template (GTK_WIDGET (self));
}

In this example, I used a box-style layout manager, but there are quite a few other variants, such as bins and tables. See the full list here. There's also more information at this development blog post.

Alternatively, if your layout is more exotic, you can implement the get_request_mode()/measure()/size_allocate() class methods to manually place subwidgets. This is actually quite complicated to do correctly and not recommended unless you cannot get an existing GtkLayoutManager to work.

Note, extending GtkBox as suggested in the other answer is the correct way to do it with gtk2 and gtk3, but is discouraged with gtk4 in favor of LayoutManagers. It will still work (for now), but it's overly complicated.

Sign up to request clarification or add additional context in comments.

Comments

0

If you want to create a widget based on GtkWidget you will have to manually implement the static method Gtk.WidgetClass.get_request_mode as explained here. Without this, the widget takes up no space and is not rendered.

Although there are situations where implementing a widget directly inheriting from GtkWidget makes sense, in most cases it is much simpler to just extend the main widget you are using in your custom widget. In your case, GtkLabel, leaving your XML like this:

<?xml version="1.0" encoding="UTF-8"?>
<interface>
   <requires lib="gtk" version="4.0"/>
   <requires lib="Adw" version="1.0"/>
   <template class="MyWidget" parent="GtkLabel">
      <property name="label">Label</property>
   </template>
</interface>

2 Comments

Yeah, the widget is composed of other widgets too. I was using GtkLabel for testing purpose. I implemented the get_request_mode as suggested, returning GTK_SIZE_REQUEST_CONSTANT_SIZE. Strangely, it's still not working too.
For a custom widget you can extend a Gtk.Box (parent="GtkBox") and place everything you need inside it. My understanding of C is very basic, I don't know enough to try to implement Gtk.Widget so my recommendation is still not to use it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.