If you have previous experience with C++ or GUI toolkits, you probably want to get a sense of Inti::Gtk's "feel" in a real-world context. This "Hello, World" is a bit longer than the traditional one, but includes all the major features and concepts of Inti::Gtk, so you can get an immediate sense of the library.
#include <inti/gtk/window.h> #include <inti/gtk/button.h> #include <inti/ptr.h> #include <inti/main.h> #include <iostream> using namespace Inti; class HelloWindow : public Gtk::Window { public: HelloWindow (); protected: ~HelloWindow (); virtual void on_destroy (); private: ptr<Gtk::Button> button_; void on_button_clicked (); }; HelloWindow::HelloWindow () : button_(new Gtk::Button ("Hello, World!")) { add (button_); button_->show (); button_->sig_clicked ().connect (this, &HelloWindow::on_button_clicked); } HelloWindow::~HelloWindow () { } void HelloWindow::on_destroy () { if (Main::primary ()) Main::primary ()->quit (); } void HelloWindow::on_button_clicked () { cout << "Button was clicked!" << endl; destroy (); } int main (int argc, char **argv) { using namespace Gtk; init (&argc, &argv); HelloWindow * hw = new HelloWindow; hw->set_title ("Hello World"); hw->show (); Main::Loop loop; loop.run (); return 0; } |
Let's start with main(), overlooking the details of the HelloWindow class for now. The first line in main() imports the Gtk namespace, to save some typing. The Inti namespace was imported at the top of the file.
There are three significant steps in main(): initializing Inti::Gtk, creating the HelloWindow widget, and entering the main loop.
Gtk::init() initializes the Inti::Gtk module.
init (&argc, &argv); |
The third line in main() creates an instance of a custom HelloWindow widget.
HelloWindow * hw = new HelloWindow; hw->set_title ("Hello World"); hw->show (); |
Note: Notice that HelloWindow is created on the heap with the new operator. This is required for many objects in Inti, because the objects are reference counted. If the objects were on the stack, the C++ compiler could unconditionally schedule their destruction regardless of reference count.
Once there's a widget for the user to interact with, the program goes to sleep and waits for events. When events occur, such as the user clicking on a button, the program wakes up again, handles the events, and goes back to sleep. This infinite loop is called the main loop.
Main::Loop loop; loop.run (); |
HelloWindow is a subclass of Gtk::Window; Gtk::Window represents a window on the screen, typically with a title bar, resize/minimize/maximize buttons, and so forth. HelloWindow extends the base class in two simple ways: it adds a pushbutton inside the window, and it exits the application when the window is closed or the button is clicked.
Conventionally, objects in Inti contain virtual methods that are "hooks" to be invoked when something of interest happens to the object. Hook methods are always named starting with on_. For example, the on_destroy() method is called when an object is destroyed (clicking the close button on a window will destroy it). HelloWindow implements on_destroy() with code that exits the application:
void HelloWindow::on_destroy () { if (Main::primary ()) Main::primary ()->quit (); } |
The HelloWindow constructor sets up the button inside the window.
HelloWindow::HelloWindow () : button_(new Gtk::Button ("Hello, World!")) { add (button_); button_->show (); button_->sig_clicked ().connect (this, &HelloWindow::on_button_clicked); } |
After creating the button, the code adds it to the window; the window's add() method achieves this. Then it's necessary to show() the button, so the user can see it.
The final statement in the constructor requires more explanation; it uses the Inti signal system. (Note that the signal system is separate from the Inti::Gtk module, so you can use it in nongraphical applications; but it is used pervasively in the Gtk module.)
button_->sig_clicked ().connect (this, &HelloWindow::on_button_clicked); |
Roughly speaking, a signal is a list of functions or methods to be invoked when a specific event occurs. In this case, when the button is clicked, you want the method on_button_clicked() to be called. Invoking the list of callbacks is referred to as emitting the signal. Connecting to a signal means adding a method to the list the signal emission will invoke.
In this case, the sig_clicked() method of Gtk::Button returns an object called a signal proxy. A signal proxy exports the public operations on a signal, most notably the ability to connect to it.
Signals are a flexible alternative to hook methods. In Inti::Gtk you always have a choice, because signals and hook methods come in pairs. That is, this program could have connected to the window's destroy signal instead of overriding on_destroy(), and it could have created a subclass of Gtk::Button that overrode the Gtk::Button::on_clicked() method. In each case it uses the most convenient option.
The implementation of HelloWindow::on_button_clicked () is trivial. This method is called when the button's clicked signal is emitted.
void HelloWindow::on_button_clicked () { cout << "Button was clicked!" << endl; destroy (); } |