[gnome-builder/wip/chergert/three-finger-swipe] layout: stub out three-finger-swipe
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/chergert/three-finger-swipe] layout: stub out three-finger-swipe
- Date: Fri, 13 Oct 2017 10:55:50 +0000 (UTC)
commit 9670462244e31cb991a22f96837155c127d9a652
Author: Christian Hergert <chergert redhat com>
Date: Fri Oct 13 01:15:24 2017 -0700
layout: stub out three-finger-swipe
This starts on plumbing to use three-finger-swipe to move
IdeLayoutView left/right.
Still a bunch to do, and it's not totally correct yet, but I
want to commit this so I don't loose it.
src/libide/layout/ide-layout-stack.c | 284 ++++++++++++++++++++++++++++++---
src/libide/layout/ide-layout-stack.ui | 211 +++++++++++++------------
2 files changed, 371 insertions(+), 124 deletions(-)
---
diff --git a/src/libide/layout/ide-layout-stack.c b/src/libide/layout/ide-layout-stack.c
index 75b2193..de7857f 100644
--- a/src/libide/layout/ide-layout-stack.c
+++ b/src/libide/layout/ide-layout-stack.c
@@ -54,12 +54,26 @@ typedef struct
GPtrArray *in_transition;
PeasExtensionSet *addins;
+ /*
+ * Our gestures are used to do interactive moves when the user
+ * does a three finger swipe. We create the dummy gesture to
+ * ensure things work, because it for some reason does not without
+ * the dummy gesture set.
+ *
+ * https://bugzilla.gnome.org/show_bug.cgi?id=788914
+ */
+ GtkGesture *dummy;
+ GtkGesture *pan;
+ DzlBoxTheatric *pan_theatric;
+ IdeLayoutView *pan_view;
+
/* Template references */
DzlBox *empty_state;
DzlEmptyState *failed_state;
IdeLayoutStackHeader *header;
GtkStack *stack;
GtkStack *top_stack;
+ GtkEventBox *event_box;
} IdeLayoutStackPrivate;
typedef struct
@@ -82,7 +96,8 @@ enum {
N_SIGNALS
};
-static void list_model_iface_init (GListModelInterface *iface);
+static void list_model_iface_init (GListModelInterface *iface);
+static void animation_state_complete (gpointer data);
G_DEFINE_TYPE_WITH_CODE (IdeLayoutStack, ide_layout_stack, GTK_TYPE_BOX,
G_ADD_PRIVATE (IdeLayoutStack)
@@ -91,6 +106,13 @@ G_DEFINE_TYPE_WITH_CODE (IdeLayoutStack, ide_layout_stack, GTK_TYPE_BOX,
static GParamSpec *properties [N_PROPS];
static guint signals [N_SIGNALS];
+static inline gboolean
+is_uninitialized (GtkAllocation *alloc)
+{
+ return (alloc->x == -1 && alloc->y == -1 &&
+ alloc->width == 1 && alloc->height == 1);
+}
+
static void
ide_layout_stack_view_failed (IdeLayoutStack *self,
GParamSpec *pspec,
@@ -388,6 +410,191 @@ ide_layout_stack_addin_removed (PeasExtensionSet *set,
ide_layout_stack_addin_unload (addin, self);
}
+static gboolean
+ide_layout_stack_pan_begin (IdeLayoutStack *self,
+ GdkEventSequence *sequence,
+ GtkGesturePan *gesture)
+{
+ IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ GtkAllocation alloc;
+ cairo_surface_t *surface = NULL;
+ IdeLayoutView *view;
+ GdkWindow *window;
+ GtkWidget *grid;
+ cairo_t *cr;
+ gdouble x, y;
+ gboolean enable_animations;
+
+ g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (GTK_IS_GESTURE_PAN (gesture));
+ g_assert (priv->pan_theatric == NULL);
+
+ view = ide_layout_stack_get_visible_child (self);
+
+ gtk_widget_get_allocation (GTK_WIDGET (view), &alloc);
+
+ g_object_get (gtk_settings_get_default (),
+ "gtk-enable-animations", &enable_animations,
+ NULL);
+
+ if (sequence != NULL ||
+ view == NULL ||
+ !enable_animations ||
+ is_uninitialized (&alloc) ||
+ NULL == (window = gtk_widget_get_window (GTK_WIDGET (view))) ||
+ NULL == (surface = gdk_window_create_similar_surface (window,
+ CAIRO_CONTENT_COLOR,
+ alloc.width,
+ alloc.height)))
+ {
+ if (sequence != NULL)
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
+ return FALSE;
+ }
+
+ gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (gesture), &x, &y);
+
+ cr = cairo_create (surface);
+ gtk_widget_draw (GTK_WIDGET (view), cr);
+ cairo_destroy (cr);
+
+ grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_LAYOUT_GRID_COLUMN);
+ gtk_widget_translate_coordinates (GTK_WIDGET (priv->stack), grid, 0, 0,
+ &alloc.x, &alloc.y);
+
+ priv->pan_view = g_object_ref (view);
+ priv->pan_theatric = g_object_ref (g_object_new (DZL_TYPE_BOX_THEATRIC,
+ "surface", surface,
+ "height", alloc.height,
+ "target", grid,
+ "width", alloc.width,
+ "x", (gint)(alloc.x + x),
+ "y", alloc.y,
+ NULL));
+
+ g_clear_pointer (&surface, cairo_surface_destroy);
+
+ /* Hide the view while we begin the possible transition to another
+ * layout stack.
+ */
+ gtk_widget_hide (GTK_WIDGET (priv->pan_view));
+
+ return TRUE;
+}
+
+static void
+ide_layout_stack_pan_update (IdeLayoutStack *self,
+ GdkEventSequence *sequence,
+ GtkGestureSwipe *gesture)
+{
+ IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ GtkAllocation alloc;
+ gdouble x, y;
+
+ g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (GTK_IS_GESTURE_PAN (gesture));
+ g_assert (priv->pan_theatric != NULL);
+
+ gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (gesture), &x, &y);
+
+ gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+
+ g_object_set (priv->pan_theatric,
+ "x", (gint)(alloc.x + x),
+ NULL);
+}
+
+static void
+ide_layout_stack_pan_end (IdeLayoutStack *self,
+ GdkEventSequence *sequence,
+ GtkGesturePan *gesture)
+{
+ IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeLayoutStack *dest;
+ GtkAllocation alloc;
+ GtkWidget *grid;
+ GtkWidget *column;
+ gdouble x, y;
+ gint direction;
+ gint index = 0;
+
+ g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (GTK_IS_GESTURE_PAN (gesture));
+ g_assert (priv->pan_theatric != NULL);
+ g_assert (priv->pan_view != NULL);
+
+ gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+
+ gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (gesture), &x, &y);
+
+ if (x > (alloc.width / 3))
+ direction = 1;
+ else if (x < -(alloc.width / 3))
+ direction = -1;
+ else
+ direction = 0;
+
+ grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_LAYOUT_GRID);
+ g_assert (grid != NULL);
+ g_assert (IDE_IS_LAYOUT_GRID (grid));
+
+ column = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_LAYOUT_GRID_COLUMN);
+ g_assert (column != NULL);
+ g_assert (IDE_IS_LAYOUT_GRID_COLUMN (column));
+
+ gtk_container_child_get (GTK_CONTAINER (grid), GTK_WIDGET (column),
+ "index", &index,
+ NULL);
+
+ dest = _ide_layout_grid_get_nth_stack (IDE_LAYOUT_GRID (grid), index + direction);
+ g_assert (dest != NULL);
+ g_assert (IDE_IS_LAYOUT_STACK (dest));
+
+ gtk_widget_get_allocation (GTK_WIDGET (dest), &alloc);
+
+ if (!is_uninitialized (&alloc))
+ {
+ AnimationState *state;
+
+ state = g_slice_new0 (AnimationState);
+ state->source = g_object_ref (self);
+ state->dest = g_object_ref (dest);
+ state->view = g_object_ref (priv->pan_view);
+ state->theatric = priv->pan_theatric;
+
+ dzl_object_animate_full (state->theatric,
+ DZL_ANIMATION_EASE_IN_OUT_CUBIC,
+ TRANSITION_DURATION,
+ gtk_widget_get_frame_clock (GTK_WIDGET (self)),
+ animation_state_complete,
+ state,
+ "x", alloc.x,
+ "width", alloc.width,
+ NULL);
+
+ if (dest != self)
+ {
+ g_ptr_array_add (priv->in_transition, g_object_ref (priv->pan_view));
+ gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (priv->pan_view));
+ }
+ }
+ else
+ {
+ IdeLayoutStackPrivate *dest_priv = ide_layout_stack_get_instance_private (dest);
+
+ g_object_ref (priv->pan_view);
+ gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (priv->pan_view));
+ gtk_container_add (GTK_CONTAINER (dest_priv->stack), GTK_WIDGET (priv->pan_view));
+ gtk_widget_show (GTK_WIDGET (priv->pan_view));
+ g_object_unref (priv->pan_view);
+ }
+
+ g_clear_object (&priv->pan_theatric);
+ g_clear_object (&priv->pan_view);
+
+ gtk_widget_queue_draw (gtk_widget_get_toplevel (GTK_WIDGET (self)));
+}
+
static void
ide_layout_stack_constructed (GObject *object)
{
@@ -415,6 +622,37 @@ ide_layout_stack_constructed (GObject *object)
peas_extension_set_foreach (priv->addins,
ide_layout_stack_addin_added,
self);
+
+ gtk_widget_add_events (GTK_WIDGET (priv->event_box), GDK_TOUCH_MASK);
+ priv->pan = g_object_new (GTK_TYPE_GESTURE_PAN,
+ "widget", priv->event_box,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "n-points", 3,
+ NULL);
+ g_signal_connect_swapped (priv->pan,
+ "begin",
+ G_CALLBACK (ide_layout_stack_pan_begin),
+ self);
+ g_signal_connect_swapped (priv->pan,
+ "update",
+ G_CALLBACK (ide_layout_stack_pan_update),
+ self);
+ g_signal_connect_swapped (priv->pan,
+ "end",
+ G_CALLBACK (ide_layout_stack_pan_end),
+ self);
+ gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->pan),
+ GTK_PHASE_BUBBLE);
+
+ /*
+ * FIXME: Our priv->pan gesture does not activate unless we add another
+ * dummy gesture. I currently have no idea why that is.
+ *
+ * https://bugzilla.gnome.org/show_bug.cgi?id=788914
+ */
+ priv->dummy = gtk_gesture_rotate_new (GTK_WIDGET (priv->event_box));
+ gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->dummy),
+ GTK_PHASE_BUBBLE);
}
static void
@@ -463,6 +701,8 @@ ide_layout_stack_destroy (GtkWidget *widget)
g_clear_object (&priv->signals);
}
+ g_clear_object (&priv->pan);
+
GTK_WIDGET_CLASS (ide_layout_stack_parent_class)->destroy (widget);
}
@@ -557,6 +797,7 @@ ide_layout_stack_class_init (IdeLayoutStackClass *klass)
gtk_widget_class_bind_template_child_private (widget_class, IdeLayoutStack, header);
gtk_widget_class_bind_template_child_private (widget_class, IdeLayoutStack, stack);
gtk_widget_class_bind_template_child_private (widget_class, IdeLayoutStack, top_stack);
+ gtk_widget_class_bind_template_child_private (widget_class, IdeLayoutStack, event_box);
g_type_ensure (IDE_TYPE_LAYOUT_STACK_HEADER);
g_type_ensure (IDE_TYPE_SHORTCUT_LABEL);
@@ -854,25 +1095,33 @@ animation_state_complete (gpointer data)
g_assert (IDE_IS_LAYOUT_VIEW (state->view));
/* Add the widget to the new stack */
- gtk_container_add (GTK_CONTAINER (state->dest), GTK_WIDGET (state->view));
-
- /* Now remove it from our temporary transition. Be careful in case we were
- * destroyed in the mean time.
- */
- priv = ide_layout_stack_get_instance_private (state->source);
-
- if (priv->in_transition != NULL)
+ if (state->dest != state->source)
{
- guint position = 0;
+ gtk_container_add (GTK_CONTAINER (state->dest), GTK_WIDGET (state->view));
- if (g_ptr_array_find_with_equal_func (priv->views, state->view, NULL, &position))
+ /* Now remove it from our temporary transition. Be careful in case we were
+ * destroyed in the mean time.
+ */
+ priv = ide_layout_stack_get_instance_private (state->source);
+
+ if (priv->in_transition != NULL)
{
- g_ptr_array_remove (priv->in_transition, state->view);
- g_ptr_array_remove_index (priv->views, position);
- g_list_model_items_changed (G_LIST_MODEL (state->source), position, 1, 0);
+ guint position = 0;
+
+ if (g_ptr_array_find_with_equal_func (priv->views, state->view, NULL, &position))
+ {
+ g_ptr_array_remove (priv->in_transition, state->view);
+ g_ptr_array_remove_index (priv->views, position);
+ g_list_model_items_changed (G_LIST_MODEL (state->source), position, 1, 0);
+ }
}
}
+ /* We might need to reshow the widget in cases where we are in
+ * a three-finger-swipe of the view.
+ */
+ gtk_widget_show (GTK_WIDGET (state->view));
+
g_clear_object (&state->source);
g_clear_object (&state->dest);
g_clear_object (&state->view);
@@ -880,13 +1129,6 @@ animation_state_complete (gpointer data)
g_slice_free (AnimationState, state);
}
-static inline gboolean
-is_uninitialized (GtkAllocation *alloc)
-{
- return (alloc->x == -1 && alloc->y == -1 &&
- alloc->width == 1 && alloc->height == 1);
-}
-
void
_ide_layout_stack_transfer (IdeLayoutStack *self,
IdeLayoutStack *dest,
diff --git a/src/libide/layout/ide-layout-stack.ui b/src/libide/layout/ide-layout-stack.ui
index cf15da7..be87e44 100644
--- a/src/libide/layout/ide-layout-stack.ui
+++ b/src/libide/layout/ide-layout-stack.ui
@@ -10,125 +10,130 @@
</object>
</child>
<child>
- <object class="GtkStack" id="top_stack">
- <property name="expand">true</property>
- <property name="homogeneous">false</property>
- <property name="interpolate-size">false</property>
+ <object class="GtkEventBox" id="event_box">
<property name="visible">true</property>
<child>
- <object class="DzlBox" id="empty_state">
- <property name="expand">false</property>
- <property name="halign">center</property>
- <property name="orientation">vertical</property>
- <property name="valign">center</property>
- <property name="max-width-request">275</property>
- <property name="margin">32</property>
- <property name="spacing">6</property>
+ <object class="GtkStack" id="top_stack">
+ <property name="expand">true</property>
+ <property name="homogeneous">false</property>
+ <property name="interpolate-size">false</property>
<property name="visible">true</property>
<child>
- <object class="GtkLabel">
- <property name="label" translatable="yes">Open a File or Terminal</property>
- <property name="margin-bottom">6</property>
- <property name="visible">true</property>
- <style>
- <class name="dim-label"/>
- </style>
- <attributes>
- <attribute name="weight" value="bold"/>
- <attribute name="scale" value="1.2"/>
- </attributes>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="label" translatable="yes">Use the page switcher above or use one of the
following:</property>
- <property name="justify">center</property>
- <property name="margin-bottom">18</property>
- <property name="visible">true</property>
- <property name="wrap">true</property>
- <style>
- <class name="dim-label"/>
- </style>
- <attributes>
- <attribute name="scale" value=".909"/>
- </attributes>
- </object>
- </child>
- <child>
- <object class="IdeShortcutLabel">
- <property name="title" translatable="yes">Search</property>
- <property name="action">win.global-search</property>
- <property name="visible">true</property>
- <!-- Remove after auto accel tracking -->
- <property name="accel">Ctrl+.</property>
- </object>
- </child>
- <child>
- <object class="IdeShortcutLabel">
- <property name="title" translatable="yes">Project sidebar</property>
- <property name="action">editor.sidebar</property>
- <property name="visible">true</property>
- <!-- Remove after auto accel tracking -->
- <property name="accel">F9</property>
- </object>
- </child>
- <child>
- <object class="IdeShortcutLabel">
- <property name="title" translatable="yes">File chooser</property>
- <property name="action">editor.open-file</property>
- <property name="visible">true</property>
- <!-- Remove after auto accel tracking -->
- <property name="accel">Ctrl+O</property>
- </object>
- </child>
- <child>
- <object class="IdeShortcutLabel">
- <property name="title" translatable="yes">New terminal</property>
- <property name="action">win.new-terminal</property>
- <property name="visible">true</property>
- <!-- Remove after auto accel tracking -->
- <property name="accel">Ctrl+Shift+T</property>
- </object>
- </child>
- <child>
- <object class="GtkBox">
- <property name="homogeneous">true</property>
- <property name="margin-top">18</property>
+ <object class="DzlBox" id="empty_state">
+ <property name="expand">false</property>
+ <property name="halign">center</property>
+ <property name="orientation">vertical</property>
+ <property name="valign">center</property>
+ <property name="max-width-request">275</property>
+ <property name="margin">32</property>
<property name="spacing">6</property>
<property name="visible">true</property>
<child>
- <object class="GtkButton">
- <property name="action-name">editor.open-file</property>
- <property name="label" translatable="yes">Open File…</property>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Open a File or Terminal</property>
+ <property name="margin-bottom">6</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ <attribute name="scale" value="1.2"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Use the page switcher above or use one of the
following:</property>
+ <property name="justify">center</property>
+ <property name="margin-bottom">18</property>
+ <property name="visible">true</property>
+ <property name="wrap">true</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <attributes>
+ <attribute name="scale" value=".909"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="IdeShortcutLabel">
+ <property name="title" translatable="yes">Search</property>
+ <property name="action">win.global-search</property>
<property name="visible">true</property>
+ <!-- Remove after auto accel tracking -->
+ <property name="accel">Ctrl+.</property>
</object>
</child>
<child>
- <object class="GtkButton">
- <property name="action-name">win.new-terminal</property>
- <property name="label" translatable="yes">New Terminal</property>
+ <object class="IdeShortcutLabel">
+ <property name="title" translatable="yes">Project sidebar</property>
+ <property name="action">editor.sidebar</property>
<property name="visible">true</property>
+ <!-- Remove after auto accel tracking -->
+ <property name="accel">F9</property>
+ </object>
+ </child>
+ <child>
+ <object class="IdeShortcutLabel">
+ <property name="title" translatable="yes">File chooser</property>
+ <property name="action">editor.open-file</property>
+ <property name="visible">true</property>
+ <!-- Remove after auto accel tracking -->
+ <property name="accel">Ctrl+O</property>
+ </object>
+ </child>
+ <child>
+ <object class="IdeShortcutLabel">
+ <property name="title" translatable="yes">New terminal</property>
+ <property name="action">win.new-terminal</property>
+ <property name="visible">true</property>
+ <!-- Remove after auto accel tracking -->
+ <property name="accel">Ctrl+Shift+T</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="homogeneous">true</property>
+ <property name="margin-top">18</property>
+ <property name="spacing">6</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkButton">
+ <property name="action-name">editor.open-file</property>
+ <property name="label" translatable="yes">Open File…</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="action-name">win.new-terminal</property>
+ <property name="label" translatable="yes">New Terminal</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
</object>
</child>
</object>
</child>
- </object>
- </child>
- <child>
- <object class="DzlEmptyState" id="failed_state">
- <property name="icon-name">computer-fail-symbolic</property>
- <property name="pixel-size">160</property>
- <property name="title" translatable="yes">Uh oh, something went wrong</property>
- <property name="subtitle" translatable="yes">There was a failure while trying to perform the
operation.</property>
- <property name="visible">true</property>
- </object>
- </child>
- <child>
- <object class="GtkStack" id="stack">
- <property name="expand">true</property>
- <property name="homogeneous">false</property>
- <property name="interpolate-size">false</property>
- <property name="visible">true</property>
+ <child>
+ <object class="DzlEmptyState" id="failed_state">
+ <property name="icon-name">computer-fail-symbolic</property>
+ <property name="pixel-size">160</property>
+ <property name="title" translatable="yes">Uh oh, something went wrong</property>
+ <property name="subtitle" translatable="yes">There was a failure while trying to perform the
operation.</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStack" id="stack">
+ <property name="expand">true</property>
+ <property name="homogeneous">false</property>
+ <property name="interpolate-size">false</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
</object>
</child>
</object>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]