c++-gtk-utils
|
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> #include <c++-gtk-utils/window.h> #include <c++-gtk-utils/shared_handle.h> #include <c++-gtk-utils/gobj_handle.h> // *** class headers *** struct UiBuildError: public std::exception { Cgu::GcharSharedHandle message; 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*); // Message objects are constructed from GtkBuilder objects. The class // has two helper static memberer functions, get_window() and // build_interface(). class Message: public Cgu::WinBase { // get_window() must be static as it is called when initialising the // base class static GtkWindow* get_window(const Cgu::GobjHandle<GtkBuilder>&); public: // build_interface() must be static as it is invoked before the // Message object constructor is entered static Cgu::GobjHandle<GtkBuilder> build_interface(); friend void message_button_clicked(GtkWidget*, void*); // the Message::build_interface() static member function provides // the in-built default UI for Message objects. Alternative // interfaces can be passed when the Message object is constructed, // but they must provide the 'win', 'label' and 'button' objects or // the constructor will throw UiBuildError Message(const char* text, const Cgu::GobjHandle<GtkBuilder>& = Message::build_interface()); }; // *** class implementation *** void message_button_clicked(GtkWidget*, void* data) { static_cast<Message*>(data)->close(); } // this is the default UI for Message objects, obtained by calling // Message::build_interface(). Any custom interface passed to the // Message constructor must provide 'win', 'label' and 'button' // objects. The remainder are optional. A custom interface could // for example provide an icon in the window, or change layout. 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>"; GtkWindow* Message::get_window(const Cgu::GobjHandle<GtkBuilder>& builder) { GObject* win = gtk_builder_get_object(builder, "win"); if (!win) { throw UiBuildError("Message::get_window(): Can't find 'win' object"); } return GTK_WINDOW(win); } Cgu::GobjHandle<GtkBuilder> Message::build_interface() { Cgu::GobjHandle<GtkBuilder> builder(gtk_builder_new()); 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, const Cgu::GobjHandle<GtkBuilder>& builder): Cgu::WinBase("Message", 0, 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())); } // *** user code *** 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> #include <c++-gtk-utils/window.h> #include <c++-gtk-utils/shared_handle.h> #include <c++-gtk-utils/gobj_handle.h> // *** class headers *** struct UiBuildError: public std::exception { Cgu::GcharSharedHandle message; 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*); class Message: public Cgu::WinBase { public: friend void message_button_clicked(GtkWidget*, void*); Message(const char* text); }; // *** class implementation *** void message_button_clicked(GtkWidget*, void* data) { static_cast<Message*>(data)->close(); } // this is the default UI for Message objects, obtained by calling // Message::build_interface(). Any custom interface passed to the // Message constructor must provide 'win', 'label' and 'button' // objects. The remainder are optional. A custom interface could // for example provide an icon in the window, or change layout. 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) { Cgu::GobjHandle<GtkBuilder> builder(gtk_builder_new()); 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())); } // *** user code *** 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.