This example implements the old 15-puzzle game using a collection of push buttons. All buttons have the same fixed dimension. When a button is pressed, it swaps itself with the empty square if it is adjacent to the empty square. This is done by setting a new location for the button. The layout of widgets in this program is the following.
We first create a toplevel frame toplevel
, with a vertical
orientation. It contains three children widgets; a label
at the
top; a frame frame
in the middle for holding the game buttons;
and another frame at the bottom for a Quit
button and a
Randomize
button. For frame
, we set its width and
height both to 0 so that it will not be expanded by its parent
toplevel
, in case toplevel
is resized. Finally,
we create 15 game buttons. The location of each game buttons is
encoded as an integer 4*row+col, and is saved at the IntData slots
in the widget data structure.
/***************** Example 2 ********************************/ #include "EZ.h" /* the header file */ static void cleanExit(EZ_Widget *, void *); /* quit btn callback */ static void button_callback(EZ_Widget *, void *); /* button callback */ static void randomize(EZ_Widget *, void *); /* scramble the puzzle */ static EZ_Widget *buttons[4][4]; /* the buttons */ static int width = 39, height = 33; /* size of the buttons */ static int emptyx = 3, emptyy = 3; /* the empty slot */ main(int ac, char **av) { int i,j, k; char str[4]; EZ_Widget *toplevel, *frame, *label, *tmp, *tmp1; EZ_Initialize(ac,av,0); /* Initialize EZWGL */ /* the toplevel frame */ toplevel = EZ_CreateWidget(EZ_WIDGET_FRAME,NULL, EZ_STACKING, EZ_VERTICAL, /* vert. orientation */ 0); label = EZ_CreateWidget(EZ_WIDGET_LABEL, toplevel, EZ_LABEL_STRING, "The 15 Puzzle", EZ_FOREGROUND, "blue", EZ_FONT_NAME, "-Adobe-Times-Bold-R-Normal--*-180-*-*-*-*-*-*", 0); frame = EZ_CreateWidget(EZ_WIDGET_FRAME, toplevel, /* the button box */ EZ_PADX,0, EZ_PADY,0, /* no padding */ EZ_BORDER_TYPE,EZ_BORDER_SUNKEN, /* a sunken border */ EZ_BORDER_WIDTH, 2, /* of width 2 */ EZ_WIDTH, 0, EZ_HEIGHT, 0, /* always keep it */ 0); /* at its min size */ tmp = EZ_CreateWidget(EZ_WIDGET_FRAME, toplevel, 0); /* a frame to hold two handy buttons */ tmp1 = EZ_CreateWidget(EZ_WIDGET_NORMAL_BUTTON, tmp, /* a quit button */ EZ_LABEL_STRING, "Quit", 0); EZ_AddWidgetCallBack(tmp1, EZ_CALLBACK, cleanExit, NULL, 0); tmp1 = EZ_CreateWidget(EZ_WIDGET_NORMAL_BUTTON, tmp, /* and a new game btn */ EZ_LABEL_STRING, "Scramble", 0); EZ_AddWidgetCallBack(tmp1, EZ_CALLBACK, randomize, NULL,0); for(i = 0; i < 4; i++) /* create buttons for the puzzle */ for(j = 0; j < 4; j++) /* 4 rows, 4 columns, 1 btns */ { if(i + j != 6) /* ignore the last one */ { k = 4 * i + j; sprintf(str,"%d",k+1); /* btn at [i,j] */ tmp = buttons[i][j] = EZ_CreateWidget(EZ_WIDGET_NORMAL_BUTTON, frame, EZ_LABEL_STRING,str, EZ_X, 3 + j * width, /* and a perminent size */ EZ_Y, 3 + i * height, EZ_WIDTH, width, EZ_HEIGHT, height, EZ_PROPAGATE, False, EZ_CALLBACK, button_callback, NULL, /* unique callback */ 0); EZ_SetWidgetIntData(tmp, k); /* K encodes its initial location */ } } srand(getpid()); EZ_DisplayWidget(toplevel); EZ_EventMainLoop(); } static void button_callback(EZ_Widget *widget, void *data) { int tmp, x, y; tmp = EZ_GetWidgetIntData(widget); /* cur location */ x = tmp >> 2; /* at the yth row, the x' column */ y = tmp & 0x3; if(abs(emptyx - x) + abs(emptyy - y) == 1) /* next to the empty square */ { /* swap with the empty square */ int tx = emptyx, ty = emptyy; emptyx = x; emptyy = y; /* the new empty square */ EZ_SetWidgetIntData(widget, 4 * tx + ty); /* and my new location */ EZ_SetWidgetPosition(widget, 3 + ty * width, 3 + tx * height); } } static void randomize(EZ_Widget *widget, void *data) { /* just make a few random moves */ int i, x, y; for(i = 0; i < 256; i++) { x = (rand()>> 5) % 4; y = (rand()>> 5) % 4; if((x + y != 6)) button_callback(buttons[x][y], NULL); } } static void cleanExit(EZ_Widget *w, void *data) { EZ_Shutdown(); exit(0); } /***************** Example 2 ********************************/
Setting the width (height) of a widget has a special side effect.
It prevents the widget from being stretched by its parent in the
horizontal (vertical) direction. This feature is, surprisingly, very
useful. Consider the following scenario. Your application's toplevel
widget contains two subwidgets arranged horizontally side by side.
Say, a list tree on the left and a text widget on the right. When the
toplevel widget window is resized, it by default divides the extra
space into two equal parts and distributes them to the two children
widgets. This may not be desirable since most of the time, one wants
the extra space be distributed to the text widget only.
This can be achieved by
setting the width of the list tree, so it will not be
stretchable in the horizontal direction. Example 3 is such a
program.