[gnome-builder] hack: delay GtkTextView size-allocate during spurious animation



commit 9b2817f8af5dea48ef57372581fa5a60f5d9d1e0
Author: Christian Hergert <christian hergert me>
Date:   Mon Sep 14 01:29:28 2015 -0700

    hack: delay GtkTextView size-allocate during spurious animation
    
    This hack is to delay size-allocate in GtkTextView until after the
    animation sequence has completed. Currently, this hack only works for
    shrinking the animation. Such occurrence happens when the panels
    animate in.
    
    We don't really need to re-expose the content until after the sequence
    completes. This is potentially invalid for RTL or right-aligned text
    during the sequence, but it is not totally unexpected for that content
    to "snap" into place after a live resize. (See Safari on OS X for an
    example of how they do this with 'inLiveResize').
    
    It would be much better if we could set if we are in a live resize on
    a GdkWindow so that animation sequences and Drag'n'Resize could be
    propagated to widgets to allow them to "do less work" during the draw
    cycle. GtkTextView might choose to work extra hard to not drop it's
    pixel cache in this case.
    
    The result here, is that you can animate all three panels in smoothly
    now. (Animating out is still a big jagedy since we don't mitigate that
    yet). Some hardware, of course, may vary.

 libide/ide-source-view.c |   83 +++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 82 insertions(+), 1 deletions(-)
---
diff --git a/libide/ide-source-view.c b/libide/ide-source-view.c
index e369954..3445f53 100644
--- a/libide/ide-source-view.c
+++ b/libide/ide-source-view.c
@@ -136,6 +136,9 @@ typedef struct
 
   guint                        font_scale;
 
+  guint                        delay_size_allocate_chainup;
+  GtkAllocation                delay_size_allocation;
+
   guint                        auto_indent : 1;
   guint                        completion_blocked : 1;
   guint                        completion_visible : 1;
@@ -4830,6 +4833,77 @@ ide_source_view_replay_scroll (gpointer data)
   return G_SOURCE_REMOVE;
 }
 
+static gboolean
+ide_source_view_do_size_allocate_hack_cb (gpointer data)
+{
+  IdeSourceView *self = data;
+  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+  GtkAllocation alloc = priv->delay_size_allocation;
+
+  g_assert (IDE_IS_SOURCE_VIEW (self));
+
+  priv->delay_size_allocate_chainup = 0;
+
+  GTK_WIDGET_CLASS (ide_source_view_parent_class)->size_allocate (GTK_WIDGET (self), &alloc);
+
+  return G_SOURCE_REMOVE;
+}
+
+/*
+ * HACK:
+ *
+ * We really want the panels in Builder to be as smooth as possible when
+ * animating in and out of the scene. However, since these are not floating
+ * panels, we have the challenge of trying to go through the entire relayout,
+ * pixelcache, draw cycle many times per-second. Most systems are simply not
+ * up to the task.
+ *
+ * We can, however, take a shortcut when shrinking the allocation. We can
+ * simply defer the allocation request that would normally be chained up
+ * to GtkTextView and finish that work after the animation has completed.
+ * We use a simple heuristic to determine this, simply "missing" a size
+ * allocation from the typical frame clock cycle.
+ */
+static gboolean
+ide_source_view_do_size_allocate_hack (IdeSourceView *self,
+                                       GtkAllocation *allocation)
+{
+  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+  GtkWidget *widget = (GtkWidget *)self;
+  GtkAllocation old;
+
+  g_assert (IDE_IS_SOURCE_VIEW (self));
+  g_assert (allocation != NULL);
+
+  /*
+   * If we are shrinking the allocation, we can go forward with the hack.
+   * If not, we will abort our request and do the normal chainup cycle.
+   */
+  gtk_widget_get_allocation (widget, &old);
+  if ((old.width < allocation->width) || (old.height < allocation->height))
+    return FALSE;
+
+  /*
+   * Save the allocation for later. We'll need it to apply after our timeout
+   * which will occur just after the last frame (or sooner if we stall the
+   * drawing pipeline).
+   */
+  priv->delay_size_allocation = *allocation;
+
+  /*
+   * Register our timeout to occur just after a normal frame interval.
+   * If we are animating at 60 FPS, we should get another size-allocate within
+   * the frame cycle, typically 17 msec.
+   */
+  if (priv->delay_size_allocate_chainup)
+    g_source_remove (priv->delay_size_allocate_chainup);
+  priv->delay_size_allocate_chainup = g_timeout_add (30,
+                                                     ide_source_view_do_size_allocate_hack_cb,
+                                                     self);
+
+  return TRUE;
+}
+
 static void
 ide_source_view_size_allocate (GtkWidget     *widget,
                                GtkAllocation *allocation)
@@ -4841,7 +4915,8 @@ ide_source_view_size_allocate (GtkWidget     *widget,
   g_assert (IDE_IS_SOURCE_VIEW (self));
   g_assert (allocation != NULL);
 
-  GTK_WIDGET_CLASS (ide_source_view_parent_class)->size_allocate (widget, allocation);
+  if (!ide_source_view_do_size_allocate_hack (self, allocation))
+    GTK_WIDGET_CLASS (ide_source_view_parent_class)->size_allocate (GTK_WIDGET (self), allocation);
 
   /*
    * If we were in a scroll, and we got a size-allocate, we might need to adjust how far we
@@ -4964,6 +5039,12 @@ ide_source_view_dispose (GObject *object)
       priv->delayed_scroll_replay = 0;
     }
 
+  if (priv->delay_size_allocate_chainup)
+    {
+      g_source_remove (priv->delay_size_allocate_chainup);
+      priv->delay_size_allocate_chainup = 0;
+    }
+
   g_clear_object (&priv->capture);
   g_clear_object (&priv->indenter_adapter);
   g_clear_object (&priv->line_change_renderer);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]