diff --git a/src/sugar3/event-controller/sugar-long-press-controller.c b/src/sugar3/event-controller/sugar-long-press-controller.c index b68c1fb9..ad3f5a15 100644 --- a/src/sugar3/event-controller/sugar-long-press-controller.c +++ b/src/sugar3/event-controller/sugar-long-press-controller.c @@ -23,6 +23,8 @@ #define DEFAULT_THRESHOLD 32 #define DEFAULT_TIMEOUT 800 +#define ANIM_WINDOW_WIDTH 100 +#define ARC_LINE_WIDTH 10 typedef struct _SugarLongPressControllerPriv SugarLongPressControllerPriv; @@ -36,8 +38,13 @@ struct _SugarLongPressControllerPriv { GdkDevice *device; GdkEventSequence *sequence; + GtkWidget *anim_window; + gint64 start_time; gint x; gint y; + gint root_x; + gint root_y; + guint anim_id; guint timeout_id; guint threshold; guint timeout; @@ -60,6 +67,7 @@ sugar_long_press_controller_init (SugarLongPressController *controller) priv->threshold = DEFAULT_THRESHOLD; priv->timeout = DEFAULT_TIMEOUT; priv->x = priv->y = -1; + priv->root_x = priv->root_y = -1; } static void @@ -75,6 +83,7 @@ _sugar_long_press_controller_unset_device (SugarLongPressController *controller) priv->sequence = NULL; priv->x = priv->y = -1; + priv->root_x = priv->root_y = -1; priv->cancelled = priv->triggered = FALSE; } @@ -83,6 +92,16 @@ _sugar_long_press_controller_cancel (SugarLongPressController *controller) { SugarLongPressControllerPriv *priv = controller->_priv; + if (priv->anim_id) + { + g_source_remove (priv->anim_id); + priv->anim_id = 0; + } + + if (priv->anim_window && + gtk_widget_get_visible (priv->anim_window)) + gtk_widget_hide (priv->anim_window); + if (priv->timeout_id) { g_source_remove (priv->timeout_id); @@ -151,6 +170,139 @@ sugar_long_press_controller_finalize (GObject *object) G_OBJECT_CLASS (sugar_long_press_controller_parent_class)->finalize (object); } +static void +_stroke_progress_notification (cairo_t *cr, + gdouble progress) +{ + cairo_set_line_width (cr, ARC_LINE_WIDTH); + cairo_arc (cr, + ANIM_WINDOW_WIDTH / 2, + ANIM_WINDOW_WIDTH / 2, + ANIM_WINDOW_WIDTH / 2 - ARC_LINE_WIDTH / 2, + - G_PI_2, (2 * G_PI * progress) - G_PI_2); + cairo_stroke (cr); +} + +static gboolean +_sugar_long_press_anim_draw (GtkWidget *widget, + cairo_t *cr, + SugarLongPressController *controller) +{ + SugarLongPressControllerPriv *priv = controller->_priv; + gdouble progress; + gint64 diff_msec; + + diff_msec = (g_get_monotonic_time () - priv->start_time) / 1000; + progress = (gdouble) diff_msec / priv->timeout; + + cairo_save (cr); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba (cr, 0, 0, 0, 0); + cairo_paint (cr); + cairo_restore (cr); + + _stroke_progress_notification (cr, progress); + + return TRUE; +} + +static void +_sugar_long_press_controller_update_shape (GtkWidget *widget, + SugarLongPressController *controller) +{ + cairo_surface_t *surface; + cairo_region_t *region; + cairo_t *cr; + + surface = cairo_image_surface_create (CAIRO_FORMAT_A1, + ANIM_WINDOW_WIDTH, + ANIM_WINDOW_WIDTH); + cr = cairo_create (surface); + _stroke_progress_notification (cr, 1.0); + cairo_destroy (cr); + + region = gdk_cairo_region_create_from_surface (surface); + cairo_surface_destroy (surface); + + gtk_widget_shape_combine_region (widget, region); + cairo_region_destroy (region); +} + +static gboolean +_sugar_long_press_anim_timeout (gpointer user_data) +{ + SugarLongPressController *controller = user_data; + SugarLongPressControllerPriv *priv = controller->_priv; + GtkWidget *widget; + + g_object_get (controller, "widget", &widget, NULL); + + if (!widget) + { + priv->anim_id = 0; + return FALSE; + } + + if (priv->anim_window && + gtk_widget_get_screen (widget) != + gtk_widget_get_screen (priv->anim_window)) + { + gtk_widget_destroy (priv->anim_window); + priv->anim_window = NULL; + } + + if (!priv->anim_window) + { + GdkScreen *screen; + GdkVisual *rgba_visual; + + priv->anim_window = gtk_window_new (GTK_WINDOW_POPUP); + gtk_widget_set_app_paintable (priv->anim_window, TRUE); + gtk_widget_input_shape_combine_region (priv->anim_window, NULL); + gtk_window_set_type_hint (GTK_WINDOW (priv->anim_window), + GDK_WINDOW_TYPE_HINT_UTILITY); + + screen = gtk_widget_get_screen (widget); + rgba_visual = gdk_screen_get_rgba_visual (screen); + + gtk_window_set_screen (GTK_WINDOW (priv->anim_window), screen); + + if (rgba_visual) + { + GdkRGBA bg = { 0, 0, 0, 0 }; + + gtk_widget_set_visual (priv->anim_window, rgba_visual); + gtk_widget_override_background_color (priv->anim_window, 0, &bg); + } + else + _sugar_long_press_controller_update_shape (priv->anim_window, + controller); + g_signal_connect (priv->anim_window, "draw", + G_CALLBACK (_sugar_long_press_anim_draw), + controller); + } + + if (!gtk_widget_get_visible (priv->anim_window)) + { + gtk_window_move (GTK_WINDOW (priv->anim_window), + priv->root_x - (ANIM_WINDOW_WIDTH / 2), + priv->root_y - (ANIM_WINDOW_WIDTH / 2)); + gtk_window_resize (GTK_WINDOW (priv->anim_window), + ANIM_WINDOW_WIDTH, ANIM_WINDOW_WIDTH); + gtk_widget_show (priv->anim_window); + + priv->anim_id = + gdk_threads_add_timeout (20, _sugar_long_press_anim_timeout, + controller); + return FALSE; + } + else + { + gtk_widget_queue_draw (priv->anim_window); + return TRUE; + } +} + static gboolean _sugar_long_press_controller_timeout (gpointer user_data) { @@ -232,10 +384,17 @@ sugar_long_press_controller_handle_event (SugarEventController *controller, { case GDK_TOUCH_BEGIN: priv->device = g_object_ref (device); + priv->start_time = g_get_monotonic_time (); priv->x = event->touch.x; priv->y = event->touch.y; + priv->root_x = event->touch.x_root; + priv->root_y = event->touch.y_root; priv->sequence = sequence; + priv->anim_id = + gdk_threads_add_timeout (100, + _sugar_long_press_anim_timeout, + controller); priv->timeout_id = gdk_threads_add_timeout (priv->timeout, _sugar_long_press_controller_timeout,