From fc9ecdf82ed13cf14be9e8fd5fda9787f8972205 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Mon, 27 Sep 2010 16:36:16 +0100 Subject: [PATCH 1/4] cookbook: Example of simple handling of button events Added an example showing how to examine the content of a ClutterButtonEvent in a signal handler. --- doc/cookbook/examples/Makefile.am | 2 + doc/cookbook/examples/events-buttons.c | 107 +++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 doc/cookbook/examples/events-buttons.c diff --git a/doc/cookbook/examples/Makefile.am b/doc/cookbook/examples/Makefile.am index 2d38269b0..c74a45110 100644 --- a/doc/cookbook/examples/Makefile.am +++ b/doc/cookbook/examples/Makefile.am @@ -28,6 +28,7 @@ noinst_PROGRAMS = \ textures-crossfade-slideshow \ script-ui \ script-signals \ + events-buttons \ $(NULL) INCLUDES = \ @@ -76,5 +77,6 @@ textures_crossfade_cogl_SOURCES = textures-crossfade-cogl.c textures_crossfade_slideshow_SOURCES = textures-crossfade-slideshow.c script_ui_SOURCES = script-ui.c script_signals_SOURCES = script-signals.c +events_buttons_SOURCES = events-buttons.c -include $(top_srcdir)/build/autotools/Makefile.am.gitignore diff --git a/doc/cookbook/examples/events-buttons.c b/doc/cookbook/examples/events-buttons.c new file mode 100644 index 000000000..f6f6b0372 --- /dev/null +++ b/doc/cookbook/examples/events-buttons.c @@ -0,0 +1,107 @@ +#include +#include + +static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff }; +static const ClutterColor red_color = { 0xff, 0x00, 0x00, 0xff }; +static const ClutterColor green_color = { 0x00, 0xff, 0x00, 0xff }; + +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 when the button event occurred */ + 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 button was pressed */ + 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 %s at %.0f,%.0f; %s; click count %d", + button_pressed, + event_type, + x, + y, + ctrl_pressed, + click_count); + + return TRUE; +} + +int +main (int argc, + char *argv[]) +{ + ClutterActor *stage; + ClutterActor *red; + ClutterActor *green; + + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_actor_set_size (stage, 400, 400); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); + + red = clutter_rectangle_new_with_color (&red_color); + clutter_actor_set_size (red, 100, 100); + clutter_actor_set_position (red, 50, 150); + clutter_actor_set_reactive (red, TRUE); + + green = clutter_rectangle_new_with_color (&green_color); + clutter_actor_set_size (green, 100, 100); + clutter_actor_set_position (green, 250, 150); + clutter_actor_set_reactive (green, TRUE); + + g_signal_connect (red, + "button-press-event", + G_CALLBACK (button_event_cb), + NULL); + + g_signal_connect (red, + "button-release-event", + G_CALLBACK (button_event_cb), + NULL); + + g_signal_connect (green, + "button-press-event", + G_CALLBACK (button_event_cb), + NULL); + + g_signal_connect (green, + "button-release-event", + G_CALLBACK (button_event_cb), + NULL); + + clutter_container_add (CLUTTER_CONTAINER (stage), + red, + green, + NULL); + + clutter_actor_show (stage); + + clutter_main (); + + return EXIT_SUCCESS; +} From 0cda6c006b5909da5919c4e3a1492a32895c596c Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Tue, 28 Sep 2010 16:37:26 +0100 Subject: [PATCH 2/4] cookbook: Example of ClutterClickAction Example of handling clicks on an actor; part of the recipe on handling button events. --- doc/cookbook/examples/Makefile.am | 2 + doc/cookbook/examples/events-buttons-click.c | 66 ++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 doc/cookbook/examples/events-buttons-click.c diff --git a/doc/cookbook/examples/Makefile.am b/doc/cookbook/examples/Makefile.am index c74a45110..4ee90ee42 100644 --- a/doc/cookbook/examples/Makefile.am +++ b/doc/cookbook/examples/Makefile.am @@ -29,6 +29,7 @@ noinst_PROGRAMS = \ script-ui \ script-signals \ events-buttons \ + events-buttons-click \ $(NULL) INCLUDES = \ @@ -78,5 +79,6 @@ textures_crossfade_slideshow_SOURCES = textures-crossfade-slideshow.c script_ui_SOURCES = script-ui.c script_signals_SOURCES = script-signals.c events_buttons_SOURCES = events-buttons.c +events_buttons_click_SOURCES = events-buttons-click.c -include $(top_srcdir)/build/autotools/Makefile.am.gitignore diff --git a/doc/cookbook/examples/events-buttons-click.c b/doc/cookbook/examples/events-buttons-click.c new file mode 100644 index 000000000..bb6cbd68e --- /dev/null +++ b/doc/cookbook/examples/events-buttons-click.c @@ -0,0 +1,66 @@ +#include +#include + +static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff }; +static const ClutterColor red_color = { 0xff, 0x00, 0x00, 0xff }; +static const ClutterColor blue_color = { 0x00, 0x00, 0xff, 0xff }; + +void +clicked_cb (ClutterClickAction *action, + ClutterActor *actor, + gpointer user_data) +{ + g_debug ("Button %d clicked", clutter_click_action_get_button (action)); +} + +int +main (int argc, + char *argv[]) +{ + ClutterActor *stage; + ClutterAction *action1; + ClutterAction *action2; + ClutterActor *actor1; + ClutterActor *actor2; + + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_actor_set_size (stage, 400, 400); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); + + actor1 = clutter_rectangle_new_with_color (&red_color); + clutter_actor_set_size (actor1, 100, 100); + clutter_actor_set_reactive (actor1, TRUE); + clutter_actor_set_position (actor1, 50, 150); + + actor2 = clutter_rectangle_new_with_color (&blue_color); + clutter_actor_set_size (actor2, 100, 100); + clutter_actor_set_position (actor2, 250, 150); + clutter_actor_set_reactive (actor2, TRUE); + + action1 = clutter_click_action_new (); + clutter_actor_add_action (actor1, action1); + + action2 = clutter_click_action_new (); + clutter_actor_add_action (actor2, action2); + + clutter_container_add (CLUTTER_CONTAINER (stage), actor1, actor2, NULL); + + g_signal_connect (action1, + "clicked", + G_CALLBACK (clicked_cb), + NULL); + + g_signal_connect (action2, + "clicked", + G_CALLBACK (clicked_cb), + NULL); + + clutter_actor_show (stage); + + clutter_main (); + + return EXIT_SUCCESS; +} From 94439e55265d91e0c5419306115d2af02378e75e Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Tue, 28 Sep 2010 16:53:30 +0100 Subject: [PATCH 3/4] cookbook: Example of using button press and release events A longer example of using button press and release events to draw rectangles with random colors. --- doc/cookbook/examples/Makefile.am | 2 + doc/cookbook/examples/events-buttons-lasso.c | 154 +++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 doc/cookbook/examples/events-buttons-lasso.c diff --git a/doc/cookbook/examples/Makefile.am b/doc/cookbook/examples/Makefile.am index 4ee90ee42..cc58af0e7 100644 --- a/doc/cookbook/examples/Makefile.am +++ b/doc/cookbook/examples/Makefile.am @@ -30,6 +30,7 @@ noinst_PROGRAMS = \ script-signals \ events-buttons \ events-buttons-click \ + events-buttons-lasso \ $(NULL) INCLUDES = \ @@ -80,5 +81,6 @@ script_ui_SOURCES = script-ui.c script_signals_SOURCES = script-signals.c events_buttons_SOURCES = events-buttons.c events_buttons_click_SOURCES = events-buttons-click.c +events_buttons_lasso_SOURCES = events-buttons-lasso.c -include $(top_srcdir)/build/autotools/Makefile.am.gitignore diff --git a/doc/cookbook/examples/events-buttons-lasso.c b/doc/cookbook/examples/events-buttons-lasso.c new file mode 100644 index 000000000..68957250c --- /dev/null +++ b/doc/cookbook/examples/events-buttons-lasso.c @@ -0,0 +1,154 @@ +/* Simple rectangle drawing using button and pointer events; + * click, drag and release a mouse button to draw a rectangle + */ +#include +#include + +static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff }; +static const ClutterColor lasso_color = { 0xaa, 0xaa, 0xaa, 0x33 }; + +typedef struct +{ + ClutterActor *actor; + gfloat x; + gfloat y; +} Lasso; + +static guint +random_color_component () +{ + return (guint) (155 + (100.0 * rand () / (RAND_MAX + 1.0))); +} + +static gboolean +button_pressed_cb (ClutterActor *actor, + ClutterEvent *event, + gpointer user_data) +{ + Lasso *lasso = (Lasso *) user_data; + + /* start drawing the lasso actor */ + lasso->actor = clutter_rectangle_new_with_color (&lasso_color); + + /* store lasso's start coordinates */ + clutter_event_get_coords (event, &(lasso->x), &(lasso->y)); + + clutter_container_add_actor (CLUTTER_CONTAINER (actor), lasso->actor); + + return TRUE; +} + +static gboolean +button_released_cb (ClutterActor *stage, + ClutterEvent *event, + gpointer user_data) +{ + Lasso *lasso = (Lasso *) user_data; + ClutterActor *rectangle; + ClutterColor *random_color; + gfloat x; + gfloat y; + gfloat width; + gfloat height; + + if (lasso->actor == NULL) + return TRUE; + + /* create a new rectangle */ + random_color = clutter_color_new (random_color_component (), + random_color_component (), + random_color_component (), + random_color_component ()); + rectangle = clutter_rectangle_new_with_color (random_color); + + /* set the rectangle to the same size and shape as the lasso */ + clutter_actor_get_position (lasso->actor, &x, &y); + clutter_actor_get_size (lasso->actor, &width, &height); + + clutter_actor_set_position (rectangle, x, y); + clutter_actor_set_size (rectangle, width, height); + + clutter_container_add_actor (CLUTTER_CONTAINER (stage), rectangle); + + /* clear up the lasso actor */ + clutter_actor_destroy (lasso->actor); + lasso->actor = NULL; + + clutter_actor_queue_redraw (stage); + + return TRUE; + +} + +static gboolean +pointer_motion_cb (ClutterActor *stage, + ClutterEvent *event, + gpointer user_data) +{ + gfloat pointer_x; + gfloat pointer_y; + gfloat new_x; + gfloat new_y; + gfloat width; + gfloat height; + + Lasso *lasso = (Lasso *) user_data; + + if (lasso->actor == NULL) + return TRUE; + + /* redraw the lasso actor */ + clutter_event_get_coords (event, &pointer_x, &pointer_y); + + new_x = MIN (pointer_x, lasso->x); + new_y = MIN (pointer_y, lasso->y); + width = MAX (pointer_x, lasso->x) - new_x; + height = MAX (pointer_y, lasso->y) - new_y; + + clutter_actor_set_position (lasso->actor, new_x, new_y); + clutter_actor_set_size (lasso->actor, width, height); + + return TRUE; +} + +int +main (int argc, + char *argv[]) +{ + /* seed random number generator */ + srand ((unsigned int) time (NULL)); + + Lasso *lasso = g_new0 (Lasso, 1); + + ClutterActor *stage; + + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_actor_set_size (stage, 320, 240); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); + + g_signal_connect (stage, + "button-press-event", + G_CALLBACK (button_pressed_cb), + lasso); + + g_signal_connect (stage, + "button-release-event", + G_CALLBACK (button_released_cb), + lasso); + + g_signal_connect (stage, + "motion-event", + G_CALLBACK (pointer_motion_cb), + lasso); + + clutter_actor_show (stage); + + clutter_main (); + + g_free (lasso); + + return EXIT_SUCCESS; +} From cb191ff6f18e291810077271f853658c8d679e3d Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Thu, 30 Sep 2010 11:12:14 +0100 Subject: [PATCH 4/4] 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 + + + + +
+ +
+