📄 gnome-canvas-events.html
字号:
<HTML><HEAD><TITLE>Canvas Events</TITLE><METANAME="GENERATOR"CONTENT="Modular DocBook HTML Stylesheet Version 1.61"><LINKREL="HOME"TITLE="Writing GNOME Applications"HREF="index.html"><LINKREL="UP"TITLE="The GNOME Canvas"HREF="gnome-canvas.html"><LINKREL="PREVIOUS"TITLE="Canvas Items"HREF="gnome-canvas-items.html"><LINKREL="NEXT"TITLE="GNU Free Documentation License"HREF="fdl.html"></HEAD><BODYCLASS="SECT1"><DIVCLASS="NAVHEADER"><TABLEWIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><THCOLSPAN="3"ALIGN="center">Writing GNOME Applications</TH></TR><TR><TDWIDTH="10%"ALIGN="left"VALIGN="bottom"><AHREF="gnome-canvas-items.html">Prev</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom">Chapter 11. The GNOME Canvas</TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><AHREF="fdl.html">Next</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="SECT1"><H1CLASS="SECT1"><ANAME="GNOME-CANVAS-EVENTS">Canvas Events</A></H1><P> As we learned in Section 11.1.4, only the Canvas widget as a whole can receive true GDK events. To pass events on to individual Canvas items, the Canvas must crack open the GDK events and translate them into internal Canvas events. In this section we'll investigate how to hook into the Canvas's synthetic event queue and react to a variety of mouse and keyboard events. </P><DIVCLASS="SECT2"><H2CLASS="SECT2"><ANAME="AEN1259">The Event Callback</A></H2><P> To avoid reinventing the wheel and to keep things simpler and more consistent for the developer, the GNOME Canvas reuses the existing GdkEvent structure, with a few minor modifications to its contents, before passing it on to the targeted Canvas item. The most important difference is that the event coordinates are translated from regular pixel-based window coordinates into abstract world coordinates. Fortunately, the coordinate fields in GdkEvent are already of type gdouble, because of the subpixel resolution of certain drawing devices, like drawing tablets, so the Canvas can reuse the GdkEvent structure ex- actly as it's defined in GTK+. </P><P> The item event callback looks like any other event callbacks you'll see for a normal GTK+ widget, depending on whether or not you care about the extra data parameter: </P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING">gint (* event) (GnomeCanvasItem *item, GdkEvent *event);gint (* event) (GnomeCanvasItem *item, GdkEvent *event, gpointer data); </PRE></TD></TR></TABLE><P> You set up the callback and connect the signal just as you would with a normal event handler. As far as your Canvas item is concerned (aside from coordinate system remapping), it is registering itself for a true GDK event. Naturally, you'll have to connect an event signal for every Canvas item you want to accept outside events. Any items you don't connect will ignore all keyboard and mouse events and will behave like a static part of the background. The application can still move the nonconnected items around and change their properties programmatically, through the API; the Canvas just won't be able to react to GDK events directed at those particular items. Listing 11.7 shows what your event handler might look like. </P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING">Listing 11.7 GnomeCanvasItem Event Handlergint handle_canvas_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data){ switch (event->type) { case GDK_BUTTON_PRESS: /* Handle mouse button press */ return TRUE; case GDK_BUTTON_RELEASE: /* Handle mouse button release */ return TRUE; case GDK_MOTION_NOTIFY: /* Handle mouse movement */ return TRUE; } /* Event not handled; try parent item */ return FALSE;}int main(int argc, char *argv[]){ GnomeCanvasItem *item; gpointer *itemdata; ... gtk_signal_connect(GTK_OBJECT(item), "event", GTK_SIGNAL_FUNC(handle_canvas_event), itemdata);} </PRE></TD></TR></TABLE><P> The Canvas first sends the event to the item with which the user is interacting; in the case of a mouse event, this is the item the cursor is directly over. If that item's event handler returns a TRUE, the item swallows the event and the event processing stops there. However, if the item's event handler returns a FALSE, indicating that the item chose not to process the event or doesn't care if its parent groups or items see the event too, the Canvas continues to pass the event up the hierarchy, from child item to parent item, until an event handler returns TRUE or the event reaches the Canvas's root item. In the example in Listing 11.7 we return TRUE for all the events we handle, but it's just as legal to allow a Canvas event to be handled multiple times, by returning a FALSE from the callback function. </P></DIV><DIVCLASS="SECT2"><H2CLASS="SECT2"><ANAME="AEN1267">Clicks and Double Clicks</A></H2><P> Handling a mouse click on a Canvas item is just like handling a mouse click on a true GTK+ widget. You check the GdkEvent for the type of event and react accordingly. The most common way to do this is with a switch( ) statement, as we did in Listing 11.7. </P><P> At first, it may seem intimidating to have to create an event handler for every Canvas item you want to interact with. If you had to do that with every normal widget, your application would quickly bloat into a maze of event- processing code. (Fortunately, most of this event code is hidden inside each widget, safely tucked away in the respective GTK+ or GNOME library.) </P><P> Really, though, you don't need that much event code for Canvas items. Items can receive only a small subset of the possible GDK events. The only Canvas item events you'll have to worry about are mouse clicks, mouse movements, keyboard events, and any item-specific events a derived Canvas item might declare. The Canvas takes care of all the rest, including the painting and exposures, focus switching, selection, and creation. You can also share the same event handler with multiple similar items, using the handler's user data parameter if you need to distinguish among them. </P><P> Let's dive a little deeper into mouse click events. The item can receive two types of click events: a button press and a button release. You can find out which type of click event, if any, the current event is by checking the type field of the GdkEvent structure, and you can react to that value in a switch( ) statement, as in Listing 11.7. The GdkEvent structure is really a union of similar structures, each one populated with fields useful to a specific event type, as shown in Listing 11.8. Peruse the GDK documentation for more information about these various event types. For now, we're interested in only the GdkEventButton structure (see Listing 11.9). </P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING">Listing 11.8 GdkEvent Structureunion _GdkEvent{ GdkEventType type; GdkEventAny any; GdkEventExpose expose; GdkEventNoExpose no_expose; GdkEventVisibility visibility; GdkEventMotion motion; GdkEventButton button; GdkEventKey key; GdkEventCrossing crossing; GdkEventFocus focus_change; GdkEventConfigure configure; GdkEventProperty property; GdkEventSelection selection; GdkEventProximity proximity; GdkEventClient client; GdkEventDND dnd;}; </PRE></TD></TR></TABLE><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING">Listing 11.9 GdkEventButton Structurestruct _GdkEventButton{ GdkEventType type; GdkWindow *window; gint8 send_event; guint32 time; gdouble x; gdouble y; gdouble pressure; gdouble xtilt; gdouble ytilt; guint state; guint button; GdkInputSource source; guint32 deviceid; gdouble x_root, y_root;}; </PRE></TD></TR></TABLE><P> The GdkEvent union carries GdkEventButton as its button field. Thus you can find out the coordinates of the button click (in world coordinates) inside your event handler with event->button.x and event->button.y. If you need to differentiate which mouse button the user clicked, you can check the value of event->button.button. This value can be any integer from 1 through 5. The left button is typically 1, the middle button 2, and the right button 3, although the user is allowed to remap these values through the X server. Buttons 4 and 5 are most often used by wheel mice. GTK+ does its best to handle these values for you, so you're better off ignoring these last two buttons. </P><P> The other useful GdkEventButton field is state. The state field tells you what else was being held down at the time of the current event, not including the button or key that caused the event. You can use this field to determine if the user made a multibutton click, a Ctrl-click, a Shift-Alt-click, or something else. To express more than one concurrent state, GTK+ defines an enumeration of bit flags, as shown in Listing 11.10. The corresponding bit is turned on in GdkEventButton's state field for each modifier that was active at the time of the event. </P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING">Listing 11.10 GdkModifierType Enumerationtypedef enum{ GDK_SHIFT_MASK = 1 << 0, GDK_LOCK_MASK = 1 << 1, GDK_CONTROL_MASK = 1 << 2, GDK_MOD1_MASK = 1 << 3, GDK_MOD2_MASK = 1 << 4, GDK_MOD3_MASK = 1 << 5, GDK_MOD4_MASK = 1 << 6, GDK_MOD5_MASK = 1 << 7, GDK_BUTTON1_MASK = 1 << 8, GDK_BUTTON2_MASK = 1 << 9, GDK_BUTTON3_MASK = 1 << 10, GDK_BUTTON4_MASK = 1 << 11, GDK_BUTTON5_MASK = 1 << 12, GDK_RELEASE_MASK = 1 << 13, GDK_MODIFIER_MASK = 0x3fff} GdkModifierType; </PRE></TD></TR></TABLE><P> Of particular note are the Shift and Ctrl keys (GDK_SHIFT_MASK and GDK_CONTROL_MASK), the Alt or Meta key (GDK_MOD1_MASK), the Caps Lock key (GDK_LOCK_MASK), and the mouse buttons (GDK_BUTTON?_MASK). Thus if the user made a Shift-Ctrl-click with the first mouse button, the state field would equal (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_BUTTON1_MASK), and the button field would be set to 1. The GDK_RELEASE_MASK flag isn't used much for Canvas items. </P><P> Be careful not to make direct equality tests against the state field because often multiple flags are set. For example, the aforementioned Shift-Ctrl-click would result in a state field of 261 (256 + 4 + 1), which is not equal to GDK_SHIFT_MASK's value of 1, nor to GDK_CONTROL_MASK's value of 4. You should use the binary & operator in your comparisons: </P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING">if((event->button.state & GDK_SHIFT_MASK) && (event->button.state & GDK_CONTROL_MASK))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -