📄 gtk_tut-22.html
字号:
{ 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; } }}</PRE></CODE></BLOCKQUOTE><P>And finally, an example program using our Tictactoe widget:<P><BLOCKQUOTE><CODE><PRE>#include <gtk/gtk.h>#include "tictactoe.h"/* Invoked when a row, column or diagonal is completed */voidwin (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 Tictactoe widget */ ttt = tictactoe_new (); gtk_container_add (GTK_CONTAINER (window), ttt); gtk_widget_show (ttt); /* And attach to its "tictactoe" signal */ gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe", GTK_SIGNAL_FUNC (win), NULL); gtk_widget_show (window); gtk_main (); return 0;}</PRE></CODE></BLOCKQUOTE><P><H2><A NAME="ss22.4">22.4 Creating a widget from scratch.</A></H2><H3>Introduction</H3><P>In this section, we'll learn more about how widgets display themselveson the screen and interact with events. As an example of this, we'llcreate an analog dial widget with a pointer that the user can drag toset the value.<P><H3>Displaying a widget on the screen</H3><P>There are several steps that are involved in displaying on the screen.After the widget is created with a call to <CODE>WIDGETNAME_new()</CODE>,several more functions are needed:<P><UL><LI> <CODE>WIDGETNAME_realize()</CODE> is responsible for creating an Xwindow for the widget if it has one.</LI><LI> <CODE>WIDGETNAME_map()</CODE> is invoked after the user calls<CODE>gtk_widget_show()</CODE>. It is responsible for making sure the widgetis actually drawn on the screen (<EM>mapped</EM>). For a container class,it must also make calls to <CODE>map()</CODE>> functions of any child widgets.</LI><LI> <CODE>WIDGETNAME_draw()</CODE> is invoked when <CODE>gtk_widget_draw()</CODE>is called for the widget or one of its ancestors. It makes the actualcalls to the drawing functions to draw the widget on the screen. Forcontainer widgets, this function must make calls to<CODE>gtk_widget_draw()</CODE> for its child widgets.</LI><LI> <CODE>WIDGETNAME_expose()</CODE> is a handler for expose events for thewidget. It makes the necessary calls to the drawing functions to drawthe exposed portion on the screen. For container widgets, thisfunction must generate expose events for its child widgets which don'thave their own windows. (If they have their own windows, then X willgenerate the necessary expose events)</LI></UL><P>You might notice that the last two functions are quite similar - eachis responsible for drawing the widget on the screen. In fact manytypes of widgets don't really care about the difference between thetwo. The default <CODE>draw()</CODE> function in the widget class simplygenerates a synthetic expose event for the redrawn area. However, sometypes of widgets can save work by distinguishing between the twofunctions. For instance, if a widget has multiple X windows, thensince expose events identify the exposed window, it can redraw onlythe affected window, which is not possible for calls to <CODE>draw()</CODE>.<P>Container widgets, even if they don't care about the difference forthemselves, can't simply use the default <CODE>draw()</CODE> function becausetheir child widgets might care about the difference. However,it would be wasteful to duplicate the drawing code between the twofunctions. The convention is that such widgets have a function called<CODE>WIDGETNAME_paint()</CODE> that does the actual work of drawing thewidget, that is then called by the <CODE>draw()</CODE> and <CODE>expose()</CODE>functions.<P>In our example approach, since the dial widget is not a containerwidget, and only has a single window, we can take the simplestapproach and use the default <CODE>draw()</CODE> function and only implementan <CODE>expose()</CODE> function.<P><H3>The origins of the Dial Widget</H3><P>Just as all land animals are just variants on the first amphibian thatcrawled up out of the mud, Gtk widgets tend to start off as variantsof some other, previously written widget. Thus, although this sectionis entitled `Creating a Widget from Scratch', the Dial widget reallybegan with the source code for the Range widget. This was picked as astarting point because it would be nice if our Dial had the sameinterface as the Scale widgets which are just specialized descendentsof the Range widget. So, though the source code is presented below infinished form, it should not be implied that it was written, <EM>deusex machina</EM> in this fashion. Also, if you aren't yet familiar withhow scale widgets work from the application writer's point of view, itwould be a good idea to look them over before continuing.<P><H3>The Basics</H3><P>Quite a bit of our widget should look pretty familiar from theTictactoe widget. First, we have a header file:<P><BLOCKQUOTE><CODE><PRE>/* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#ifndef __GTK_DIAL_H__#define __GTK_DIAL_H__#include <gdk/gdk.h>#include <gtk/gtkadjustment.h>#include <gtk/gtkwidget.h>#ifdef __cplusplusextern "C" {#endif /* __cplusplus */#define GTK_DIAL(obj) GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial)#define GTK_DIAL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass)#define GTK_IS_DIAL(obj) GTK_CHECK_TYPE (obj, gtk_dial_get_type ())typedef struct _GtkDial GtkDial;typedef struct _GtkDialClass GtkDialClass;struct _GtkDial{ GtkWidget widget; /* update policy (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */ guint policy : 2; /* Button currently pressed or 0 if none */ guint8 button; /* Dimensions of dial components */ gint radius; gint pointer_width; /* ID of update timer, or 0 if none */ guint32 timer; /* Current angle */ gfloat angle; /* Old values from adjustment stored so we know when something changes */ gfloat old_value; gfloat old_lower; gfloat old_upper; /* The adjustment object that stores the data for this dial */ GtkAdjustment *adjustment;};struct _GtkDialClass{ GtkWidgetClass parent_class;};GtkWidget* gtk_dial_new (GtkAdjustment *adjustment);guint gtk_dial_get_type (void);GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial);void gtk_dial_set_update_policy (GtkDial *dial, GtkUpdateType policy);void gtk_dial_set_adjustment (GtkDial *dial, GtkAdjustment *adjustment);#ifdef __cplusplus}#endif /* __cplusplus */#endif /* __GTK_DIAL_H__ */</PRE></CODE></BLOCKQUOTE><P>Since there is quite a bit more going on in this widget, than the lastone, we have more fields in the data structure, but otherwise thingsare pretty similar.<P>Next, after including header files, and declaring a few constants,we have some functions to provide information about the widgetand initialize it:<P><BLOCKQUOTE><CODE><PRE>#include <math.h>#include <stdio.h>#include <gtk/gtkmain.h>#include <gtk/gtksignal.h>#include "gtkdial.h"#define SCROLL_DELAY_LENGTH 300#define DIAL_DEFAULT_SIZE 100/* Forward declarations */[ omitted to save space ]/* Local data */static GtkWidgetClass *parent_class = NULL;guintgtk_dial_get_type (){ static guint dial_type = 0; if (!dial_type) { GtkTypeInfo dial_info = { "GtkDial", sizeof (GtkDial), sizeof (GtkDialClass), (GtkClassInitFunc) gtk_dial_class_init, (GtkObjectInitFunc) gtk_dial_init, (GtkArgSetFunc) NULL, (GtkArgGetFunc) NULL, }; dial_type = gtk_type_unique (gtk_widget_get_type (), &dial_info); } return dial_type;}static voidgtk_dial_class_init (GtkDialClass *class){ GtkObjectClass *object_class; GtkWidgetClass *widget_class; object_class = (GtkObjectClass*) class; widget_class = (GtkWidgetClass*) class; parent_class = gtk_type_class (gtk_widget_get_type ()); object_class->destroy = gtk_dial_destroy; widget_class->realize = gtk_dial_realize; widget_class->expose_event = gtk_dial_expose; widget_class->size_request = gtk_dial_size_request; widget_class->size_allocate = gtk_dial_size_allocate; widget_class->button_press_event = gtk_dial_button_press; widget_class->button_release_event = gtk_dial_button_release; widget_class->motion_notify_event = gtk_dial_motion_notify;}static voidgtk_dial_init (GtkDial *dial){ dial->button = 0; dial->policy = GTK_UPDATE_CONTINUOUS; dial->timer = 0; dial->radius = 0; dial->pointer_width = 0; dial->angle = 0.0; dial->old_value = 0.0; dial->old_lower = 0.0; dial->old_upper = 0.0; dial->adjustment = NULL;}GtkWidget*gtk_dial_new (GtkAdjustment *adjustment){ GtkDial *dial; dial = gtk_type_new (gtk_dial_get_type ()); if (!adjustment) adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0); gtk_dial_set_adjustment (dial, adjustment); return GTK_WIDGET (dial);}static voidgtk_dial_destroy (GtkObject *object){ GtkDial *dial; g_return_if_fail (object != NULL); g_return_if_fail (GTK_IS_DIAL (object)); dial = GTK_DIAL (object); if (dial->adjustment) gtk_object_unref (GTK_OBJECT (dial->adjustment)); if (GTK_OBJECT_CLASS (parent_class)->destroy) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);}</PRE></CODE></BLOCKQUOTE><P>Note that this <CODE>init()</CODE> function does less than for the Tictactoewidget, since this is not a composite widget, and the <CODE>new()</CODE>function does more, since it now has an argument. Also, note that whenwe store a pointer to the Adjustment object, we increment itsreference count, (and correspondingly decrement when we no longer useit) so that GTK can keep track of when it can be safely destroyed.<P><P>Also, there are a few function to manipulate the widget's options:<P><BLOCKQUOTE><CODE><PRE>GtkAdjustment*gtk_dial_get_adjustment (GtkDial *dial){ g_return_val_if_fail (dial != NULL, NULL); g_return_val_if_fail (GTK_IS_DIAL (dial), NULL); return dial->adjustment;}voidgtk_dial_set_update_policy (GtkDial *dial, GtkUpdateType policy){ g_return_if_fail (dial != NULL); g_return_if_fail (GTK_IS_DIAL (dial)); dial->policy = policy;}voidgtk_dial_set_adjustment (GtkDial *dial, GtkAdjustment *adjustment){ g_return_if_fail (dial != NULL); g_return_if_fail (GTK_IS_DIAL (dial)); if (dial->adjustment) { gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial); gtk_object_unref (GTK_OBJECT (dial->adjustment)); } dial->adjustment = adjustment; gtk_object_ref (GTK_OBJECT (dial->adjustment)); gtk_signal_connect (GTK_OBJECT (adjustment), "changed", (GtkSignalFunc) gtk_dial_adjustment_changed, (gpointer) dial); gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed", (GtkSignalFunc) gtk_dial_adjustment_value_changed, (gpointer) dial); dial->old_value = adjustment->value; dial->old_lower = adjustment->lower; dial->old_upper = adjustment->upper; gtk_dial_update (dial);}</PRE></CODE></BLOCKQUOTE><P><H3><CODE>gtk_dial_realize()</CODE></H3><P>Now we come to some new types of functions. First, we have a functionthat does the work of creating the X window. Notice that a mask ispassed to the function <CODE>gdk_window_new()</CODE> which specifies which fields ofthe GdkWindowAttr structure actually have data in them (the remainingfields will be given default values). Also worth noting is the way theevent mask of the widget is created. We call<CODE>gtk_widget_get_events()</CODE> to retrieve the event mask that the userhas specified for this widget (with <CODE>gtk_widget_set_events()</CODE>, andadd the events that we are interested in ourselves.<P><P>After creating the window, we set its style and background, and put apointer to the widget in the user data field of the GdkWindow. Thislast step allows GTK to dispatch events for this window to the correctwidget.<P><BLOCKQUOTE><CODE><PRE>static voidgtk_dial_realize (GtkWidget *widget){ GtkDial *dial; GdkWindowAttr attributes; gint attributes_mask; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_DIAL (widget)); GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); dial = GTK_DIAL (widget); attributes.x = widget->allocation.x; attributes.y = widget->allocation.y; attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.window_type = GDK_WINDOW_CHILD; attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK; attributes.visual = gtk_widget_get_visual (widget); attributes.colormap = gtk_widget_get_colormap (widget); attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask); widget->style = gtk_style_attach (widget->style, widget->window); gdk_window_set_user_data (widget->window, widget);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -