GtkDialog subclass not using headerbar

My application is in Rust using gtk4. I have a preferences dialog which was originally created with a builder but was not subclassed. I am experiementing with creating it instead as a composite template and subclass of GtkDialog. I have it basically working except it no longer uses a headerbar. The ui file currently looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <template class="Prefs" parent="GtkDialog">
    <property name="title" translatable="1">Eva - Preferences</property>
    <property name="destroy-with-parent">1</property>
    <property name="modal">1</property>
    <property name="use-header-bar">1</property>
    <child type="titlebar">
      <object class="GtkHeaderBar"/>
    </child>
    <child type="action">
      <object class="GtkButton" id="button_cancel">
        <property name="label" translatable="1">_Cancel</property>
        <property name="use-underline">1</property>
      </object>
    </child>
    <child type="action">
      <object class="GtkButton" id="button_accept">
        <property name="label" translatable="1">_Accept</property>
        <property name="use-underline">1</property>
        <property name="receives-default">1</property>
      </object>
    </child>
    <action-widgets>
      <action-widget response="cancel">button_cancel</action-widget>
      <action-widget response="accept" default="true">button_accept</action-widget>
    </action-widgets>
    <child internal-child="content_area">
    <!-- omitted for brevity -->

And the Rust code:

// mod.rs
glib::wrapper! {
    pub struct Prefs(ObjectSubclass<imp::Prefs>)
        @extends gtk::Dialog, gtk::Widget, gtk::Window,
        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget,
            gtk::Native, gtk::Root, gtk::ShortcutManager;
}

impl Prefs {
    pub fn new() -> Self {
        let dlg: Self = Object::new(&[]).expect("Failed to create Preferences Dialog");
        // some more actions
        dlg
    }
}

The ui file was ported from the original just changing <object class="GtkDialog"> to <template class="Prefs">. I’m unsure why it isn’t using a headerbar anymore. I can force it to use a headerbar by adding dlg.set_titlebar(Some(&gtk::HeaderBar::new())); in the init function, but the dialog’s action buttons stay at the bottom of the window.

GtkDialog has a use-header-bar property which is a tri-state, -1/0/1. -1 is to do that based on current platform, 0 is to always not use a headerbar, 1 is to always one.

Not that if you use a use-header-bar, you should not set a titlebar property because now you are overriding that behaviour.

What I would really suggest here is just to use plain GtkWindow as you don’t benefit much from the GtkDialog API if you want to force the header bar usage all the time.

Thank you for answering. Based on your response, I removed the titlebar child from my ui file.

    <!-- removed -->
    <child type="titlebar">
      <object class="GtkHeaderBar"/>
    </child>

Still no headerbar. So I added a print statement to the object’s constructor to check and see if the property was set. Even though I’m setting it to 1 in the ui file, it is coming up as 0 when the object is constructed. My next step was attempting to manually set it via theset_property method.

impl Prefs {
    pub fn new() -> Self {
        let dlg: Self = Object::new(&[]).expect("Failed to create Preferences Dialog");
        dlg.set_property("use-header-bar", 1);

The result was unexpected. It panics, saying that the property is not writable.

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: BoolError { message: "property 'use-header-bar' of type 'Prefs' is not writable", filename: "/home/nathan/.cargo/registry/src/github.com-1ecc6299db9ec823/glib-0.15.11/src/object.rs", function: "glib::object", line: 3237 }', /home/nathan/.cargo/registry/src/github.com-1ecc6299db9ec823/glib-0.15.11/src/object.rs:2186:53

I could take your advice and just subclass a GtkWindow instead, but I’m thinking that something is incorrect with how I’m subclassing GtkDialog, and I don’t want to just subclass a different widget without understanding why this is failing.

I’m going to mark this solved, although I still don’t understand why it’s ignoring the property set in the ui file. I changed the new() constructor to set the property during construction, and it now works.

impl Prefs {
    pub fn new() -> Self {
        let dlg: Self = Object::new(&[("use-header-bar", &1)]).expect("Failed to create Preferences Dialog");

I suppose because GtkBuilder first constructs the object then uses g_object_set_propertieswhich won’t work for CONSTRUCT_ONLY properties like use-header-bar