[gnome-builder/wip/libide] libide: add scroll helpers that take scroll offset into account
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/libide] libide: add scroll helpers that take scroll offset into account
- Date: Tue, 10 Mar 2015 00:24:34 +0000 (UTC)
commit c531f683a9e1aa91dea7f3d1c962a8b489a6df1a
Author: Christian Hergert <christian hergert me>
Date: Mon Mar 9 17:16:50 2015 -0700
libide: add scroll helpers that take scroll offset into account
These helpers are similar to their GtkTextView counterparts except they
will take IdeSourceView:scroll-offset into account.
These do not currently animate to their new position (easily fixable with
ide_object_animate() if we decide to go that route). I think doing so is
actually really difficult to follow with code, so I haven't made the
switch yet. Further investigation necessary.
There is also one other pesky thing (only really noticeable if you have
--debug-scroll-offset enabled) that the forward motion insert mark can
go one line past what we actually want scroll offset to contain.
Probably just an off by one I'll figure out in a followup commit.
libide/ide-source-view.c | 222 ++++++++++++++++++++++++++++++++++++++++++++--
libide/ide-source-view.h | 17 ++++
2 files changed, 231 insertions(+), 8 deletions(-)
---
diff --git a/libide/ide-source-view.c b/libide/ide-source-view.c
index a2d6e32..22b314f 100644
--- a/libide/ide-source-view.c
+++ b/libide/ide-source-view.c
@@ -67,6 +67,16 @@
gtk_text_buffer_end_user_action(b); \
} G_STMT_END
+#define _GDK_RECTANGLE_X2(rect) ((rect)->x + (rect)->width)
+#define _GDK_RECTANGLE_Y2(rect) ((rect)->y + (rect)->height)
+#define _GDK_RECTANGLE_CONTAINS(rect,other) \
+ (((rect)->x <= (other)->x) && \
+ (_GDK_RECTANGLE_X2(rect) >= _GDK_RECTANGLE_X2(other)) && \
+ ((rect)->y <= (other)->y) && \
+ (_GDK_RECTANGLE_Y2(rect) >= _GDK_RECTANGLE_Y2(other)))
+#define _GDK_RECTANGLE_CENTER_X(rect) ((rect)->x + ((rect)->width/2))
+#define _GDK_RECTANGLE_CENTER_Y(rect) ((rect)->y + ((rect)->height/2))
+
typedef struct
{
IdeBackForwardList *back_forward_list;
@@ -96,7 +106,8 @@ typedef struct
guint count;
guint scroll_offset;
- guint cached_line_height;
+ gint cached_char_height;
+ gint cached_char_width;
guint saved_line;
guint saved_line_offset;
@@ -1679,8 +1690,6 @@ ide_source_view_real_style_updated (GtkWidget *widget)
IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
PangoContext *context;
PangoLayout *layout;
- int width;
- int height;
g_assert (IDE_IS_SOURCE_VIEW (self));
@@ -1689,8 +1698,7 @@ ide_source_view_real_style_updated (GtkWidget *widget)
context = gtk_widget_get_pango_context (widget);
layout = pango_layout_new (context);
pango_layout_set_text (layout, "X", 1);
- pango_layout_get_pixel_size (layout, &width, &height);
- priv->cached_line_height = height;
+ pango_layout_get_pixel_size (layout, &priv->cached_char_width, &priv->cached_char_height);
g_object_unref (layout);
}
@@ -3744,21 +3752,219 @@ ide_source_view_get_visible_rect (IdeSourceView *self,
* If we don't have valid line height, not much we can do now. We can just adjust things
* later once it becomes available.
*/
- if (priv->cached_line_height)
+ if (priv->cached_char_height)
{
gint max_scroll_offset;
gint scroll_offset;
gint visible_lines;
gint scroll_offset_height;
- visible_lines = area.height / priv->cached_line_height;
+ visible_lines = area.height / priv->cached_char_height;
max_scroll_offset = (visible_lines - 1) / 2;
scroll_offset = MIN (priv->scroll_offset, max_scroll_offset);
- scroll_offset_height = priv->cached_line_height * scroll_offset;
+ scroll_offset_height = priv->cached_char_height * scroll_offset;
area.y += scroll_offset_height;
area.height -= (2 * scroll_offset_height);
+
+ /*
+ * Use a multiple of the line height so we don't jump around when
+ * focusing the last line (due to Y2 not fitting in the visible area).
+ */
+ area.height = (area.height / priv->cached_char_height) * priv->cached_char_height;
}
*visible_rect = area;
}
+
+void
+ide_source_view_scroll_mark_onscreen (IdeSourceView *self,
+ GtkTextMark *mark)
+{
+ GtkTextView *text_view = (GtkTextView *)self;
+ GtkTextBuffer *buffer;
+ GdkRectangle visible_rect;
+ GdkRectangle mark_rect;
+ GtkTextIter iter;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ ide_source_view_get_visible_rect (self, &visible_rect);
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
+ gtk_text_view_get_iter_location (text_view, &iter, &mark_rect);
+
+ if (!_GDK_RECTANGLE_CONTAINS (&visible_rect, &mark_rect))
+ ide_source_view_scroll_to_mark (self, mark, 0.0, FALSE, 0.0, 0.0);
+
+ IDE_EXIT;
+}
+
+gboolean
+ide_source_view_move_mark_onscreen (IdeSourceView *self,
+ GtkTextMark *mark)
+{
+ GtkTextView *text_view = (GtkTextView *)self;
+ GtkTextBuffer *buffer;
+ GtkTextIter iter;
+ GtkTextIter end;
+ GdkRectangle visible_rect;
+ GdkRectangle iter_rect;
+
+ IDE_ENTRY;
+
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
+ g_return_val_if_fail (GTK_IS_TEXT_MARK (mark), FALSE);
+
+ buffer = gtk_text_view_get_buffer (text_view);
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+
+ ide_source_view_get_visible_rect (self, &visible_rect);
+ gtk_text_view_get_iter_location (text_view, &iter, &iter_rect);
+
+ if (_GDK_RECTANGLE_CONTAINS (&visible_rect, &iter_rect))
+ return FALSE;
+
+ if (_GDK_RECTANGLE_Y2 (&iter_rect) > _GDK_RECTANGLE_Y2 (&visible_rect))
+ gtk_text_view_get_iter_at_location (text_view, &iter,
+ _GDK_RECTANGLE_X2 (&visible_rect),
+ _GDK_RECTANGLE_Y2 (&visible_rect));
+ else if (iter_rect.y < visible_rect.y)
+ gtk_text_view_get_iter_at_location (text_view, &iter, visible_rect.x, visible_rect.y);
+ else
+ return gtk_text_view_move_mark_onscreen (text_view, mark);
+
+ gtk_text_buffer_move_mark (buffer, mark, &iter);
+
+ return TRUE;
+}
+
+void
+ide_source_view_scroll_to_iter (IdeSourceView *self,
+ const GtkTextIter *iter,
+ gdouble within_margin,
+ gboolean use_align,
+ gdouble xalign,
+ gdouble yalign)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextView *text_view = (GtkTextView *)self;
+ GtkAdjustment *hadj;
+ GtkAdjustment *vadj;
+ GdkRectangle real_visible_rect;
+ GdkRectangle visible_rect;
+ GdkRectangle iter_rect;
+ gdouble yvalue;
+ gdouble xvalue;
+ gint xoffset;
+ gint yoffset;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+ g_return_if_fail (iter);
+ g_return_if_fail (xalign >= 0.0);
+ g_return_if_fail (xalign <= 1.0);
+ g_return_if_fail (yalign >= 0.0);
+ g_return_if_fail (yalign <= 1.0);
+
+ gtk_text_view_get_visible_rect (text_view, &real_visible_rect);
+ ide_source_view_get_visible_rect (self, &visible_rect);
+
+ hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (self));
+ vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self));
+
+ gtk_text_view_get_iter_location (text_view, iter, &iter_rect);
+
+ /* leave a character of room to the right of the screen */
+ visible_rect.width -= priv->cached_char_width;
+
+ if (use_align == FALSE)
+ {
+ if (iter_rect.y < visible_rect.y)
+ yalign = 0.0;
+ else if (_GDK_RECTANGLE_Y2 (&iter_rect) > _GDK_RECTANGLE_Y2 (&visible_rect))
+ yalign = 1.0;
+ else
+ yalign = (iter_rect.y - visible_rect.y) / (gdouble)visible_rect.height;
+
+ if (iter_rect.x < visible_rect.x)
+ {
+ /* if we can get all the way to the line start, do so */
+ if (_GDK_RECTANGLE_X2 (&iter_rect) < visible_rect.width)
+ xalign = 1.0;
+ else
+ xalign = 0.0;
+ }
+ else if (_GDK_RECTANGLE_X2 (&iter_rect) > _GDK_RECTANGLE_X2 (&visible_rect))
+ xalign = 1.0;
+ else
+ xalign = (iter_rect.x - visible_rect.x) / (gdouble)visible_rect.width;
+ }
+
+ g_assert_cmpint (xalign, >=, 0.0);
+ g_assert_cmpint (yalign, >=, 0.0);
+ g_assert_cmpint (xalign, <=, 1.0);
+ g_assert_cmpint (yalign, <=, 1.0);
+
+ /* get the screen coordinates within the real visible area */
+ xoffset = (visible_rect.x - real_visible_rect.x) + (xalign * visible_rect.width);
+ yoffset = (visible_rect.y - real_visible_rect.y) + (yalign * visible_rect.height);
+
+ /*
+ * now convert those back to alignments in the real visible area, but leave
+ * enough space for an input character.
+ */
+ yalign = yoffset / (gdouble)real_visible_rect.height;
+ xalign = xoffset / (gdouble)real_visible_rect.width;
+
+ yvalue = iter_rect.y - (yalign * real_visible_rect.height);
+ xvalue = iter_rect.x - (xalign * real_visible_rect.width);
+
+ gtk_adjustment_set_value (hadj, xvalue);
+ gtk_adjustment_set_value (vadj, yvalue);
+
+ IDE_EXIT;
+}
+
+void
+ide_source_view_scroll_to_mark (IdeSourceView *self,
+ GtkTextMark *mark,
+ gdouble within_margin,
+ gboolean use_align,
+ gdouble xalign,
+ gdouble yalign)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter iter;
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+ g_return_if_fail (GTK_IS_TEXT_MARK (mark));
+ g_return_if_fail (xalign >= 0.0);
+ g_return_if_fail (xalign <= 1.0);
+ g_return_if_fail (yalign >= 0.0);
+ g_return_if_fail (yalign <= 1.0);
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
+ ide_source_view_scroll_to_iter (self, &iter, within_margin, use_align, xalign, yalign);
+}
+
+gboolean
+ide_source_view_place_cursor_onscreen (IdeSourceView *self)
+{
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ insert = gtk_text_buffer_get_insert (buffer);
+
+ return ide_source_view_move_mark_onscreen (self, insert);
+}
diff --git a/libide/ide-source-view.h b/libide/ide-source-view.h
index b132f0c..a1a9178 100644
--- a/libide/ide-source-view.h
+++ b/libide/ide-source-view.h
@@ -285,6 +285,23 @@ void ide_source_view_set_back_forward_list (IdeSource
IdeBackForwardList
*back_forward_list);
void ide_source_view_get_visible_rect (IdeSourceView *self,
GdkRectangle
*visible_rect);
+gboolean ide_source_view_move_mark_onscreen (IdeSourceView *self,
+ GtkTextMark *mark);
+gboolean ide_source_view_place_cursor_onscreen (IdeSourceView *self);
+void ide_source_view_scroll_mark_onscreen (IdeSourceView *self,
+ GtkTextMark *mark);
+void ide_source_view_scroll_to_mark (IdeSourceView *self,
+ GtkTextMark *mark,
+ gdouble
within_margin,
+ gboolean use_align,
+ gdouble xalign,
+ gdouble yalign);
+void ide_source_view_scroll_to_iter (IdeSourceView *self,
+ const GtkTextIter *iter,
+ gdouble
within_margin,
+ gboolean use_align,
+ gdouble xalign,
+ gdouble yalign);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]