GtkChild annotion tries to bind wrong element in child template #284

Closed
opened 2025-10-23 21:12:19 +02:00 by Nyeksenn · 7 comments
Nyeksenn commented 2025-10-23 21:12:19 +02:00 (Migrated from github.com)

My app has two composite templates.
One is for an AdwWindow and one is for an AdwNavigationPage.
I want to include one template as a child of the other.
If I do this according to the gtk docs the widgets render fine, but if I try to bind a child from my AdwNavigationPage template,
the program attempts to bind org.gnome.adw.NavigationPage instead of org.gnome.gtk.MenuButton
as it should be.

(java:21735): java-gi-CRITICAL **: 20:55:26.626: Cannot get template child headerbarMenuButton in class grooveboat.MusicChooser: Can not set org.gnome.gtk.MenuButton field grooveboat.MusicChooser.headerbarMenuButton to org.gnome.adw.NavigationPage

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <template class="GrooveboatWindow" parent="AdwWindow">
    <property name="width-request">640</property>
    <property name="height-request">360</property>
    <child>
      <object class="AdwBreakpoint">
        <condition>max-width: 400sp</condition>
        <setter object="split_view" property="collapsed">true</setter>
        <setter object="sidebar-toogle-button" property="visible">true</setter>
      </object>
    </child>
    <property name="content">
      <object class="AdwNavigationSplitView" id="split_view">
        <property name="sidebar-position">end</property>
        <property name="sidebar-width-fraction">0.5</property>
        <property name="show-content">true</property>
        <property name="sidebar">
...
        </property>
        <property name="content">
          <object class="MusicChooser"></object>
        </property>
      </object>
    </property>
  </template>
</interface>
<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <template class="MusicChooser" parent="AdwNavigationPage">
    <property name="title" translatable="yes">Content</property>
    <property name="tag">content</property>
    <property name="child">
      <object class="AdwToolbarView">
        <child type="top">
          <object class="AdwHeaderBar" id="header_bar">
            <property name="title-widget">
              <object class="AdwWindowTitle">
                <property name="title" translatable="yes">GroveBoat</property>
              </object>
            </property>
            <child type="start">
              <object class="GtkMenuButton" id="headerbar_menu_button">
                <property name="direction">none</property>
              </object>
            </child>
          </object>
        </child>
        <property name="content">
...
        </property>
      </object>
    </property>
  </template>
</interface>
@GtkTemplate(ui = "/grooveboat/music-chooser.ui", name = "MusicChooser")
public class MusicChooser extends NavigationPage {

    @GtkChild(name = "headerbar_menu_button")
    public MenuButton headerbarMenuButton;

    @InstanceInit
    public void init() {
    }

    public MusicChooser() {
    }
}

My app has two composite templates. One is for an AdwWindow and one is for an AdwNavigationPage. I want to include one template as a child of the other. If I do this according to the [gtk docs](https://developer.gnome.org/documentation/tutorials/widget-templates.html) the widgets render fine, but if I try to bind a child from my AdwNavigationPage template, the program attempts to bind org.gnome.adw.NavigationPage instead of org.gnome.gtk.MenuButton as it should be. `(java:21735): java-gi-CRITICAL **: 20:55:26.626: Cannot get template child headerbarMenuButton in class grooveboat.MusicChooser: Can not set org.gnome.gtk.MenuButton field grooveboat.MusicChooser.headerbarMenuButton to org.gnome.adw.NavigationPage` ```xml <?xml version="1.0" encoding="UTF-8"?> <interface> <template class="GrooveboatWindow" parent="AdwWindow"> <property name="width-request">640</property> <property name="height-request">360</property> <child> <object class="AdwBreakpoint"> <condition>max-width: 400sp</condition> <setter object="split_view" property="collapsed">true</setter> <setter object="sidebar-toogle-button" property="visible">true</setter> </object> </child> <property name="content"> <object class="AdwNavigationSplitView" id="split_view"> <property name="sidebar-position">end</property> <property name="sidebar-width-fraction">0.5</property> <property name="show-content">true</property> <property name="sidebar"> ... </property> <property name="content"> <object class="MusicChooser"></object> </property> </object> </property> </template> </interface> ``` ```xml <?xml version="1.0" encoding="UTF-8"?> <interface> <template class="MusicChooser" parent="AdwNavigationPage"> <property name="title" translatable="yes">Content</property> <property name="tag">content</property> <property name="child"> <object class="AdwToolbarView"> <child type="top"> <object class="AdwHeaderBar" id="header_bar"> <property name="title-widget"> <object class="AdwWindowTitle"> <property name="title" translatable="yes">GroveBoat</property> </object> </property> <child type="start"> <object class="GtkMenuButton" id="headerbar_menu_button"> <property name="direction">none</property> </object> </child> </object> </child> <property name="content"> ... </property> </object> </property> </template> </interface> ``` ```java @GtkTemplate(ui = "/grooveboat/music-chooser.ui", name = "MusicChooser") public class MusicChooser extends NavigationPage { @GtkChild(name = "headerbar_menu_button") public MenuButton headerbarMenuButton; @InstanceInit public void init() { } public MusicChooser() { } } ```
jwharm commented 2025-10-24 21:08:31 +02:00 (Migrated from github.com)

I've reproduced the problem, but don't know what the cause is yet. While assigning the template child objects to the Java fields, the class instance is a NavigationPage (the parent type of MusicChooser).

I've reproduced the problem, but don't know what the cause is yet. While assigning the template child objects to the Java fields, the class instance is a NavigationPage (the parent type of MusicChooser).
jwharm commented 2025-10-24 22:30:34 +02:00 (Migrated from github.com)

Aha, found it.

While processing the template ui files, Gtk constructs a new (native) instance of the MusicChooser gtype, and then a java "wrapper instance" is created for it. But your MusicChooser java class doesn't have a constructor for an existing native instance (i.e. a memory address).

Add this constructor to the MusicChooser class:

public MusicChooser(MemorySegment address) {
    super(address);
}

This is documented here but I can imagine that this is easily overlooked...

Aha, found it. While processing the template ui files, Gtk constructs a new (native) instance of the MusicChooser gtype, and then a java "wrapper instance" is created for it. But your MusicChooser java class doesn't have a constructor for an existing native instance (i.e. a memory address). Add this constructor to the `MusicChooser` class: ```java public MusicChooser(MemorySegment address) { super(address); } ``` This is documented [here](https://java-gi.org/register/#classes-constructed-from-native-code) but I can imagine that this is easily overlooked...
Nyeksenn commented 2025-10-24 23:14:09 +02:00 (Migrated from github.com)

Thank you so much for the quick response.
Totally my bad for not reading the docs properly. I didn't think this would be the issue. Maybe the logs could tell the user to
checks this if this type missmatch occurs.

Thank you so much for the quick response. Totally my bad for not reading the docs properly. I didn't think this would be the issue. Maybe the logs could tell the user to checks this if this type missmatch occurs.
Nyeksenn commented 2025-10-25 11:12:31 +02:00 (Migrated from github.com)

I added the constructor, and now the native code can instantiate this class.
However, if I register this class using Types.register() and annotate the class with RegisteredType,
the instantiated object doesn't have any children anymore, as if it had ignored the GtkTemplate annotation now.
The object is correctly instantiated if I manually instantiate the class once in the app instead of registering it,
but then it gets instantiated twice, which causes funky behavior.
I might be missing something again but I currently can't find what might be causing this.
It would be nice if you could help me out once more 😅.

I added the constructor, and now the native code can instantiate this class. However, if I register this class using Types.register() and annotate the class with RegisteredType, the instantiated object doesn't have any children anymore, as if it had ignored the GtkTemplate annotation now. The object is correctly instantiated if I manually instantiate the class once in the app instead of registering it, but then it gets instantiated twice, which causes funky behavior. I might be missing something again but I currently can't find what might be causing this. It would be nice if you could help me out once more 😅.
jwharm commented 2025-10-25 11:41:06 +02:00 (Migrated from github.com)

You need to use TemplateTypes.register() instead.

In the upcoming 0.13.0 release this will be automatic, and I already updated the docs, sorry.

You need to use `TemplateTypes.register()` instead. In the upcoming 0.13.0 release this will be automatic, and I already updated the docs, sorry.
jwharm commented 2025-10-25 11:44:30 +02:00 (Migrated from github.com)

Btw I agree that the error log for the missing constructor can be improved, please keep the bug open so I don't forget.

Btw I agree that the error log for the missing constructor can be improved, please keep the bug open so I don't forget.
Nyeksenn commented 2025-10-25 11:59:03 +02:00 (Migrated from github.com)

Oh I see. Now it works.
Thanks again.
I will keep the issue open.

Oh I see. Now it works. Thanks again. I will keep the issue open.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
java-gi/java-gi#284
No description provided.