From cb191ff6f18e291810077271f853658c8d679e3d Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Thu, 30 Sep 2010 11:12:14 +0100 Subject: [PATCH] cookbook: Add recipe about handling button events Recipe covers adding handlers for button-press-event and button-release-event signals on actors, and how to examine the content of a ClutterButtonEvent via API functions. The discussion section explains about click count (the criteria for how clicks get counted, including distance and time settings); how button numbers are reported; and how to use ClutterClickAction as an alternative for press + release in certain scenarios. --- doc/cookbook/events.xml | 388 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 388 insertions(+) diff --git a/doc/cookbook/events.xml b/doc/cookbook/events.xml index cb15421ed..b6e889023 100644 --- a/doc/cookbook/events.xml +++ b/doc/cookbook/events.xml @@ -1021,4 +1021,392 @@ ClutterStage *stage = clutter_event_get_stage (event); + +
+ Making an actor respond to button events + +
+ Problem + + You want an actor to respond to button events. These might + be buttons on an input device like a mouse; + or input events caused by other means, like touches on a screen. + + Some examples of where this is useful: + + + + For implementing button widgets which respond to + button clicks. + + + To make actor selections by mouse click (e.g. + as part of a drawing application). + + + To recognise a button press followed by pointer + motion and button release (e.g. to implement drag + and drop or kinetic animations). + + + +
+ +
+ Solution + + Connect a handler to the button-press-event + and/or button-release-event signals of an + actor. + + + The button-press-event is emitted + when a button is pressed (not necessarily released) on a reactive + actor; the button-release-event when a + button is released on a reactive actor (even if the button was + pressed down somewhere else). + + + First, ensure the actor is reactive: + + + +clutter_actor_set_reactive (actor, TRUE); + + + + Next, create a function to handle the signal(s) you are + interested in. The function signature is the same for both the + press and release signals: + + + +gboolean +callback_function (ClutterActor *actor, + ClutterEvent *event, + gpointer user_data); + + + + You can use a single function as the + callback for both signals (or write a different one for each signal). + Here's an example function which can be used as a callback + for both press and release signals, as it simply pulls data + out of the event and displays it: + + + +/* event is a ClutterButtonEvent + * for both the press and the release signal; it contains + * data about where the event occurred + */ +static gboolean +button_event_cb (ClutterActor *actor, + ClutterEvent *event, + gpointer user_data) +{ + gfloat x, y; + gchar *event_type; + guint button_pressed; + ClutterModifierType state; + gchar *ctrl_pressed; + guint32 click_count; + + /* where the pointer was (relative to the stage) + * when the button event occurred; use + * clutter_actor_transform_stage_point() + * to transform to actor-relative coordinates + */ + clutter_event_get_coords (event, &x, &y); + + /* check whether it was a press or release event */ + event_type = "released"; + if (clutter_event_type (event) == CLUTTER_BUTTON_PRESS) + event_type = "pressed"; + + /* which button triggered the event */ + button_pressed = clutter_event_get_button (event); + + /* keys down when the event occurred; + * this is a bit mask composed of the bits for each key held down + * when the button was pressed or released; see the + * ClutterModifierType enum in the Clutter API docs + * for a list of the available modifiers + */ + state = clutter_event_get_state (event); + + ctrl_pressed = "ctrl not pressed"; + if (state & CLUTTER_CONTROL_MASK) + ctrl_pressed = "ctrl pressed"; + + /* click count */ + click_count = clutter_event_get_click_count (event); + + g_debug ("button %d was %s at %.0f,%.0f; %s; click count %d", + button_pressed, + event_type, + x, + y, + ctrl_pressed, + click_count); + + return TRUE; +} + + + + Finally, connect the signals to the function(s): + + + +/* connect the press event */ +g_signal_connect (actor, + "button-press-event", + G_CALLBACK (button_event_cb), + NULL); + +/* connect the release event */ +g_signal_connect (actor, + "button-release-event", + G_CALLBACK (button_event_cb), + NULL); + + + + Pressing or releasing a button on the actor will now + trigger a call to the button_event_cb() + function. See the full + example for more details. + +
+ +
+ Discussion + + Properties of the ClutterButtonEvent + emitted by both signals should be examined using the + clutter_event_* functions (rather than struct + members directly), as in the example above. While most of these + functions are self-explanatory, a couple require more explanation: + see the sections below. + + Also covered below is an alternative approach to handling a + button press followed by a release on a single actor: by adding + a ClutterClickAction to an actor. See + this section + for details. + + Finally, a longer + example is included, showing how to make use of button press, + button release and pointer events in a simple drawing application. + +
+ Click count + + The click count records the number of times a press/release + pair occurred in sequence. You can retrieve it via the + clutter_event_get_click_count() function. + + + A press/release pair is effectively a click, so this term + will be used from now on throughout this section, + to make the explanation simpler. However, the click count has + nothing to do with ClutterClickActions, described + later. + + + + For clicks to be considered part of the same sequence (for + the purposes of counting), all the clicks after the first one + must occur within the global double_click_distance + (pixels) of the first click; and the time between click + n and click n+1 must be <= + the global double_click_time (milliseconds). + + The clicks do not have to occur on + the same actor: providing they occur within the double click + distance and time, they are counted as part of the same click + sequence. Also note that the clicks don't even have to happen + on a reactive actor: providing they happen somewhere on the + stage, they will still increment the click count. + + The default double click time and distance are + stored in the ClutterSettings associated + with an application. You can get/set their values like this: + + + +gint double_click_distance; +gint double_click_time; + +ClutterSettings *settings = clutter_settings_get_default (); + +/* get double click settings */ +g_object_get (settings, + "double-click-distance", &double_click_distance, + "double-click-time", &double_click_time, + NULL); + +/* set */ +g_object_set (settings, + "double-click-distance", 50, + "double-click-time", 1000, + NULL); + + + +
+ +
+ Button numbering + + clutter_event_get_button() returns + an integer representing the pressed or released button. + + In the case of a standard scroll mouse, the numbers + returned are reliable across different hardware models: + + + + 1 = left mouse button + + + 2 = scroll wheel + + + 3 = right mouse button + + + + For mice with more buttons, or other types of + input devices, the mappings may not be so + straightforward: you may have to experiment to see + which button returns which value. + +
+ +
+ <type>ClutterClickAction</type> + + ClutterActions add flexible event handling + to ClutterActors. They recognise and abstract + common sequences of low-level events into a single, more easily + managed high-level event. In the case of a + ClutterClickAction, the abstraction is over + a press followed by a release on a single actor. This is + achieved by "synthesising" the press and release signals on + the actor: in other words, the action captures those + two signals when emitted by a single actor; and, once captured, the + action emits a single clicked signal + instead of the two signals being + emitted by the actor. + + The pointer can move off the actor between the press and + release, but the press and release must both occur on the same + actor, with no intervening presses or releases on other + actors. In addition, there are no maximum distance or time + constraints on the press and release. + + If a press occurs and you want to force it to be released + (e.g. to break a pointer grab after a certain length of + time has elapsed), use + clutter_click_action_release(). + + On the down side, the clicked signal + doesn't present the same detailed ClutterButtonEvent + to the handler. So, for example, you can't get a click count from a + ClutterClickAction (though you could count + the clicks yourself, of course); and you don't have access + to the coordinates where the press or release occurred. + + To add a click action to a ClutterActor: + + + +ClutterAction *action = clutter_click_action_new (); +clutter_actor_add_action (actor, action); + + + + + An actor must still be set to reactive so that its + signals can be routed to a click action. + + + Create a handler function (note the function + signature is different from the one for the press or + releas signal handler): + + + +void +clicked_cb (ClutterClickAction *action, + ClutterActor *actor, + gpointer user_data) +{ + /* display the number of the clicked button (equivalent + * to the number returned by clutter_event_get_button()) + */ + g_debug ("Button %d clicked", clutter_click_action_get_button (action)); +} + + + + Connect the signal to the handler: + + + +g_signal_connect (action, + "clicked", + G_CALLBACK (clicked_cb), + NULL); + + + + The example + code gives a bit more detail about how to use click + actions. + +
+ +
+ +
+ Full examples + + + Examining properties of a <type>ClutterButtonEvent</type> + + + a code sample should be here... but isn't + + + + + + Using <type>ClutterClickAction</type> to capture + button events on an actor + + + a code sample should be here... but isn't + + + + + + Using button and pointer events for drawing + This code was inspired by + ClutterSmith + + + + a code sample should be here... but isn't + + + + +
+ +
+