Tutorial

Inti Logo

« Getting Started
Packing Widgets »

Moving On

  1. Coding Conventions
  2. References
  3. Parameters and Return Values
  4. More on Connecting Signals
  5. An Upgraded Hello World

Before working through an upgraded Hello World example lets take a brief look at some of the design principles that went into Inti, especially its coding conventions and use of pointers and references.


Coding Conventions

Inti uses the following coding conventions:

Function and variable names are in lower case with words separated by an underscore.

Private member variable names have a trailing underscore when naming conflicts arise.

Class accessors and methods use the name of the GTK+ function they wrap, without the library prefix or struct type, e.g. get_window() instead of gtk_widget_get_window().

Inti types are named with each word capitalized, as in RadioMenuItem, ScrolledWindow and Widget.

C++ data types such as int, unsigned int and char are used instead of their corresponding GLib typedefs, gint, guint and gchar.

Code formatting is essentially the GNU C++ coding style except for two differences. The * is associated with the variable name not the type, i.e. Widget *widget and the indentation of namespaces and templates is kept to a minimum to preserve horizontal space in header files.


References

You will need to understand references because they are an integral part of C++ and Inti uses them frequently for specifying function parameters and return values. There are two ways to pass an argument to a function, call-by-value and call-by-reference. Call-by-value copies an argument into the function parameter; modifying the parameter will have no affect on the argument. This is the default in both C and C++. Call-by-reference copies the address of an argument into the function parameter, not the argument; any modifications made to the parameter will affect the argument. In C you call-by-reference when you pass a pointer as an argument to a function. In C++ there are two ways to call-by-reference; you can either pass a pointer or a reference as an argument to a function. If it helps, you can think of a reference as behaving like a constant pointer but using a different notation and with several restrictions. A reference is declared by preceding the parameter name with the &, as in Gtk::Widget& widget. In contrast to pointers, Inti associates the & with the type not the variable name. The restrictions on the use references are:

  • You must initialize a reference, either at its point of declaration or in a constructor's initializer list if it is a class member.
  • No operator operates on a reference.  Operators operate on the object the reference refers to.
  • You cannot reference a reference.
  • You cannot create arrays of references.
  • You cannot create a pointer to a reference.
  • References are not allowed on bit fields.
  • You don't use the & operator when passing an object as a reference.
  • You don't use the * operator when using a reference. You use a reference as if it were the object being referred to.

Parameters and Return Values

  • A non-copyable object is passed as a pointer if null is a valid value for the call-by-reference function parameter. If null is not a valid value the object is passed by reference.
  • Copyable values such as built-in types, strings and vectors are passed as a const reference and returned by value.


More on Connecting Signals

Lets take another look at the signal connection method.

Connection connect(TypeInstance *instance, SlotType *slot, bool after = false)const;

Notice the Connection return value? This is a connection class that identifies your slot. As stated before, you may connect as many slots per signal as you need, and each will be called in turn, in the order they were connected. If you want further control over your connection you will need store a copy of the returned Connection class.

Connection c = button->sig_clicked().connect(slot(this, &MyWindow::on_button_clicked));

You can remove the slot from the signal's connection list by calling:

c.disconnect();

You don't need to call disconnect() for every slot because slots are automatically disconnected when the signal they're  connected to gets destroyed. You can temporarily disable a slot by calling the block() method:

c.block();

and you can enable a disabled slot by calling the unblock() method.

c.unblock();


An Upgraded Hello World

Let's take a look at a slightly improved helloworld with better examples of using slots. This will also introduce us to our next topic, packing widgets.

HelloWorld2

The header file for Hello Buttons!  is helloworld.h

#include<inti/main.h>
#include <inti/core.h>

using namespace Inti;

class HelloWorld2 : public Gtk::Window
{
protected:
    void on_clicked(const char *text);

public:
    HelloWorld2();
    ~HelloWorld2();
};


and the source file is helloworld2.cc

#include"helloworld2.h"
#include <inti/gtk/button.h>
#include <inti/bind.h>
#include <iostream>


HelloWorld2::HelloWorld2()
{
    // This is a new call, which just sets the title of our new window to "Hello Buttons!"
    set_title("Hello Buttons!");

    // Sets the border width of the window.
    set_border_width(10);

    // We create a box to pack widgets into. The box is not really visible, it is just used
    // as a tool to arrange widgets.
    Gtk::HBox *box1 = new Gtk::HBox;

    // Put the box into the main window.
    add(*box1);

    // Creates a new button with the label "Button 1".
    Gtk::Button *button = new Gtk::Button("Button 1");

    // Now when the button is clicked, we call the slot function with a pointer to "button 1" bound to it.
    button->sig_clicked().connect(bind(slot(this, &HelloWorld2::on_clicked), "button 1"));

    // Instead of Gtk::Container::add, we pack this button into the invisible box, which has been added to the window.
    box1->pack_start(*button);

    // Always remember this step, this tells Inti that our preparation for this button is complete,
    // and it can now be displayed.
    button->show();

    // Do these same steps again to create a second button
    button = new Gtk::Button("Button 2");

    // Call the same slot function with a different argument, passing a pointer to "button 2" instead.
    button->sig_clicked().connect(bind(slot(this, &HelloWorld2::on_clicked), "button 2"));
    box1->pack_start(*button);

    // The order in which we show the buttons is not really important, but I recommend showing the window last,
    // so it all pops up at once.
    button->show();
    box1->show();
}

HelloWorld2::~HelloWorld2()
{
}

void
HelloWorld2::on_clicked(const char *text)
{
    using namespace std;

    cout << "Hello again" << " - " << text << " " << "was pressed" << endl;
}

int main (int argc, char *argv[])
{
    using namespace Main;

    init(&argc, &argv);

    HelloWorld2 window;
    window.sig_destroy().connect(slot(&Inti::Main::quit));
    window.show();

    run();
    return 0;
}

Compile this program using the same linking arguments as our first example. You'll notice this time there is no easy way to exit the program, you have to use your window manager or command line to kill it. A good exercise for the reader would be to insert a third "Quit" button that will exit the program. You may also wish to play with the options to Gtk::Box::pack_start() while reading the next section. Try resizing the window, and observe the behavior.



« Getting Started
Index
Top
Packing Widgets »