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 attaches a widget heirarchy constructed with GtkBuilder to a GtkApplicationWindow object constructed with a Cgu::WinBase object:
#include <exception>
#include <string>
#include <gtk/gtk.h>
class 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, GApplication* app);
};
namespace {
extern "C" {
void app_beep(GSimpleAction*,
GVariant*,
void*) {
gdk_beep();
}
void app_quit(GSimpleAction*,
GVariant*,
void* data) {
for (auto iter = wins.begin(); iter != wins.end(); ++iter) {
delete *iter;
}
}
GActionEntry app_entries[] = {
{ "beep", app_beep, NULL, NULL, NULL },
{ "quit", app_quit, NULL, NULL, NULL },
};
void win_beep(GSimpleAction*, GVariant*, void*) {
gdk_beep();
}
GActionEntry win_entries[] = {
{ "beep", win_beep, NULL, NULL, NULL },
};
}
}
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>";
const gchar menus[] =
"<interface>"
" <menu id='app-menu'>"
" <section>"
" <item>"
" <attribute name='label'>Beep</attribute>"
" <attribute name='action'>app.beep</attribute>"
" </item>"
" <item>"
" <attribute name='label'>_Quit</attribute>"
" <attribute name='action'>app.quit</attribute>"
" </item>"
" </section>"
" </menu>"
" <menu id='menubar'>"
" <submenu>"
" <attribute name='label'>Menu1</attribute>"
" <section>"
" <item>"
" <attribute name='label'>More beep</attribute>"
" <attribute name='action'>win.beep</attribute>"
" </item>"
" </section>"
" </submenu>"
" </menu>"
"</interface>";
Message::Message(const char* text,
GApplication* app):
Cgu::WinBase{
"Message", 0,
false, 0,
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(app)))} {
g_action_map_add_action_entries(G_ACTION_MAP(get_win()),
win_entries,
G_N_ELEMENTS(win_entries),
static_cast<void*>(0));
GError* err = 0;
if (!gtk_builder_add_from_string(builder, ui, sizeof(ui) - 1, &err)) {
std::string msg{"Message::build_interface: "};
msg += err->message;
throw UiBuildError{msg.c_str()};
}
GObject* component = gtk_builder_get_object(builder, "vbox");
if (!component) {
throw UiBuildError{"Message constructor: Can't find 'vbox' object"};
}
gtk_container_add(GTK_CONTAINER(get_win()), GTK_WIDGET(component));
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()));
}
g_action_map_add_action_entries(G_ACTION_MAP(app->
get_g_app()),
app_entries,
G_N_ELEMENTS(app_entries),
app);
GObject* component = gtk_builder_get_object(builder, "app-menu");
if (!component) {
g_error("%s", "startup: Can't find 'app-menu' object");
}
gtk_application_set_app_menu(GTK_APPLICATION(app->
get_g_app()),
G_MENU_MODEL(component));
component = gtk_builder_get_object(builder, "menubar");
if (!component) {
g_error("%s", "startup: Can't find 'menubar' object");
}
gtk_application_set_menubar(GTK_APPLICATION(app->
get_g_app()),
G_MENU_MODEL(component));
}
gtk_window_present(app->
get_windows().front()->get_win());
return;
}
try {
dialog =
new Message{
"This is a message", app->
get_g_app()};
}
catch (std::exception& e) {
g_error("%s", e.what());
}
}
int main(int argc, char* argv[]) {
}
Secondly, an example which constructs the top level window and all its children using GtkBuilder:
#include <exception>
#include <string>
#include <gtk/gtk.h>
class 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,
GApplication* app,
};
namespace {
extern "C" {
void app_beep(GSimpleAction*,
GVariant*,
void*) {
gdk_beep();
}
void app_quit(GSimpleAction*,
GVariant*,
void* data) {
for (auto iter = wins.begin(); iter != wins.end(); ++iter) {
delete *iter;
}
}
GActionEntry app_entries[] = {
{ "beep", app_beep, NULL, NULL, NULL },
{ "quit", app_quit, NULL, NULL, NULL },
};
void win_beep(GSimpleAction*, GVariant*, void*) {
gdk_beep();
}
GActionEntry win_entries[] = {
{ "beep", win_beep, NULL, NULL, NULL },
};
}
}
void message_button_clicked(GtkWidget*, void* data) {
static_cast<Message*>(data)->close();
}
const gchar ui[] =
"<interface>"
" <object class='GtkApplicationWindow' 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>";
const gchar menus[] =
"<interface>"
" <menu id='app-menu'>"
" <section>"
" <item>"
" <attribute name='label'>Beep</attribute>"
" <attribute name='action'>app.beep</attribute>"
" </item>"
" <item>"
" <attribute name='label'>_Quit</attribute>"
" <attribute name='action'>app.quit</attribute>"
" </item>"
" </section>"
" </menu>"
" <menu id='menubar'>"
" <submenu>"
" <attribute name='label'>Menu1</attribute>"
" <section>"
" <item>"
" <attribute name='label'>More beep</attribute>"
" <attribute name='action'>win.beep</attribute>"
" </item>"
" </section>"
" </submenu>"
" </menu>"
"</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);
}
GError* err = 0;
if (!gtk_builder_add_from_string(builder, ui, sizeof(ui) - 1, &err)) {
std::string msg{"Message::build_interface: "};
msg += err->message;
throw UiBuildError{msg.c_str()};
}
return builder;
}
Message::Message(const char* text,
GApplication* app,
false, 0,
get_window(builder)} {
g_object_set(get_win(),
"application", app,
static_cast<void*>(0));
g_action_map_add_action_entries(G_ACTION_MAP(get_win()),
win_entries,
G_N_ELEMENTS(win_entries),
static_cast<void*>(0));
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()));
}
g_action_map_add_action_entries(G_ACTION_MAP(app->
get_g_app()),
app_entries,
G_N_ELEMENTS(app_entries),
app);
GObject* component = gtk_builder_get_object(builder, "app-menu");
if (!component) {
g_error("%s", "startup: Can't find 'app-menu' object");
}
gtk_application_set_app_menu(GTK_APPLICATION(app->
get_g_app()),
G_MENU_MODEL(component));
component = gtk_builder_get_object(builder, "menubar");
if (!component) {
g_error("%s", "startup: Can't find 'menubar' object");
}
gtk_application_set_menubar(GTK_APPLICATION(app->
get_g_app()),
G_MENU_MODEL(component));
}
gtk_window_present(app->
get_windows().front()->get_win());
return;
}
try {
dialog =
new Message{
"This is a message", app->
get_g_app()};
}
catch (std::exception& e) {
g_error("%s", e.what());
}
}
int main(int argc, char* argv[]) {
}
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.