Create a composite component
Introduction
You may be happy to create such a type of component. It is just a combination of other GTK components. It provides a convenient way to assemble user interface elements for reuse. The file selection and Color Selection Component in the standard release are examples of this type of component.
We will create a game component in this section, a 3x3 switch button matrix, trigger a signal when all three buttons in the same column, row, or diagonal line are pressed.
Select a parent class
Generally, the parent class of a composite component is a container class that holds all elements of the composite component. For example, the parent class of the file selection component is the dialog box class. Because our buttons are arranged in a table, we should make the table class our parent class. But unfortunately, it cannot work. Component creation is divided into two functions-one user callsWidgetname_new ()
Function, the other isWidgetname_init ()
The function is used to initialize the component._ New ()
Function parameters. The sub-component only calls_ Init
Function. However, this division of labor cannot work normally in the table, because you need to know the number of rows and columns of the table when creating the table. Unless we want to implement it againGtk_table_new ()
To avoid deriving components from a table. For this reason, instead of the table, we derive the component from the vertical box, and then put the table into the vertical box.
Header file
Each component class has a header file that declares the object, class structure, and common functions of the component. Two features are worth noting. To avoid repeated definitions, we put the entire header file into the following statement:
#ifndef __TICTACTOE_H__ #define __TICTACTOE_H__ . . . #endif /* __TICTACTOE_H__ */ |
In addition, the C ++ program can include the header file:
#ifdef __cplusplus extern "C" { #endif /* __cplusplus */ . . . #ifdef __cplusplus } #endif /* __cplusplus */ |
In our header file, three standard macros are declared together with functions and structures.Tictactoe (OBJ)
,Tictactoe_class (class)
AndIs_tictactoe (OBJ)
These three macros convert the pointer to an object, a class pointer, and check whether an object is a game component.
The following are all header files:
/* GTK-gimp Toolkit * Copyright (c) 1995-1997 Peter Mattis, Spencer Kimball, and Josh MacDonald * * This program is free software. You can resend the Service under the gnu gpl clause released by the Free Software fund. * Or modify it. GPL can use version 2 or any subsequent version. * * The purpose of this program distribution is that it may be useful to others, but does not provide any guarantees, including implied * And guarantee suitable for specific purposes. For more information, see the GNU General Public License. * * You should have received a GNU General Public License along with the software. If not, please write * Free Software Foundation, inc., 59 temple place-Suite 330, * Boston, MA 02111-1307, USA. */
# Ifndef _ tictactoe_h __ # DEFINE _ tictactoe_h __
# Include <gdk/gdk. h> # Include <GTK/gtkvbox. h>
# Ifdef _ cplusplus Extern "C "{ # Endif/* _ cplusplus */
# Define tictactoe (OBJ) gtk_check_cast (OBJ, tictactoe_get_type (), tictactoe) # Define tictactoe_class (Klass) gtk_check_class_cast (Klass, tictactoe_get_type (), tictactoeclass) # Define is_tictactoe (OBJ) gtk_check_type (OBJ, tictactoe_get_type ())
Typedef struct _ tictactoe; Typedef struct _ tictactoeclass;
Struct _ tictactoe { Gtkvbox vbox;
Gtkwidget * buttons [3] [3]; };
Struct _ tictactoeclass { Gtkvboxclass parent_class;
Void (* tictactoe) (tictactoe * TTT ); };
Gtktype tictactoe_get_type (void ); Gtkwidget * tictactoe_new (void ); Void tictactoe_clear (tictactoe * TTT );
# Ifdef _ cplusplus } # Endif/* _ cplusplus */
# Endif/* _ tictactoe_h __*/
|
_ Get_type ()
Function
Now, we continue to implement our components.Widgetname_get_type ()
A function is the core function of each component. When you call this function for the first time, the function tells the GTK component class and obtains an ID that uniquely identifies the component class. Only this ID is returned.
GtkType tictactoe_get_type () { static guint ttt_type = 0;
if (!ttt_type) { GtkTypeInfo ttt_info = { "Tictactoe", sizeof (Tictactoe), sizeof (TictactoeClass), (GtkClassInitFunc) tictactoe_class_init, (GtkObjectInitFunc) tictactoe_init, (GtkArgSetFunc) NULL, (GtkArgGetFunc) NULL };
ttt_type = gtk_type_unique (gtk_vbox_get_type (), &ttt_info); }
return ttt_type; } |
The structure of gtktypeinfo is defined as follows:
struct _GtkTypeInfo { gchar *type_name; guint object_size; guint class_size; GtkClassInitFunc class_init_func; GtkObjectInitFunc object_init_func; GtkArgSetFunc arg_set_func; GtkArgGetFunc arg_get_func; }; |
The domain of this structure is very vivid. We ignore it hereArg_set_func
AndArg_get_func
These two domains: they are important, but most of them are not yet implemented. They allow the explanatory language to easily set the attributes of the component. Once GTK correctly fills in the structure, it knows how to create an object of the Special Component type.
_ Class_init ()
Function
Widgetname_class_init ()
The function initializes the domain of the component class structure and sets any signal for the class. The component of our word game is as follows:
enum { TICTACTOE_SIGNAL, LAST_SIGNAL };
static gint tictactoe_signals[LAST_SIGNAL] = { 0 };
static void tictactoe_class_init (TictactoeClass *class) { GtkObjectClass *object_class;
object_class = (GtkObjectClass*) class;
tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe), gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);
class->tictactoe = NULL; } |
Our component only has oneTictactoe
Signal. When a line, column, or diagonal line is connected, the signal is triggered. Not all composite components require signals. Therefore, when you read this section for the first time, you can skip the next section because this section is difficult for beginners.
Function:
gint gtk_signal_new( const gchar *name, GtkSignalRunType run_type, GtkType object_type, gint function_offset, GtkSignalMarshaller marshaller, GtkType return_val, guint nparams, ...); |
Create a new signal. The parameter is:
Name
: The name of the signal.
Run_type
: Set whether to run the default handler before or after the user's handler function. This value is usuallyGtk_run_first
OrGtk_run_last
, Although there are other values.
Object_type
: ID of the object that sends the signal. (It may also be the ID of the sub-object .)
Function_offset
: The offset of the class structure pointer to the default processing function.
Marshaller
: A function used to call the signal processing function. For signal processing functions with no parameters except for the objects that send signals and user data, we can use the marshaller function provided in advance.Gtk_signal_default_marshaller
.
Return_val
: Type of the returned value.
Nparams
: The number of parameters of the signal processing function (except for the two default parameters mentioned above: "The object that sends the signal" and "User Data)
...
: Data Type of the parameter.
When the type is specifiedGtktype
Enumeration type:
Typedef Enum { Gtk_type_invalid, Gtk_type_none, Gtk_type_char, Gtk_type_bool, Gtk_type_int, Gtk_type_uint, Gtk_type_long, Gtk_type_ulong, Gtk_type_float, Gtk_type_double, Gtk_type_string, Gtk_type_enum, Gtk_type_flags, Gtk_type_boxed, Gtk_type_foreign, Gtk_type_callback, Gtk_type_args,
Gtk_type_pointer,
/* If the following two objects can be deleted. */ Gtk_type_signal, Gtk_type_c_callback,
Gtk_type_object
} Gtkfundamentaltype; |
Gtk_signal_new ()
Returns a unique integer that can recognize a signal. we store it inTictactoe_signals
Array and use enumeration values for indexing. (By convention, the enumerated members are the names of signals in uppercase,Tictactoe ()
Macro conflicts, so we useTictactoe_signal
.)
After creating a signal, we need to connect gtk to the tictactoe class. CallGtk_object_class_add_signals ()
Functions can do this. Then we set the pointer to the default processing function of the "tictactoe" signal to null, indicating that there is no default action.
_ Init ()
Function
Each component class also needs a function to initialize the object structure. Generally, this function has a very limited task, that is, to set the structure member to the default value. For composite components, this function also creates component components.
static void tictactoe_init (Tictactoe *ttt) { GtkWidget *table; gint i,j;
table = gtk_table_new (3, 3, TRUE); gtk_container_add (GTK_CONTAINER(ttt), table); gtk_widget_show (table);
for (i=0;i<3; i++) for (j=0;j<3; j++) { ttt->buttons[i][j] = gtk_toggle_button_new (); gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j], i, i+1, j, j+1); gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled", GTK_SIGNAL_FUNC (tictactoe_toggle), ttt); gtk_widget_set_size_request (ttt->buttons[i][j], 20, 20); gtk_widget_show (ttt->buttons[i][j]); } } |
The rest...
In addition, each component of a function (except for basic component types, such as bin cannot be instantiated) needs to be called by the user to create a function of this type of object. This call is usuallyWidgetname_new ()
. In some components, this function obtains several parameters and makes some settings based on these parameters. Our game component does not. The other two functions are for the component of the game.
Tictactoe_clear ()
Is a public function that resets the buttons in the component. Note:Gtk_signal_handler_block_by_data ()
Function is used to prevent button switching signal processing functions from being triggered without any need.
Tictactoe_toggle ()
It is the signal processing function called when the user clicks the button. It determines whether a win-win combination exists in the switch button. If yes, it sends a "tictactoe" signal.
GtkWidget* tictactoe_new () { return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ())); }
void tictactoe_clear (Tictactoe *ttt) { int i,j;
for (i=0;i<3;i++) for (j=0;j<3;j++) { gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]), FALSE); gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt); } }
static void tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt) { int i,k;
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 } };
int success, found;
for (k=0; k<8; k++) { success = TRUE; found = FALSE;
for (i=0;i<3;i++) { success = success && GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active; found = found || ttt->buttons[rwins[k][i]][cwins[k][i]] == widget; }
if (success && found) { gtk_signal_emit (GTK_OBJECT (ttt), tictactoe_signals[TICTACTOE_SIGNAL]); break; } } } |
Finally, an example of a program that uses the game component:
# Include <GTK/GTK. h> # Include "tictactoe. H"
/* Call when the game wins */ Void Win (gtkwidget * widget, gpointer data) { G_print ("Yay! /N "); Tictactoe_clear (tictactoe (widget )); }
Int Main (INT argc, char * argv []) { Gtkwidget * window; Gtkwidget * TTT;
Gtk_init (& argc, & argv );
Window = gtk_window_new (gtk_window_toplevel );
Gtk_window_set_title (gtk_window (window), "aspect frame ");
Gtk_signal_connect (gtk_object (window), "Destroy ", Gtk_signal_func (gtk_exit), null );
Gtk_container_set_border_width (gtk_container (window), 10 );
/* Create a new game component */ TTT = tictactoe_new (); Gtk_container_add (gtk_container (window), TTT ); Gtk_widget_show (TTT );
/* Connect the processing function of the "tictactoe" signal */ Gtk_signal_connect (gtk_object (TTT), "tictactoe ", Gtk_signal_func (WIN), null );
Gtk_widget_show (window );
Gtk_main ();
Return 0; } |
<Previous |
Home |
Next >>> |
Analysis of a component |
Up |
Create component from scratch |