Xfce Foundation Classes
 « Main Page

Writing a Composite Widget


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

The example widget that we'll create here 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. The parent class for a composite widget is typically the container class that will hold all of the elements of the composite widget. For example, the parent of the FontSelection widget is the Dialog class. Since our buttons will be arranged in a 3x3 array, it might seem natural to make our parent class the Table class. Unfortunately, this turns out not to work that well because when tables are created they need to know the number of rows and columns, so we'll derive the widget from VBox instead and pack a 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 at compile time, we wrap the entire header file in an inclusion guard:

#ifndef TICTACTOE_HH
#define TICTACTOE_HH
.
.
#endif // TICTACTOE_HH


 The header file for the Tictactoe class is <tictactoe.hh>:
 
#ifndef TICTACTOE_HH
#define TICTACTOE_HH

#ifndef XFC_GTK_BOX_HH
#include <xfc/gtk/box.hh>
#endif

namespace Xfc {

namespace Gtk {
class ToggleButton;
}

} // namespace Xfc

/*  Tictactoe
 */

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

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

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

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

// Signals
    sigc::signal<void> tictactoe_signal;
};

#endif // TICTACTOE_HH


The Tictactoe class declares a two dimensional  array of ToggleButtons pointers called 'buttons' and an  on_toggle() signal handler that is called each time a button's active state is toggled. The handler is a common signal handler that takes a ToggleButton pointer as its only argument. The clear() method resets the state of all toggle buttons to inactive, thus restarting the game. The 'tictactoe_signal' is a libsigc++ signal that gets emitted whenever a winning combination of buttons is toggled.

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

#include "tictactoe.hh"
#include <xfc/gtk/table.hh>
#include <xfc/gtk/togglebutton.hh>

using namespace Xfc;

/*  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]->signal_toggled().connect(sigc::bind(sigc::mem_fun(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;
        }
    }
}


In the Tictactoe constructor a 3x3 table is added to the vertical box. Then nine ToggleButtons are created and pointers to them added to the 'buttons' array. Each button's "toggled" signal is connected to the on_toggle() signal handler and the button's size is set to (20, 20). You will notice that sigc::bind() is used to bind each button's "toggled" signal to the on_toggle() signal handler. The clear() method is straight forward; it loops over each button setting its state to inactive.  Inside on_toggle() two arrays specifying the winning combinations are defined. Each time the handler is called it loops over all the buttons in the array, checking each button's active state. If a winning combination of buttons is found to have been toggled the 'tictactoe_signal' is emitted.

When implementing your own libsigc++ signals, they need to be explicitly emitted. For example, you can emit the tictactoe_signal either by calling emit(), as above, or by calling the signal's function operator, like this:

tictactoe_signal();

The source file for the application that will test the Tictactoe widget is <ttt_test.cc>:
 
#include <xfc/main.hh>
#include <xfc/gtk/window.hh>
#include <iostream>
#include "tictactoe.hh"

using namespace Xfc;

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(sigc::mem_fun(this, &TictactoeTest::on_win));
    ttt->show();
}

TictactoeTest::~TictactoeTest()
{
}

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

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

XFC_MAIN(TictactoeTest)


This program is fairly simple. The constructor creates a new Tictactoe widget and adds to the main window. Remember windows are bin widgets and can only contain one widget. The widget's 'tictactoe_signal' is connected to the on_win() signal handler which prints a congratulatory message to stdout (usually a console window).

Compiling Tictactoe

If you compiled and installed XFC yourself, you will find the source code for Tictactoe in the <examples/tictactoe> source directory along with a Makefile. If XFC came pre-installed, or you installed it from an RPM package, you will find the source code in the </usr/share/doc/xfcui-X.X/examples/tictactoe> subdirectory. In this case you will have to create the Makefile yourself (replace X.X with the version number of the libXFCui library you have installed).

To create a Makefile for Tictactoe, add the following lines to a new text file and save it using the name "Makefile":

CC = g++

CFLAGS = -Wall -O2

ttt_test: tictactoe.o ttt_test.o
    $(CC) ttt_test.o tictactoe.o -o ttt_test `pkg-config --libs xfcui-X.X`

ttt_test.o: ttt_test.cc tictactoe.hh
    $(CC) -c ttt_test.cc -o ttt_test.o $(CFLAGS) `pkg-config xfcui-X.X --cflags`

tictactoe.o: tictactoe.cc tictactoe.hh
    $(CC) -c tictactoe.cc -o tictactoe.o $(CFLAGS) `pkg-config xfcui-X.X --cflags`

clean:
    rm -f *.o ttt_test


If you cut and paste these lines make sure the whitespace before $(CC) and rm is a tab character. When you compile and run this program you will see the following window appear displaying the Tictactoe widget:



The XFC_MAIN macro is a convenience macro that writes a simple main function, its only argument is the name of the main window class. The macro is defined in <xfc/main.hh> as:

#define XFC_MAIN(MainWidget)\
    int main (int argc, char *argv[])\
    {\
        Xfc::Main::init(&argc, &argv);\
        MainWidget main_widget;\
        main_widget.signal_destroy().connect(sigc::ptr_fun(&Xfc::Main::quit));\
        main_widget.show();\
        Xfc::Main::run();\
        return 0;\
    }

Most main functions in C++ are simple because all the creation work for the main window is done inside its constructor, not the main function.


Copyright © 2004-2005 The XFC Development Team Top
XFC 4.4