6bdd276d05
Former-commit-id: fd56571888259555122d8a0f58c68838229cea2b
736 lines
20 KiB
Diff
736 lines
20 KiB
Diff
diff --git a/gdk/gdkevents.h b/gdk/gdkevents.h
|
|
index e6c516c..f3fa26a 100644
|
|
--- a/gdk/gdkevents.h
|
|
+++ b/gdk/gdkevents.h
|
|
@@ -65,6 +65,11 @@ typedef struct _GdkEventWindowState GdkEventWindowState;
|
|
typedef struct _GdkEventSetting GdkEventSetting;
|
|
typedef struct _GdkEventGrabBroken GdkEventGrabBroken;
|
|
|
|
+/* OS X specific gesture events */
|
|
+typedef struct _GdkEventGestureMagnify GdkEventGestureMagnify;
|
|
+typedef struct _GdkEventGestureRotate GdkEventGestureRotate;
|
|
+typedef struct _GdkEventGestureSwipe GdkEventGestureSwipe;
|
|
+
|
|
typedef union _GdkEvent GdkEvent;
|
|
|
|
typedef void (*GdkEventFunc) (GdkEvent *event,
|
|
@@ -152,6 +157,9 @@ typedef enum
|
|
GDK_OWNER_CHANGE = 34,
|
|
GDK_GRAB_BROKEN = 35,
|
|
GDK_DAMAGE = 36,
|
|
+ GDK_GESTURE_MAGNIFY = 37,
|
|
+ GDK_GESTURE_ROTATE = 38,
|
|
+ GDK_GESTURE_SWIPE = 39,
|
|
GDK_EVENT_LAST /* helper variable for decls */
|
|
} GdkEventType;
|
|
|
|
@@ -488,6 +496,52 @@ struct _GdkEventDND {
|
|
gshort x_root, y_root;
|
|
};
|
|
|
|
+/* Event types for OS X gestures */
|
|
+
|
|
+struct _GdkEventGestureMagnify
|
|
+{
|
|
+ GdkEventType type;
|
|
+ GdkWindow *window;
|
|
+ gint8 send_event;
|
|
+ guint32 time;
|
|
+ gdouble x;
|
|
+ gdouble y;
|
|
+ guint state;
|
|
+ gdouble magnification;
|
|
+ GdkDevice *device;
|
|
+ gdouble x_root, y_root;
|
|
+};
|
|
+
|
|
+struct _GdkEventGestureRotate
|
|
+{
|
|
+ GdkEventType type;
|
|
+ GdkWindow *window;
|
|
+ gint8 send_event;
|
|
+ guint32 time;
|
|
+ gdouble x;
|
|
+ gdouble y;
|
|
+ guint state;
|
|
+ gdouble rotation;
|
|
+ GdkDevice *device;
|
|
+ gdouble x_root, y_root;
|
|
+};
|
|
+
|
|
+struct _GdkEventGestureSwipe
|
|
+{
|
|
+ GdkEventType type;
|
|
+ GdkWindow *window;
|
|
+ gint8 send_event;
|
|
+ guint32 time;
|
|
+ gdouble x;
|
|
+ gdouble y;
|
|
+ guint state;
|
|
+ gdouble delta_x;
|
|
+ gdouble delta_y;
|
|
+ GdkDevice *device;
|
|
+ gdouble x_root, y_root;
|
|
+};
|
|
+
|
|
+
|
|
union _GdkEvent
|
|
{
|
|
GdkEventType type;
|
|
@@ -511,6 +565,9 @@ union _GdkEvent
|
|
GdkEventWindowState window_state;
|
|
GdkEventSetting setting;
|
|
GdkEventGrabBroken grab_broken;
|
|
+ GdkEventGestureMagnify magnify;
|
|
+ GdkEventGestureRotate rotate;
|
|
+ GdkEventGestureSwipe swipe;
|
|
};
|
|
|
|
GType gdk_event_get_type (void) G_GNUC_CONST;
|
|
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
|
|
index d20b424..fb89451 100644
|
|
--- a/gdk/gdkwindow.c
|
|
+++ b/gdk/gdkwindow.c
|
|
@@ -9813,6 +9813,9 @@ static const guint type_masks[] = {
|
|
0, /* GDK_OWNER_CHANGE = 34 */
|
|
0, /* GDK_GRAB_BROKEN = 35 */
|
|
0, /* GDK_DAMAGE = 36 */
|
|
+ 0, /* GDK_GESTURE_MAGNIFY = 37 */
|
|
+ 0, /* GDK_GESTURE_ROTATE = 38 */
|
|
+ 0, /* GDK_GESTURE_SWIPE = 39 */
|
|
};
|
|
G_STATIC_ASSERT (G_N_ELEMENTS (type_masks) == GDK_EVENT_LAST);
|
|
|
|
diff --git a/gdk/quartz/GdkQuartzView.c b/gdk/quartz/GdkQuartzView.c
|
|
index 2c897fb..cdae2f1 100644
|
|
--- a/gdk/quartz/GdkQuartzView.c
|
|
+++ b/gdk/quartz/GdkQuartzView.c
|
|
@@ -190,4 +190,37 @@
|
|
[self updateTrackingRect];
|
|
}
|
|
|
|
+/* Handle OS X gesture events. The Apple documentation is explicit
|
|
+ * that these events should not be captured through a tracking loop (which
|
|
+ * is how we handle usual event handling), so we fallback to overriding
|
|
+ * NSResponder methods.
|
|
+ */
|
|
+
|
|
+-(void)magnifyWithEvent:(NSEvent *)event
|
|
+{
|
|
+ GdkEvent *gdk_event;
|
|
+
|
|
+ gdk_event = _gdk_quartz_events_create_magnify_event (event);
|
|
+ if (gdk_event)
|
|
+ _gdk_event_queue_append (gdk_display_get_default (), gdk_event);
|
|
+}
|
|
+
|
|
+-(void)rotateWithEvent:(NSEvent *)event
|
|
+{
|
|
+ GdkEvent *gdk_event;
|
|
+
|
|
+ gdk_event = _gdk_quartz_events_create_rotate_event (event);
|
|
+ if (gdk_event)
|
|
+ _gdk_event_queue_append (gdk_display_get_default (), gdk_event);
|
|
+}
|
|
+
|
|
+-(void)swipeWithEvent:(NSEvent *)event
|
|
+{
|
|
+ GdkEvent *gdk_event;
|
|
+
|
|
+ gdk_event = _gdk_quartz_events_create_swipe_event (event);
|
|
+ if (gdk_event)
|
|
+ _gdk_event_queue_append (gdk_display_get_default (), gdk_event);
|
|
+}
|
|
+
|
|
@end
|
|
diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
|
|
index 9478c16..128c794 100644
|
|
--- a/gdk/quartz/gdkevents-quartz.c
|
|
+++ b/gdk/quartz/gdkevents-quartz.c
|
|
@@ -374,6 +374,11 @@ get_event_mask_from_ns_event (NSEvent *nsevent)
|
|
case NSMouseExited:
|
|
return GDK_LEAVE_NOTIFY_MASK;
|
|
|
|
+ case NSEventTypeMagnify:
|
|
+ case NSEventTypeRotate:
|
|
+ case NSEventTypeSwipe:
|
|
+ return 0;
|
|
+
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
@@ -752,6 +757,11 @@ find_window_for_ns_event (NSEvent *nsevent,
|
|
|
|
return toplevel;
|
|
|
|
+ case NSEventTypeMagnify:
|
|
+ case NSEventTypeRotate:
|
|
+ case NSEventTypeSwipe:
|
|
+ return toplevel;
|
|
+
|
|
default:
|
|
/* Ignore everything else. */
|
|
break;
|
|
@@ -1011,6 +1021,139 @@ fill_key_event (GdkWindow *window,
|
|
event->key.keyval));
|
|
}
|
|
|
|
+
|
|
+static GdkWindow *
|
|
+find_window_for_ns_gesture_event (NSEvent *nsevent,
|
|
+ gint *_x,
|
|
+ gint *_y,
|
|
+ gint *x_root,
|
|
+ gint *y_root)
|
|
+{
|
|
+ GdkDisplay *display = _gdk_display;
|
|
+ GdkWindow *toplevel_window = NULL, *pointer_window = NULL;
|
|
+ gdouble found_x, found_y;
|
|
+ gint x, y;
|
|
+
|
|
+ toplevel_window = find_window_for_ns_event (nsevent, &x, &y, x_root, y_root);
|
|
+ if (!toplevel_window)
|
|
+ return NULL;
|
|
+
|
|
+ if (toplevel_window == display->pointer_info.toplevel_under_pointer)
|
|
+ pointer_window = _gdk_window_find_descendant_at (toplevel_window,
|
|
+ x, y,
|
|
+ &found_x, &found_y);
|
|
+ else
|
|
+ pointer_window = NULL;
|
|
+
|
|
+ *_x = found_x;
|
|
+ *_y = found_y;
|
|
+
|
|
+ return pointer_window;
|
|
+}
|
|
+
|
|
+
|
|
+GdkEvent *
|
|
+_gdk_quartz_events_create_magnify_event (NSEvent *nsevent)
|
|
+{
|
|
+ GdkEvent *event;
|
|
+ GdkWindow *window = NULL;
|
|
+ gint x, y, x_root, y_root;
|
|
+
|
|
+ window = find_window_for_ns_gesture_event (nsevent,
|
|
+ &x, &y,
|
|
+ &x_root, &y_root);
|
|
+
|
|
+ if (!window)
|
|
+ return NULL;
|
|
+
|
|
+ event = gdk_event_new (GDK_GESTURE_MAGNIFY);
|
|
+
|
|
+ event->any.type = GDK_GESTURE_MAGNIFY;
|
|
+ event->magnify.window = window;
|
|
+ event->magnify.time = get_time_from_ns_event (nsevent);
|
|
+ event->magnify.x = x;
|
|
+ event->magnify.y = y;
|
|
+ event->magnify.x_root = x_root;
|
|
+ event->magnify.y_root = y_root;
|
|
+ event->magnify.magnification = [nsevent magnification];
|
|
+ event->magnify.state = get_keyboard_modifiers_from_ns_event (nsevent) |
|
|
+ _gdk_quartz_events_get_current_mouse_modifiers ();
|
|
+ event->magnify.device = _gdk_display->core_pointer;
|
|
+
|
|
+ fixup_event (event);
|
|
+
|
|
+ return event;
|
|
+}
|
|
+
|
|
+GdkEvent *
|
|
+_gdk_quartz_events_create_rotate_event (NSEvent *nsevent)
|
|
+{
|
|
+ GdkEvent *event;
|
|
+ GdkWindow *window;
|
|
+ gint x, y, x_root, y_root;
|
|
+
|
|
+ window = find_window_for_ns_gesture_event (nsevent,
|
|
+ &x, &y,
|
|
+ &x_root, &y_root);
|
|
+
|
|
+ if (!window)
|
|
+ return NULL;
|
|
+
|
|
+ event = gdk_event_new (GDK_GESTURE_ROTATE);
|
|
+
|
|
+ event->any.type = GDK_GESTURE_ROTATE;
|
|
+ event->rotate.window = window;
|
|
+ event->rotate.time = get_time_from_ns_event (nsevent);
|
|
+ event->rotate.x = x;
|
|
+ event->rotate.y = y;
|
|
+ event->rotate.x_root = x_root;
|
|
+ event->rotate.y_root = y_root;
|
|
+ event->rotate.rotation = [nsevent rotation];
|
|
+ event->rotate.state = get_keyboard_modifiers_from_ns_event (nsevent) |
|
|
+ _gdk_quartz_events_get_current_mouse_modifiers ();
|
|
+ event->rotate.device = _gdk_display->core_pointer;
|
|
+
|
|
+ fixup_event (event);
|
|
+
|
|
+ return event;
|
|
+}
|
|
+
|
|
+GdkEvent *
|
|
+_gdk_quartz_events_create_swipe_event (NSEvent *nsevent)
|
|
+{
|
|
+ GdkEvent *event;
|
|
+ GdkWindow *window;
|
|
+ gint x, y, x_root, y_root;
|
|
+
|
|
+ window = find_window_for_ns_gesture_event (nsevent,
|
|
+ &x, &y,
|
|
+ &x_root, &y_root);
|
|
+
|
|
+ if (!window)
|
|
+ return NULL;
|
|
+
|
|
+ event = gdk_event_new (GDK_GESTURE_SWIPE);
|
|
+
|
|
+ event->any.type = GDK_GESTURE_SWIPE;
|
|
+ event->swipe.window = window;
|
|
+ event->swipe.time = get_time_from_ns_event (nsevent);
|
|
+ event->swipe.x = x;
|
|
+ event->swipe.y = y;
|
|
+ event->swipe.x_root = x_root;
|
|
+ event->swipe.y_root = y_root;
|
|
+ event->swipe.delta_x = [nsevent deltaX];
|
|
+ event->swipe.delta_y = [nsevent deltaY];
|
|
+ event->swipe.state = get_keyboard_modifiers_from_ns_event (nsevent) |
|
|
+ _gdk_quartz_events_get_current_mouse_modifiers ();
|
|
+ event->swipe.device = _gdk_display->core_pointer;
|
|
+
|
|
+ fixup_event (event);
|
|
+
|
|
+ return event;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
static gboolean
|
|
synthesize_crossing_event (GdkWindow *window,
|
|
GdkEvent *event,
|
|
@@ -1392,6 +1535,13 @@ gdk_event_translate (GdkEvent *event,
|
|
}
|
|
break;
|
|
|
|
+ /* Gesture events are handled through GdkQuartzView, because the
|
|
+ * Apple documentation states very clearly that such events should
|
|
+ * not be handled through a tracking loop (like GDK uses).
|
|
+ * Experiments show that gesture events do not get through our
|
|
+ * tracking loop either.
|
|
+ */
|
|
+
|
|
default:
|
|
/* Ignore everything elsee. */
|
|
return_val = FALSE;
|
|
diff --git a/gdk/quartz/gdkglobals-quartz.c b/gdk/quartz/gdkglobals-quartz.c
|
|
index 53c6d5e..6d01b59 100644
|
|
--- a/gdk/quartz/gdkglobals-quartz.c
|
|
+++ b/gdk/quartz/gdkglobals-quartz.c
|
|
@@ -41,3 +41,9 @@ gdk_quartz_osx_version (void)
|
|
else
|
|
return minor;
|
|
}
|
|
+
|
|
+gboolean
|
|
+gdk_quartz_supports_gesture_events (void)
|
|
+{
|
|
+ return TRUE;
|
|
+}
|
|
diff --git a/gdk/quartz/gdkprivate-quartz.h b/gdk/quartz/gdkprivate-quartz.h
|
|
index c1b3083..03a3857 100644
|
|
--- a/gdk/quartz/gdkprivate-quartz.h
|
|
+++ b/gdk/quartz/gdkprivate-quartz.h
|
|
@@ -179,6 +179,10 @@ GdkModifierType _gdk_quartz_events_get_current_mouse_modifiers (void);
|
|
void _gdk_quartz_events_send_enter_notify_event (GdkWindow *window);
|
|
void _gdk_quartz_events_break_all_grabs (guint32 time);
|
|
|
|
+GdkEvent *_gdk_quartz_events_create_magnify_event (NSEvent *event);
|
|
+GdkEvent *_gdk_quartz_events_create_rotate_event (NSEvent *event);
|
|
+GdkEvent *_gdk_quartz_events_create_swipe_event (NSEvent *event);
|
|
+
|
|
/* Event loop */
|
|
gboolean _gdk_quartz_event_loop_check_pending (void);
|
|
NSEvent * _gdk_quartz_event_loop_get_pending (void);
|
|
diff --git a/gdk/quartz/gdkquartz.h b/gdk/quartz/gdkquartz.h
|
|
index 742d651..f79d04a 100644
|
|
--- a/gdk/quartz/gdkquartz.h
|
|
+++ b/gdk/quartz/gdkquartz.h
|
|
@@ -57,6 +57,7 @@ NSImage *gdk_quartz_pixbuf_to_ns_image_libgtk_only (GdkPixbuf
|
|
id gdk_quartz_drag_context_get_dragging_info_libgtk_only (GdkDragContext *context);
|
|
NSEvent *gdk_quartz_event_get_nsevent (GdkEvent *event);
|
|
GdkOSXVersion gdk_quartz_osx_version (void);
|
|
+gboolean gdk_quartz_supports_gesture_events (void);
|
|
|
|
G_END_DECLS
|
|
|
|
diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
|
|
index 5a88679..db77675 100644
|
|
--- a/gtk/gtkmain.c
|
|
+++ b/gtk/gtkmain.c
|
|
@@ -1636,6 +1636,9 @@ gtk_main_do_event (GdkEvent *event)
|
|
case GDK_WINDOW_STATE:
|
|
case GDK_GRAB_BROKEN:
|
|
case GDK_DAMAGE:
|
|
+ case GDK_GESTURE_MAGNIFY:
|
|
+ case GDK_GESTURE_ROTATE:
|
|
+ case GDK_GESTURE_SWIPE:
|
|
if (!_gtk_widget_captured_event (event_widget, event))
|
|
gtk_widget_event (event_widget, event);
|
|
break;
|
|
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
|
|
index 9a43d27..2d11e09 100644
|
|
--- a/gtk/gtkwidget.c
|
|
+++ b/gtk/gtkwidget.c
|
|
@@ -197,6 +197,9 @@ enum {
|
|
KEYNAV_FAILED,
|
|
DRAG_FAILED,
|
|
DAMAGE_EVENT,
|
|
+ GESTURE_MAGNIFY_EVENT,
|
|
+ GESTURE_ROTATE_EVENT,
|
|
+ GESTURE_SWIPE_EVENT,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
@@ -2242,6 +2245,70 @@ gtk_widget_class_init (GtkWidgetClass *klass)
|
|
_gtk_marshal_BOOLEAN__BOXED,
|
|
G_TYPE_BOOLEAN, 1,
|
|
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
+ /**
|
|
+ * GtkWidget::gesture-magnify-event:
|
|
+ * @widget: the object which received the signal
|
|
+ * @event: the #GdkEventGestureMagnify event
|
|
+ *
|
|
+ * Emitted when a magnify event is received on @widget's window.
|
|
+ *
|
|
+ * Returns: %TRUE to stop other handlers from being invoked for the event.
|
|
+ * %FALSE to propagate the event further.
|
|
+ *
|
|
+ * Since: 2.24 Xamarin specific.
|
|
+ */
|
|
+ widget_signals[GESTURE_MAGNIFY_EVENT] =
|
|
+ g_signal_new (I_("gesture-magnify-event"),
|
|
+ G_TYPE_FROM_CLASS (gobject_class),
|
|
+ G_SIGNAL_RUN_LAST,
|
|
+ 0,
|
|
+ _gtk_boolean_handled_accumulator, NULL,
|
|
+ _gtk_marshal_BOOLEAN__BOXED,
|
|
+ G_TYPE_BOOLEAN, 1,
|
|
+ GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
+ /**
|
|
+ * GtkWidget::gesture-rotate-event:
|
|
+ * @widget: the object which received the signal
|
|
+ * @event: the #GdkEventGestureRotate event
|
|
+ *
|
|
+ * Emitted when a rotation event is received on @widget's window.
|
|
+ *
|
|
+ * Returns: %TRUE to stop other handlers from being invoked for the event.
|
|
+ * %FALSE to propagate the event further.
|
|
+ *
|
|
+ * Since: 2.24 Xamarin specific.
|
|
+ */
|
|
+ widget_signals[GESTURE_ROTATE_EVENT] =
|
|
+ g_signal_new (I_("gesture-rotate-event"),
|
|
+ G_TYPE_FROM_CLASS (gobject_class),
|
|
+ G_SIGNAL_RUN_LAST,
|
|
+ 0,
|
|
+ _gtk_boolean_handled_accumulator, NULL,
|
|
+ _gtk_marshal_BOOLEAN__BOXED,
|
|
+ G_TYPE_BOOLEAN, 1,
|
|
+ GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
+ /**
|
|
+ * GtkWidget::gesture-swipe-event:
|
|
+ * @widget: the object which received the signal
|
|
+ * @event: the #GdkEventGestureSwipe event
|
|
+ *
|
|
+ * Emitted when a swipe event is received on @widget's window.
|
|
+ *
|
|
+ * Returns: %TRUE to stop other handlers from being invoked for the event.
|
|
+ * %FALSE to propagate the event further.
|
|
+ *
|
|
+ * Since: 2.24 Xamarin specific.
|
|
+ */
|
|
+ widget_signals[GESTURE_SWIPE_EVENT] =
|
|
+ g_signal_new (I_("gesture-swipe-event"),
|
|
+ G_TYPE_FROM_CLASS (gobject_class),
|
|
+ G_SIGNAL_RUN_LAST,
|
|
+ 0,
|
|
+ _gtk_boolean_handled_accumulator, NULL,
|
|
+ _gtk_marshal_BOOLEAN__BOXED,
|
|
+ G_TYPE_BOOLEAN, 1,
|
|
+ GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
+
|
|
/**
|
|
* GtkWidget::grab-broken-event:
|
|
* @widget: the object which received the signal
|
|
@@ -4975,6 +5042,15 @@ gtk_widget_event_internal (GtkWidget *widget,
|
|
case GDK_DAMAGE:
|
|
signal_num = DAMAGE_EVENT;
|
|
break;
|
|
+ case GDK_GESTURE_MAGNIFY:
|
|
+ signal_num = GESTURE_MAGNIFY_EVENT;
|
|
+ break;
|
|
+ case GDK_GESTURE_ROTATE:
|
|
+ signal_num = GESTURE_ROTATE_EVENT;
|
|
+ break;
|
|
+ case GDK_GESTURE_SWIPE:
|
|
+ signal_num = GESTURE_SWIPE_EVENT;
|
|
+ break;
|
|
default:
|
|
g_warning ("gtk_widget_event(): unhandled event type: %d", event->type);
|
|
signal_num = -1;
|
|
diff --git a/tests/Makefile.am b/tests/Makefile.am
|
|
index 3888826..68a0a79 100644
|
|
--- a/tests/Makefile.am
|
|
+++ b/tests/Makefile.am
|
|
@@ -88,7 +88,8 @@ noinst_PROGRAMS = $(TEST_PROGS) \
|
|
testactions \
|
|
testgrouping \
|
|
testtooltips \
|
|
- testvolumebutton
|
|
+ testvolumebutton \
|
|
+ testgestures
|
|
|
|
if HAVE_CXX
|
|
noinst_PROGRAMS += autotestkeywords
|
|
@@ -165,6 +166,7 @@ testgrouping_DEPENDENCIES = $(TEST_DEPS)
|
|
testtooltips_DEPENDENCIES = $(TEST_DEPS)
|
|
testvolumebutton_DEPENDENCIES = $(TEST_DEPS)
|
|
testwindows_DEPENDENCIES = $(TEST_DEPS)
|
|
+testgestures_DEPENDENCIES = $(TEST_DEPS)
|
|
|
|
testentrycompletion_SOURCES = \
|
|
prop-editor.c \
|
|
@@ -273,6 +275,9 @@ testoffscreenwindow_SOURCES = \
|
|
testwindows_SOURCES = \
|
|
testwindows.c
|
|
|
|
+testgestures_SOURCES = \
|
|
+ testgestures.c
|
|
+
|
|
EXTRA_DIST += \
|
|
prop-editor.h \
|
|
testgtk.1 \
|
|
diff --git a/tests/testgestures.c b/tests/testgestures.c
|
|
new file mode 100644
|
|
index 0000000..ae3ab99
|
|
--- /dev/null
|
|
+++ b/tests/testgestures.c
|
|
@@ -0,0 +1,214 @@
|
|
+#include <gtk/gtk.h>
|
|
+#include <math.h>
|
|
+
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ gdouble width;
|
|
+ gdouble height;
|
|
+
|
|
+ gdouble angle;
|
|
+
|
|
+ GtkWidget *widget;
|
|
+ gint offset_x;
|
|
+ gint offset_y;
|
|
+ gdouble progress;
|
|
+ gboolean increasing;
|
|
+}
|
|
+RectangleInfo;
|
|
+
|
|
+
|
|
+
|
|
+static gboolean
|
|
+handle_expose_event (GtkWidget *widget,
|
|
+ GdkEventExpose *expose,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ cairo_t *cr;
|
|
+ int center_x, center_y;
|
|
+ RectangleInfo *rect = (RectangleInfo *)user_data;
|
|
+
|
|
+ cr = gdk_cairo_create (widget->window);
|
|
+
|
|
+ cairo_save (cr);
|
|
+
|
|
+ /* Background */
|
|
+ cairo_rectangle (cr, 0, 0,
|
|
+ widget->allocation.width,
|
|
+ widget->allocation.height);
|
|
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
|
|
+ cairo_fill (cr);
|
|
+
|
|
+ cairo_restore (cr);
|
|
+
|
|
+ /* Rectangle */
|
|
+ center_x = (widget->allocation.width - rect->width) / 2;
|
|
+ center_y = (widget->allocation.height - rect->height) / 2;
|
|
+
|
|
+ if (rect->progress != 0.0f)
|
|
+ {
|
|
+ cairo_translate (cr, rect->offset_x * rect->progress,
|
|
+ rect->offset_y * rect->progress);
|
|
+ }
|
|
+
|
|
+ cairo_save (cr);
|
|
+
|
|
+ cairo_translate (cr, widget->allocation.width / 2,
|
|
+ widget->allocation.height / 2);
|
|
+ cairo_rotate (cr, rect->angle * M_PI / 180.0);
|
|
+ cairo_translate (cr, -widget->allocation.width / 2,
|
|
+ -widget->allocation.height / 2);
|
|
+
|
|
+
|
|
+ cairo_rectangle (cr,
|
|
+ center_x, center_y,
|
|
+ rect->width, rect->height);
|
|
+ cairo_set_source_rgb (cr, 0.9, 0.0, 0.0);
|
|
+ cairo_stroke (cr);
|
|
+
|
|
+ cairo_rectangle (cr,
|
|
+ center_x, center_y,
|
|
+ rect->width, rect->height);
|
|
+ cairo_set_source_rgba (cr, 0.9, 0.0, 0.0, 0.3);
|
|
+ cairo_fill (cr);
|
|
+
|
|
+ cairo_restore (cr);
|
|
+
|
|
+
|
|
+ cairo_destroy (cr);
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+handle_gesture_magnify_event (GtkWidget *widget,
|
|
+ GdkEventGestureMagnify *magnify,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ RectangleInfo *rect = (RectangleInfo *)user_data;
|
|
+
|
|
+ rect->width += rect->width * magnify->magnification;
|
|
+ if (rect->width < 5)
|
|
+ rect->width = 5;
|
|
+
|
|
+ rect->height += rect->height * magnify->magnification;
|
|
+ if (rect->height < 5)
|
|
+ rect->height = 5;
|
|
+
|
|
+ gtk_widget_queue_draw (widget);
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+handle_gesture_rotate_event (GtkWidget *widget,
|
|
+ GdkEventGestureRotate *rotate,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ RectangleInfo *rect = (RectangleInfo *)user_data;
|
|
+
|
|
+ rect->angle -= rotate->rotation;
|
|
+
|
|
+ gtk_widget_queue_draw (widget);
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+
|
|
+static gboolean
|
|
+bounce_timeout (gpointer user_data)
|
|
+{
|
|
+ gboolean retval = TRUE;
|
|
+ RectangleInfo *rect = (RectangleInfo *)user_data;
|
|
+
|
|
+ if (rect->increasing)
|
|
+ rect->progress += 0.10f;
|
|
+ else
|
|
+ rect->progress -= 0.10f;
|
|
+
|
|
+ if (rect->progress > 1.0f)
|
|
+ {
|
|
+ rect->progress = 0.90f;
|
|
+ rect->increasing = FALSE;
|
|
+ }
|
|
+ else if (rect->progress <= 0.0f)
|
|
+ {
|
|
+ rect->progress = 0.0f;
|
|
+ retval = FALSE;
|
|
+ }
|
|
+
|
|
+ gtk_widget_queue_draw (rect->widget);
|
|
+
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+static void
|
|
+bounce (RectangleInfo *rect,
|
|
+ int offset_x,
|
|
+ int offset_y)
|
|
+{
|
|
+ if (rect->progress != 0.0f)
|
|
+ return;
|
|
+
|
|
+ rect->progress = 0.10f;
|
|
+ rect->increasing = TRUE;
|
|
+ rect->offset_x = offset_x;
|
|
+ rect->offset_y = offset_y;
|
|
+ gtk_widget_queue_draw (rect->widget);
|
|
+
|
|
+ gdk_threads_add_timeout (25, bounce_timeout, rect);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+handle_gesture_swipe_event (GtkWidget *widget,
|
|
+ GdkEventGestureSwipe *swipe,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ int offset_x = 150, offset_y = 150;
|
|
+ RectangleInfo *rect = (RectangleInfo *)user_data;
|
|
+
|
|
+ offset_x *= -1.0 * swipe->delta_x;
|
|
+ offset_y *= -1.0 * swipe->delta_y;
|
|
+
|
|
+ bounce (rect, offset_x, offset_y);
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+int
|
|
+main (int argc, char **argv)
|
|
+{
|
|
+ GtkWidget *window;
|
|
+ GtkWidget *drawing_area;
|
|
+ RectangleInfo rect;
|
|
+
|
|
+ gtk_init (&argc, &argv);
|
|
+
|
|
+ rect.width = 40.0;
|
|
+ rect.height = 40.0;
|
|
+ rect.angle = 0.0;
|
|
+ rect.progress = 0.0f;
|
|
+
|
|
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
+ gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
|
|
+ g_signal_connect (window, "delete-event",
|
|
+ G_CALLBACK (gtk_main_quit), NULL);
|
|
+
|
|
+ drawing_area = gtk_drawing_area_new ();
|
|
+ rect.widget = drawing_area;
|
|
+ g_signal_connect (drawing_area, "expose-event",
|
|
+ G_CALLBACK (handle_expose_event), &rect);
|
|
+ g_signal_connect (drawing_area, "gesture-magnify-event",
|
|
+ G_CALLBACK (handle_gesture_magnify_event), &rect);
|
|
+ g_signal_connect (drawing_area, "gesture-rotate-event",
|
|
+ G_CALLBACK (handle_gesture_rotate_event), &rect);
|
|
+ g_signal_connect (drawing_area, "gesture-swipe-event",
|
|
+ G_CALLBACK (handle_gesture_swipe_event), &rect);
|
|
+ gtk_container_add (GTK_CONTAINER (window), drawing_area);
|
|
+
|
|
+ gtk_widget_show_all (window);
|
|
+
|
|
+ gtk_main ();
|
|
+
|
|
+ return 0;
|
|
+}
|