The Cgu::WinBase and Cgu::MainWidgetBase classes can manage GTK+ objects constructed with GtkBuilder, and thus with UI interfaces generated by, say, glade, just as they can be used with GTK+ objects generated by hand. Lifetime management will work automatically (the Cgu::MainWidgetBase constructor calls g_object_ref_sink() to take ownership from the GtkBuilder object, and the Cgu::WinBase destructor calls gtk_widget_destroy() to correctly handle the reference count of top level windows).
With Cgu::WinBase, two different approaches are possible. First, the WinBase object can take an object comprising or derived from GtkWindow which has been generated by GtkBuilder in the ordinary way. Alternatively, Cgu::WinBase can construct its own GtkWindow object, and a widget heirarchy generated by GtkBuilder can be added to that top level window. The following two examples construct the demonstration Message class (as appearing in the Cgu::WinBase documentation) using GtkBuilder adopting either approach.
First, an example which constructs the top level window and all its children using GtkBuilder:
#include <exception>
#include <gtk/gtk.h>
struct UiBuildError: public std::exception {
public:
virtual const char* what()
const throw() {
return message.
get();}
UiBuildError(const char* msg):
message(g_strdup_printf("UiBuildError: %s", msg)) {}
~UiBuildError() throw() {}
};
extern "C" void message_button_clicked(GtkWidget*, void*);
public:
friend void message_button_clicked(GtkWidget*, void*);
};
void message_button_clicked(GtkWidget*, void* data) {
static_cast<Message*>(data)->close();
}
const gchar ui[] =
"<interface>"
" <object class='GtkWindow' id='win'>"
" <child>"
" <object class='GtkVBox' id='vbox'>"
" <property name='homogeneous'>FALSE</property>"
" <property name='spacing'>2</property>"
" <child>"
" <object class='GtkLabel' id='label'>"
" </object>"
" <packing>"
" <property name='fill'>FALSE</property>"
" </packing>"
" </child>"
" <child>"
" <object class='GtkHButtonBox' id='bbox'>"
" <child>"
" <object class='GtkButton' id='button'>"
" <property name='label'>gtk-ok</property>"
" <property name='use-stock'>TRUE</property>"
" <property name='can-focus'>TRUE</property>"
" </object>"
" </child>"
" </object>"
" <packing>"
" <property name='expand'>FALSE</property>"
" <property name='fill'>FALSE</property>"
" </packing>"
" </child>"
" </object>"
" </child>"
" </object>"
"</interface>";
GObject* win = gtk_builder_get_object(builder, "win");
if (!win) {
throw UiBuildError("Message::get_window(): Can't find 'win' object");
}
return GTK_WINDOW(win);
}
if (!gtk_builder_add_from_string(builder, ui, sizeof(ui) - 1, 0)) {
throw UiBuildError("Message::build_interface: Can't create interface from ui");
}
return builder;
}
Message::Message(const char* text,
true, 0,
get_window(builder)) {
GObject* component = gtk_builder_get_object(builder, "label");
if (!component) {
throw UiBuildError("Message constructor: Can't find 'label' object");
}
gtk_label_set_text(GTK_LABEL(component), text);
component = gtk_builder_get_object(builder, "button");
if (!component) {
throw UiBuildError("Message constructor: Can't find 'button' object");
}
g_signal_connect(component, "clicked",
G_CALLBACK(message_button_clicked), this);
gtk_widget_show_all(GTK_WIDGET(
get_win()));
}
int main(int argc, char* argv[]) {
gtk_init(&argc, &argv);
Message dialog("This is a message");
dialog.exec();
return 0;
}
Secondly, an example which attaches a widget heirarchy constructed with GtkBuilder to a GtkWindow object generated by the Cgu::WinBase object:
#include <exception>
#include <gtk/gtk.h>
struct UiBuildError: public std::exception {
public:
virtual const char* what()
const throw() {
return message.
get();}
UiBuildError(const char* msg):
message(g_strdup_printf("UiBuildError: %s", msg)) {}
~UiBuildError() throw() {}
};
extern "C" void message_button_clicked(GtkWidget*, void*);
public:
friend void message_button_clicked(GtkWidget*, void*);
Message(const char* text);
};
void message_button_clicked(GtkWidget*, void* data) {
static_cast<Message*>(data)->close();
}
const gchar ui[] =
"<interface>"
" <object class='GtkVBox' id='vbox'>"
" <property name='homogeneous'>FALSE</property>"
" <property name='spacing'>2</property>"
" <child>"
" <object class='GtkLabel' id='label'>"
" </object>"
" <packing>"
" <property name='fill'>FALSE</property>"
" </packing>"
" </child>"
" <child>"
" <object class='GtkHButtonBox' id='bbox'>"
" <child>"
" <object class='GtkButton' id='button'>"
" <property name='label'>gtk-ok</property>"
" <property name='use-stock'>TRUE</property>"
" <property name='can-focus'>TRUE</property>"
" </object>"
" </child>"
" </object>"
" <packing>"
" <property name='expand'>FALSE</property>"
" <property name='fill'>FALSE</property>"
" </packing>"
" </child>"
" </object>"
"</interface>";
Message::Message(const char* text): Cgu::WinBase("Message", 0, true) {
if (!gtk_builder_add_from_string(builder, ui, sizeof(ui) - 1, 0)) {
throw UiBuildError("Message::build_interface: Can't create interface from ui");
}
GObject* component = gtk_builder_get_object(builder, "vbox");
gtk_container_add(GTK_CONTAINER(
get_win()), GTK_WIDGET(component));
component = gtk_builder_get_object(builder, "label");
gtk_label_set_text(GTK_LABEL(component), text);
component = gtk_builder_get_object(builder, "button");
g_signal_connect(component, "clicked",
G_CALLBACK(message_button_clicked), this);
gtk_widget_show_all(GTK_WIDGET(
get_win()));
}
int main(int argc, char* argv[]) {
gtk_init(&argc, &argv);
Message dialog("This is a message");
dialog.exec();
return 0;
}
Care must be taken if initializing a Cgu::GobjHandle object with a widget or GObject object obtained from GtkBuilder. It is necessary to call g_object_ref() by hand in that case, as the Cgu::GobjHandle constructor taking a pointer does not call g_object_ref_sink() if the initializing object does not have a floating reference, and GtkBuilder always sinks floating references itself.