Tutorial

Inti Logo

« Anatomy of a Widget Creating a widget from scratch  »

Creating a Composite Widget

One type of widget that you may be interested in creating is a widget that is merely an aggregate of other Inti widgets. This type of widget does nothing that couldn't be done without creating new widgets, but provides a convenient way of packaging user interface elements for reuse. The FileSelection and ColorSelection widgets in the standard distribution are examples of this type of widget.

The example widget that we'll create in this section is the Tictactoe widget, a 3x3 array of toggle buttons which triggers a signal when all three buttons in a row, column, or on one of the diagonals are depressed.

Tictactoe


The parent class for a composite widget is typically the container class that holds all of the elements of the composite widget. For example, the parent of the FileSelection widget is the Dialog class. Since our buttons will be arranged in a table, it might seem natural to make our parent class the Table class. Unfortunately, this turns out not to work well because when tables are created they need to know the number of rows and columns, so we derive it from VBox instead and stick our table inside the VBox.

Each widget class has a header file which declares the object and its members. A couple of features are worth pointing out. To prevent multiple inclusions, we wrap the entire header file in:

#ifndef TICTACTOE_H
#define TICTACTOE_H

.
.
.
#endif // TICTACTOE_H

The header file for the Tictactoe class is tictactoe.h

#ifndef TICTACTOE_H
#define TICTACTOE_H

#ifndef INTI_GTK_BOX_H
#include <inti/gtk/box.h>
#endif

#ifndef INTI_SIGNALS_H
#include <inti/signals.h>
#endif


namespace Inti {

namespace Gtk {
class ToggleButton;
}

} // namespace Inti

/*  Tictactoe
 */

class Tictactoe : public Inti::Gtk::VBox
{
    Tictactoe(const Tictactoe&);
    Tictactoe& operator=(const Tictactoe&);

    Inti::Gtk::ToggleButton *buttons[3][3];

    void on_toggle(Inti::Gtk::ToggleButton *button);

public:
// Constructors
    Tictactoe();
    virtual ~Tictactoe();
   
// Methods
    void clear();

// Signals
    Inti::Signal0<void> tictactoe_signal;
};

#endif // TICTACTOE_H

Our widget has just one signal, the tictactoe_signal that is invoked when a row, column, or diagonal is completely filled in.

Inti provides a second signal library that lets you create your on custom signals. These signals are pure C++ signals unlike the previous signals which are just C++ wrappers for the native GTK+ signals. When implementing your own signals they need to be explicitly emitted. For example, you can emit the tictactoe_signal by either calling emit() or the signal's function operator, like this:

tictactoe_signal.emit();

// or

tictactoe_signal();

The source file for the Tictactoe class is tictactoe.cc:

#include"tictactoe.h"
#include <inti/gtk/table.h>
#include <inti/gtk/togglebutton.h>
#include <inti/bind.h>


using namespace Inti;

/*  Tictactoe
 */

Tictactoe::Tictactoe()
{
    Gtk::Table *table = new Gtk::Table(3, 3, true);
    add(*table);
    table->show();

    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            buttons[i][j] = new Gtk::ToggleButton;
            table->attach(*buttons[i][j], i, i + 1, j, j + 1);
            buttons[i][j]->sig_toggled().connect(bind(slot(this, &Tictactoe::on_toggle), buttons[i][j]));
            buttons[i][j]->set_size_request(20, 20);
            buttons[i][j]->show();
        }
    }
}

Tictactoe::~Tictactoe()
{
}

void

Tictactoe::clear()
{
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            buttons[i][j]->set_active(false);
        }
    }
}

void
Tictactoe::on_toggle(Gtk::ToggleButton *button)
{
    static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 }, { 0, 1, 2 },
                             { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 } };

    static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 }, { 0, 0, 0 },
                             { 1, 1, 1 }, { 2, 2, 2 }, { 0, 1, 2 }, { 2, 1, 0 } };

    bool success, found;
   
    for (int k = 0; k < 8; k++)
    {
        success = true;
        found = false;

        for (int i = 0; i < 3; i++)
        {
            success &= buttons[rwins[k][i]][cwins[k][i]]->get_active();
            found |= buttons[rwins[k][i]][cwins[k][i]] == button;
        }

        if (success && found)
        {
            tictactoe_signal.emit();
            break;
        }
    }
}


The source file for the Tictactoe example is ttt_test.cc:

#include<inti/main.h>
#include <inti/core.h>
#include <iostream>
#include "tictactoe.h"


using namespace Inti;

class TictactoeTest : public Gtk::Window
{
    Tictactoe *ttt;

protected:
    void on_win();

public:
    TictactoeTest();
    virtual ~TictactoeTest();
};

TictactoeTest::TictactoeTest()
{
    set_title("Tictactoe");
    set_border_width(10);

    ttt = new Tictactoe;

    add(*ttt);
    ttt->tictactoe_signal.connect(slot(this, &TictactoeTest::on_win));
    ttt->show();
}

TictactoeTest::~TictactoeTest()
{
}

void
TictactoeTest::on_win()
{
    using namespace std;

    cout << "Yay, you won!" << endl;
    ttt->clear();
}

INTI_MAIN(TictactoeTest)


Noticed that the syntax used to connect to our custom C++ signal is same that is used when connecting to a native GTK+ signal.


« Anatomy of a Widget Index
Writing Your Own Widgets
Top
Creating a widget from scratch  »