[gnome-builder/wip/chergert/three-finger-swipe] layout: stub out three-finger-swipe



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]