Larger Hello, World

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;
}

        

main()

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);
Gtk::init() must be called before using Inti::Gtk functionality. During initialization, command line arguments are scanned, and any arguments understood by Inti::Gtk are removed from argv. If arguments are removed from argv, argc is decremented accordingly.

The third line in main() creates an instance of a custom HelloWindow widget.
  HelloWindow * hw = new HelloWindow;

  hw->set_title ("Hello World");

  hw->show ();
A widget is simply an object that represents a user-visible area of the screen - such as a pushbutton, or a window. All widgets are subclasses of Gtk::Widget. HelloWindow's show() method, inherited from the Gtk::Widget base class, causes it to appear on the screen. (There's a corresponding hide() method, to make it disappear again.) The set_title() method sets the title of the window, which appears in its titlebar.

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 ();
The main loop is not specific to the Gtk module; it also works fine for nongraphical applications such as daemons. The Main::Loop class represents an entry point for the main loop. Its run() method loops infinitely, until its quit() method is called. When quit() is called, run() will return - in this program, when run() returns, the end of main() will be reached and the program will exit.

HelloWindow

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 ();
}              
            
Main::primary() is a function that returns the primary main loop, in this case the main loop object created in main(). Recall that the quit() method of Main::Loop causes the run() method to return; in this case, run() is the last operation in main(), so the application exits. Before calling quit(), it's important to be sure the primary loop still exists (windows can be destroyed more than once, so we may have already left the main loop).

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);
}
            
The member initializer creates the button widget; button_ is a smart pointer that holds a reference to the widget. (I use the trailing underscore convention for member variables.)

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 ();
}
It prints a message, and then calls Gtk::Window::destroy() to close the window. Remember that destroying the window invokes the on_destroy() hook, exiting the application.