[gnome-builder] source-view: aggressively work around GtkTextView scrolling bugs
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] source-view: aggressively work around GtkTextView scrolling bugs
- Date: Sun, 10 May 2015 00:38:00 +0000 (UTC)
commit 89f92c6e6edba4bfce700002e567c5f2cb74953b
Author: Christian Hergert <christian hergert me>
Date: Sat May 9 17:10:27 2015 -0700
source-view: aggressively work around GtkTextView scrolling bugs
GtkTextView is pretty bad about keeping it's promise with scrolling.
Probably related to various animated scrolling work.
Since we do our own scrolling animations, we can work around this
pretty aggressively. Unfortunately, GtkTextView is so slow at building
line height information, that things are still a bit jittery.
However, we actually land on the right location now, which is better than
we were doing before.
Perhaps we should dig into GtkTextLayout and see if we can do some tricks
to appear to do the line height calculations faster. We might be able
to use a n_lines*line_height hack if GtkTextView:monospace is enabled.
libide/ide-source-view.c | 140 ++++++++++++++++++++++++++++++++++++++--------
1 files changed, 117 insertions(+), 23 deletions(-)
---
diff --git a/libide/ide-source-view.c b/libide/ide-source-view.c
index cf7d8d5..36c88e8 100644
--- a/libide/ide-source-view.c
+++ b/libide/ide-source-view.c
@@ -101,6 +101,8 @@ typedef struct
GQueue *snippets;
GtkSourceCompletionProvider *snippets_provider;
GtkSourceSearchContext *search_context;
+ IdeAnimation *hadj_animation;
+ IdeAnimation *vadj_animation;
EggBindingSet *file_setting_bindings;
EggSignalGroup *buffer_signals;
@@ -133,6 +135,7 @@ typedef struct
guint overwrite_braces : 1;
guint recording_macro : 1;
guint rubberband_search : 1;
+ guint scrolling_to_scroll_mark : 1;
guint show_grid_lines : 1;
guint show_line_changes : 1;
guint show_line_diagnostics : 1;
@@ -1285,17 +1288,8 @@ ide_source_view__buffer_loaded_cb (IdeSourceView *self,
priv->completion_blocked = FALSE;
}
- /*
- * FIXME:
- *
- * This will not always scroll to the position. It used to, because textview worked hard to scroll
- * to the target location (when using scroll_to_mark). I think during the animation work
- * everything broke. We need to do our own scroll_to_mark that will work around this.
- *
- * But I'm busy, so yeah. Sometime.
- */
insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
- gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (self), insert, 0.0, TRUE, 1.0, 0.5);
+ ide_source_view_scroll_to_mark (self, insert, 0.0, TRUE, 1.0, 0.5, TRUE);
/*
* Store the line offset so movements are correct.
@@ -1350,7 +1344,7 @@ ide_source_view_bind_buffer (IdeSourceView *self,
g_clear_object (&search_settings);
- /* Create scroll mark used by movements */
+ /* Create scroll mark used by movements and our scrolling helper */
gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &iter);
priv->scroll_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (buffer), NULL, &iter, TRUE);
@@ -4739,6 +4733,32 @@ ide_source_view_set_indent_style (IdeSourceView *self,
}
static void
+ide_source_view_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ IdeSourceView *self = (IdeSourceView *)widget;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ IDE_ENTRY;
+
+ g_assert (GTK_IS_WIDGET (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 we were in a scroll, and we got a size-allocate, we might need to adjust how far we
+ * are scrolling. This could happen while the view is calculating text layout sizes and
+ * has not yet reached our target location.
+ */
+ if (priv->scrolling_to_scroll_mark)
+ ide_source_view_scroll_mark_onscreen (self, priv->scroll_mark);
+
+ IDE_EXIT;
+}
+
+static void
ide_source_view_dispose (GObject *object)
{
IdeSourceView *self = (IdeSourceView *)object;
@@ -4756,6 +4776,9 @@ ide_source_view_dispose (GObject *object)
g_clear_object (&priv->buffer_signals);
g_clear_object (&priv->file_setting_bindings);
+ ide_clear_weak_pointer (&priv->hadj_animation);
+ ide_clear_weak_pointer (&priv->vadj_animation);
+
G_OBJECT_CLASS (ide_source_view_parent_class)->dispose (object);
}
@@ -4989,6 +5012,7 @@ ide_source_view_class_init (IdeSourceViewClass *klass)
widget_class->focus_out_event = ide_source_view_focus_out_event;
widget_class->key_press_event = ide_source_view_key_press_event;
widget_class->query_tooltip = ide_source_view_query_tooltip;
+ widget_class->size_allocate = ide_source_view_size_allocate;
widget_class->style_updated = ide_source_view_real_style_updated;
text_view_class->draw_layer = ide_source_view_real_draw_layer;
@@ -6398,6 +6422,48 @@ ide_source_view_move_mark_onscreen (IdeSourceView *self,
return TRUE;
}
+static gboolean
+ide_source_view_mark_is_onscreen (IdeSourceView *self,
+ GtkTextMark *mark)
+{
+ GtkTextBuffer *buffer;
+ GdkRectangle visible_rect;
+ GdkRectangle mark_rect;
+ GtkTextIter iter;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (GTK_IS_TEXT_MARK (mark));
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
+
+ ide_source_view_get_visible_rect (self, &visible_rect);
+ gtk_text_view_get_iter_location (GTK_TEXT_VIEW (self), &iter, &mark_rect);
+
+ return (_GDK_RECTANGLE_CONTAINS (&visible_rect, &mark_rect));
+}
+
+static void
+ide_source_view__vadj_animation_completed (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ /*
+ * If the mark we were scrolling to is not yet on screen, then just wait for another size
+ * allocate so that we can continue making progress.
+ */
+ if (!ide_source_view_mark_is_onscreen (self, priv->scroll_mark))
+ IDE_EXIT;
+
+ priv->scrolling_to_scroll_mark = FALSE;
+
+ IDE_EXIT;
+}
+
void
ide_source_view_scroll_to_iter (IdeSourceView *self,
const GtkTextIter *iter,
@@ -6409,6 +6475,7 @@ ide_source_view_scroll_to_iter (IdeSourceView *self,
{
IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
GtkTextView *text_view = (GtkTextView *)self;
+ GtkTextBuffer *buffer;
GtkAdjustment *hadj;
GtkAdjustment *vadj;
GdkFrameClock *frame_clock;
@@ -6430,6 +6497,10 @@ ide_source_view_scroll_to_iter (IdeSourceView *self,
g_return_if_fail (yalign >= 0.0);
g_return_if_fail (yalign <= 1.0);
+ buffer = gtk_text_view_get_buffer (text_view);
+
+ gtk_text_buffer_move_mark (buffer, priv->scroll_mark, iter);
+
gtk_text_view_get_visible_rect (text_view, &real_visible_rect);
ide_source_view_get_visible_rect (self, &visible_rect);
@@ -6528,18 +6599,41 @@ ide_source_view_scroll_to_iter (IdeSourceView *self,
else if (difference <= page_size)
duration_msec = SMALL_SCROLL_DURATION_MSEC;
- ide_object_animate (hadj,
- IDE_ANIMATION_EASE_OUT_CUBIC,
- duration_msec,
- frame_clock,
- "value", xvalue,
- NULL);
- ide_object_animate (vadj,
- IDE_ANIMATION_EASE_OUT_CUBIC,
- duration_msec,
- frame_clock,
- "value", yvalue,
- NULL);
+ priv->scrolling_to_scroll_mark = TRUE;
+
+ if (priv->hadj_animation != NULL)
+ {
+ ide_animation_stop (priv->hadj_animation);
+ ide_clear_weak_pointer (&priv->hadj_animation);
+ }
+
+ priv->hadj_animation =
+ ide_object_animate (hadj,
+ IDE_ANIMATION_EASE_OUT_CUBIC,
+ duration_msec,
+ frame_clock,
+ "value", xvalue,
+ NULL);
+ g_object_add_weak_pointer (G_OBJECT (priv->hadj_animation),
+ (gpointer *)&priv->hadj_animation);
+
+ if (priv->vadj_animation != NULL)
+ {
+ ide_animation_stop (priv->vadj_animation);
+ ide_clear_weak_pointer (&priv->vadj_animation);
+ }
+
+ priv->vadj_animation =
+ ide_object_animate_full (vadj,
+ IDE_ANIMATION_EASE_OUT_CUBIC,
+ duration_msec,
+ frame_clock,
+ (GDestroyNotify)ide_source_view__vadj_animation_completed,
+ self,
+ "value", yvalue,
+ NULL);
+ g_object_add_weak_pointer (G_OBJECT (priv->vadj_animation),
+ (gpointer *)&priv->vadj_animation);
}
else
{
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]