Search code examples
c++tabsgtkmmglade

How do you add Gtk::Notebook tabs at runtime from Glade definitions?


I am creating a new GTK app (using gtkmm) and the main interface is tabbed, a bit like a web browser.

I want to create a new tab for each document the user opens, with the content of each tab being defined by Glade. I'm new to gtkmm, so I am not sure how to do this.

If I design my tab in Glade as a self-contained window, when I call Gtk::Builder::get_widget() it returns a pointer to the Gtk::Window, but I can't pass this to Gtk::Notebook::append_page() because that function requires a reference and I only have a pointer. I don't want to dereference the pointer, because I believe each call to get_widget() returns the same pointer - so if I want to add five pages the same, I need to somehow clone the UI structure before adding it to the Gtk::Notebook - otherwise changing a UI element on one tab will cause it to change on the other four tabs too as they are all pointing to the same UI widgets.

What is the best way to take a window designed in Glade and dynamically add it multiple times to a Gtk::Notebook?


EDIT:

Putting each tab's definitions into a separate *.glade file does allow me to load multiple instances so that part is all good.

However I still can't add the loaded structure to the Gtk::Notebook:

  • If I tell Gtk::Builder::get_widget() to retrieve the root Gtk::Window from the .glade file and add that as the new tab, it won't compile because I have to pass a Gtk::Widget to append_page() and it won't accept a Gtk::Window parameter instead.
  • If I tell it to pick the top-most widget instead, then I get a bunch of runtime errors:

    Can't set a parent on widget which has a parent
    gtkcontainer.c:858: container class `gtkmm__GtkWindow' has no child property named `tab-expand'
    gtkcontainer.c:858: container class `gtkmm__GtkWindow' has no child property named `tab-fill'
    gtkcontainer.c:858: container class `gtkmm__GtkWindow' has no child property named `tab-label'
    gtkcontainer.c:858: container class `gtkmm__GtkWindow' has no child property named `menu-label'
    gtkcontainer.c:858: container class `gtkmm__GtkWindow' has no child property named `position'
    Gtk:ERROR:gtkcontainer.c:3541:gtk_container_propagate_draw: assertion failed: (gtk_widget_get_parent (child) == GTK_WIDGET (container))
    Aborted (core dumped)
    
  • If I take the top-most widget and instead call reparent() and pass the Gtk::Notebook as the new parent then the new tab does show up with all the widgets correctly arranged, but then I can't control the tab's title.

There must be a 'correct' way to do this, I just can't figure out what it is!


Solution

  • Thanks to help from the gtkmm mailing list, I have the solution.

    In Glade, I had created a Gtk::Window with a single Gtk::Box in it, and put all my controls in the Box. This caused problems when trying to add the Box as a new tab, because the Box already had a parent (the Gtk::Window) so it was troublesome trying to make the Gtk::Notebook the new parent.

    Instead, I found you can right-click on the button for the Gtk::Box in Glade, and choose "Add widget as toplevel". There is then no need for a Gtk::Window at all, and as the Box has no parent, calling Gtk::Notebook::append_page() and passing the Gtk::Box works perfectly. I can then use the variant of append_page() that lets me set the tab's title at the same time.

    It is still a requirement however to create a new Gtk::Builder instance to load the controls for each tab, because each tab needs a new instance of the controls. If you just keep calling Gtk::Builder::get_widget() on the same object, it returns the same instance of the controls.