[gtk/wip/chergert/textview-widgets: 15/15] textview: use GtkTextViewChild for border and overlay children



commit c78e6c6dd114ffc48a1af381b2d431027dcedaa2
Author: Christian Hergert <chergert redhat com>
Date:   Thu Oct 3 19:21:45 2019 -0700

    textview: use GtkTextViewChild for border and overlay children
    
    This creates a new GtkTextViewChild that can manage overlay children at
    given x,y offsets in buffer coordinates. This simplifies GtkTextView by
    extracting this from GtkTextWindow as well as providing a real widget for
    the borders.
    
    With this change, we also rename gtk_text_view_add_child_in_window() to
    gtk_text_view_add_overlay(). For those that were using
    GTK_TEXT_WINDOW_WIDGET, they can use a GtkOverlay. It does not appear
    that anyone was using GTK_TEXT_WINDOW_(LEFT|RIGHT|TOP|BOTTOM) for widgets
    in this fashion, but that can be done by setting a gutter widget with
    gtk_text_view_set_gutter(). We can make GtkTextViewChild public if
    necessary to simplify this should it become necessary.
    
    GtkTextViewChild will setup a CSS node of either "text" or "border"
    depending on the GtkTextWindowType.
    
    The old GtkTextViewChild has been renamed to AnchoredChild as it is only
    used for widgets with anchors in the GtkTextBuffer. This also removes the
    use of allocated GSList and instead embeds a GQueue and GList to save a
    few extraneous allocations.

 docs/reference/gtk/gtk4-sections.txt         |    6 +-
 gtk/gtktextutil.c                            |   17 +-
 gtk/gtktextview.c                            | 1183 ++++++++++----------------
 gtk/gtktextview.h                            |   44 +-
 gtk/gtktextviewchild.c                       |  517 +++++++++++
 gtk/gtktextviewchild.h                       |   48 ++
 gtk/gtktextviewchildprivate.h                |   27 +
 gtk/meson.build                              |    1 +
 tests/testtextview.c                         |    7 +-
 testsuite/reftests/textview-border-windows.c |   19 +-
 10 files changed, 1122 insertions(+), 747 deletions(-)
---
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index cfd069216b..87948ae035 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -3085,8 +3085,10 @@ GtkTextChildAnchor
 gtk_text_child_anchor_new
 gtk_text_child_anchor_get_widgets
 gtk_text_child_anchor_get_deleted
-gtk_text_view_add_child_in_window
-gtk_text_view_move_child
+gtk_text_view_get_gutter
+gtk_text_view_set_gutter
+gtk_text_view_add_overlay
+gtk_text_view_move_overlay
 gtk_text_view_set_wrap_mode
 gtk_text_view_get_wrap_mode
 gtk_text_view_set_editable
diff --git a/gtk/gtktextutil.c b/gtk/gtktextutil.c
index 200f87285c..fa10328d46 100644
--- a/gtk/gtktextutil.c
+++ b/gtk/gtktextutil.c
@@ -158,6 +158,19 @@ set_attributes_from_style (GtkStyleContext   *context,
   gtk_style_context_get (context, "font", &values->font, NULL);
 }
 
+static gint
+get_border_window_size (GtkTextView       *text_view,
+                        GtkTextWindowType  window_type)
+{
+  GtkWidget *gutter;
+
+  gutter = gtk_text_view_get_gutter (text_view, window_type);
+  if (gutter != NULL)
+    return gtk_widget_get_width (gutter);
+
+  return 0;
+}
+
 GdkPaintable *
 gtk_text_util_create_rich_drag_icon (GtkWidget     *widget,
                                      GtkTextBuffer *buffer,
@@ -208,8 +221,8 @@ gtk_text_util_create_rich_drag_icon (GtkWidget     *widget,
   if (GTK_IS_TEXT_VIEW (widget))
     {
       layout_width = layout_width
-        - gtk_text_view_get_border_window_size (GTK_TEXT_VIEW (widget), GTK_TEXT_WINDOW_LEFT)
-        - gtk_text_view_get_border_window_size (GTK_TEXT_VIEW (widget), GTK_TEXT_WINDOW_RIGHT);
+        - get_border_window_size (GTK_TEXT_VIEW (widget), GTK_TEXT_WINDOW_LEFT)
+        - get_border_window_size (GTK_TEXT_VIEW (widget), GTK_TEXT_WINDOW_RIGHT);
     }
 
   style->direction = gtk_widget_get_direction (widget);
diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c
index 38e7e10356..cf735cc88b 100644
--- a/gtk/gtktextview.c
+++ b/gtk/gtktextview.c
@@ -47,6 +47,7 @@
 #include "gtkwindow.h"
 #include "gtkscrollable.h"
 #include "gtktypebuiltins.h"
+#include "gtktextviewchildprivate.h"
 #include "gtktexthandleprivate.h"
 #include "gtkcssstylepropertyprivate.h"
 #include "gtkpopover.h"
@@ -170,10 +171,14 @@ struct _GtkTextViewPrivate
 
   GtkBorder border_window_size;
   GtkTextWindow *text_window;
-  GtkTextWindow *left_window;
-  GtkTextWindow *right_window;
-  GtkTextWindow *top_window;
-  GtkTextWindow *bottom_window;
+
+  GQueue anchored_children;
+
+  GtkTextViewChild *left_child;
+  GtkTextViewChild *right_child;
+  GtkTextViewChild *top_child;
+  GtkTextViewChild *bottom_child;
+  GtkTextViewChild *center_child;
 
   GtkAdjustment *hadjustment;
   GtkAdjustment *vadjustment;
@@ -222,8 +227,6 @@ struct _GtkTextViewPrivate
   GtkWidget *popup_menu;
   GMenuModel *extra_menu;
 
-  GSList *children;
-
   GtkTextPendingScroll *pending_scroll;
 
   GtkGesture *drag_gesture;
@@ -562,6 +565,7 @@ static void gtk_text_view_remove (GtkContainer *container,
 static void gtk_text_view_forall (GtkContainer *container,
                                   GtkCallback   callback,
                                   gpointer      callback_data);
+static void update_node_ordering (GtkWidget    *widget);
 
 /* GtkTextHandle handlers */
 static void gtk_text_view_handle_drag_started  (GtkTextHandle         *handle,
@@ -618,31 +622,19 @@ static void gtk_text_view_activate_misc_insert_emoji    (GtkWidget  *widget,
 
 /* FIXME probably need the focus methods. */
 
-typedef struct _GtkTextViewChild GtkTextViewChild;
-
-struct _GtkTextViewChild
+typedef struct
 {
-  GtkWidget *widget;
-
+  GList               link;
+  GtkWidget          *widget;
   GtkTextChildAnchor *anchor;
+  int                 from_top_of_line;
+  int                 from_left_of_buffer;
+} AnchoredChild;
 
-  gint from_top_of_line;
-  gint from_left_of_buffer;
-  
-  /* These are ignored if anchor != NULL */
-  GtkTextWindowType type;
-  gint x;
-  gint y;
-};
-
-static GtkTextViewChild* text_view_child_new_anchored      (GtkWidget          *child,
-                                                           GtkTextChildAnchor *anchor,
-                                                           GtkTextLayout      *layout);
-static GtkTextViewChild* text_view_child_new_window        (GtkWidget          *child,
-                                                           GtkTextWindowType   type,
-                                                           gint                x,
-                                                           gint                y);
-static void              text_view_child_free              (GtkTextViewChild   *child);
+static AnchoredChild *anchored_child_new  (GtkWidget          *child,
+                                           GtkTextChildAnchor *anchor,
+                                           GtkTextLayout      *layout);
+static void           anchored_child_free (AnchoredChild      *child);
 
 struct _GtkTextWindow
 {
@@ -652,15 +644,10 @@ struct _GtkTextWindow
   GdkRectangle allocation;
 };
 
-static GtkTextWindow *text_window_new             (GtkTextWindowType  type,
-                                                   GtkWidget         *widget);
+static GtkTextWindow *text_window_new             (GtkWidget         *widget);
 static void           text_window_free            (GtkTextWindow     *win);
 static void           text_window_size_allocate   (GtkTextWindow     *win,
                                                    GdkRectangle      *rect);
-static void           text_window_invalidate      (GtkTextWindow     *win);
-
-static gint           text_window_get_x           (GtkTextWindow     *win);
-static gint           text_window_get_y           (GtkTextWindow     *win);
 static gint           text_window_get_width       (GtkTextWindow     *win);
 static gint           text_window_get_height      (GtkTextWindow     *win);
 
@@ -669,7 +656,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
 
 G_DEFINE_TYPE_WITH_CODE (GtkTextView, gtk_text_view, GTK_TYPE_CONTAINER,
                          G_ADD_PRIVATE (GtkTextView)
-                        G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
 
 static void
 add_move_binding (GtkBindingSet  *binding_set,
@@ -1676,7 +1663,7 @@ gtk_text_view_init (GtkTextView *text_view)
 
   priv->accepts_tab = TRUE;
 
-  priv->text_window = text_window_new (GTK_TEXT_WINDOW_TEXT, widget);
+  priv->text_window = text_window_new (widget);
 
   gesture = gtk_gesture_click_new ();
   gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 0);
@@ -1840,29 +1827,16 @@ gtk_text_view_set_buffer (GtkTextView   *text_view,
     return;
 
   old_buffer = priv->buffer;
-  if (priv->buffer != NULL)
-    {
-      /* Destroy all anchored children */
-      GSList *tmp_list;
-      GSList *copy;
 
-      copy = g_slist_copy (priv->children);
-      tmp_list = copy;
-      while (tmp_list != NULL)
+  if (old_buffer != NULL)
+    {
+      while (priv->anchored_children.length)
         {
-          GtkTextViewChild *vc = tmp_list->data;
-
-          if (vc->anchor)
-            {
-              gtk_widget_destroy (vc->widget);
-              /* vc may now be invalid! */
-            }
-
-          tmp_list = tmp_list->next;
+          AnchoredChild *ac = g_queue_peek_head (&priv->anchored_children);
+          gtk_widget_destroy (ac->widget);
+          /* ac is now invalid! */
         }
 
-      g_slist_free (copy);
-
       g_signal_handlers_disconnect_by_func (priv->buffer,
                                            gtk_text_view_mark_set_handler,
                                            text_view);
@@ -3607,6 +3581,14 @@ gtk_text_view_finalize (GObject *object)
 
   /* at this point, no "notify::buffer" handler should recreate the buffer. */
   g_assert (priv->buffer == NULL);
+
+  /* Ensure all children were removed */
+  g_assert (priv->anchored_children.length == 0);
+  g_assert (priv->left_child == NULL);
+  g_assert (priv->right_child == NULL);
+  g_assert (priv->top_child == NULL);
+  g_assert (priv->bottom_child == NULL);
+  g_assert (priv->center_child == NULL);
   
   cancel_pending_scroll (text_view);
 
@@ -3620,18 +3602,6 @@ gtk_text_view_finalize (GObject *object)
 
   text_window_free (priv->text_window);
 
-  if (priv->left_window)
-    text_window_free (priv->left_window);
-
-  if (priv->top_window)
-    text_window_free (priv->top_window);
-
-  if (priv->right_window)
-    text_window_free (priv->right_window);
-
-  if (priv->bottom_window)
-    text_window_free (priv->bottom_window);
-
   g_clear_pointer (&priv->selection_bubble, gtk_widget_unparent);
 
   if (priv->magnifier_popover)
@@ -3901,6 +3871,42 @@ gtk_text_view_get_property (GObject         *object,
     }
 }
 
+static void
+gtk_text_view_measure_borders (GtkTextView *text_view,
+                               GtkBorder   *border)
+{
+  GtkTextViewPrivate *priv = text_view->priv;
+  int left = 0;
+  int right = 0;
+  int top = 0;
+  int bottom = 0;
+
+  if (priv->left_child)
+    gtk_widget_measure (GTK_WIDGET (priv->left_child),
+                        GTK_ORIENTATION_HORIZONTAL, -1,
+                        &left, NULL, NULL, NULL);
+
+  if (priv->right_child)
+    gtk_widget_measure (GTK_WIDGET (priv->right_child),
+                        GTK_ORIENTATION_HORIZONTAL, -1,
+                        &right, NULL, NULL, NULL);
+
+  if (priv->top_child)
+    gtk_widget_measure (GTK_WIDGET (priv->top_child),
+                        GTK_ORIENTATION_VERTICAL, -1,
+                        &top, NULL, NULL, NULL);
+
+  if (priv->bottom_child)
+    gtk_widget_measure (GTK_WIDGET (priv->bottom_child),
+                        GTK_ORIENTATION_VERTICAL, -1,
+                        &bottom, NULL, NULL, NULL);
+
+  border->left = left;
+  border->right = right;
+  border->top = top;
+  border->bottom = bottom;
+}
+
 static void
 gtk_text_view_measure (GtkWidget      *widget,
                        GtkOrientation  orientation,
@@ -3910,50 +3916,52 @@ gtk_text_view_measure (GtkWidget      *widget,
                        int            *minimum_baseline,
                        int            *natural_baseline)
 {
-  GtkTextViewPrivate *priv = GTK_TEXT_VIEW (widget)->priv;
-  int min = 0, nat = 0;
-  GSList *tmp_list;
+  GtkTextView *text_view = GTK_TEXT_VIEW (widget);
+  GtkTextViewPrivate *priv = text_view->priv;
+  const GList *list;
+  GtkBorder borders;
+  int min = 0;
+  int nat = 0;
+  int extra;
 
-  if (orientation == GTK_ORIENTATION_HORIZONTAL)
-    {
-      min += priv->border_window_size.left + priv->border_window_size.right;
-      min += priv->left_margin + priv->right_margin;
-    }
-  else /* orientation == VERTICAL */
-    {
-      min += priv->border_window_size.top + priv->border_window_size.bottom;
-      min += priv->height;
-    }
+  gtk_text_view_measure_borders (text_view, &borders);
 
-  nat = min;
+  if (priv->center_child)
+    gtk_widget_measure (GTK_WIDGET (priv->center_child),
+                        orientation, for_size,
+                        &min, &nat, NULL, NULL);
 
-  tmp_list = priv->children;
-  while (tmp_list != NULL)
+  for (list = priv->anchored_children.head; list; list = list->next)
     {
-      GtkTextViewChild *child = tmp_list->data;
-      int child_min = 0, child_nat = 0;
+      const AnchoredChild *child = list->data;
+      int child_min = 0;
+      int child_nat = 0;
 
-      gtk_widget_measure (child->widget, orientation, for_size, &child_min, &child_nat, NULL, NULL);
+      gtk_widget_measure (child->widget, orientation, for_size,
+                          &child_min, &child_nat,
+                          NULL, NULL);
 
       /* Invalidate layout lines if required */
       if (child->anchor && priv->layout)
-        gtk_text_child_anchor_queue_resize (child->anchor,
-                                            priv->layout);
+        gtk_text_child_anchor_queue_resize (child->anchor, priv->layout);
 
       min = MAX (min, child_min);
       nat = MAX (nat, child_nat);
-
-      tmp_list = tmp_list->next;
     }
 
-  *minimum = min;
-  *natural = nat;
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    extra = borders.left + priv->left_margin + priv->right_margin + borders.right;
+  else
+    extra = borders.top + priv->height + borders.bottom;
+
+  *minimum = min + extra;
+  *natural = nat + extra;
 }
 
 static void
-gtk_text_view_compute_child_allocation (GtkTextView      *text_view,
-                                        GtkTextViewChild *vc,
-                                        GtkAllocation    *allocation)
+gtk_text_view_compute_child_allocation (GtkTextView         *text_view,
+                                        const AnchoredChild *vc,
+                                        GtkAllocation       *allocation)
 {
   gint buffer_y;
   GtkTextIter iter;
@@ -3977,8 +3985,8 @@ gtk_text_view_compute_child_allocation (GtkTextView      *text_view,
 }
 
 static void
-gtk_text_view_update_child_allocation (GtkTextView      *text_view,
-                                       GtkTextViewChild *vc)
+gtk_text_view_update_child_allocation (GtkTextView         *text_view,
+                                       const AnchoredChild *vc)
 {
   GtkAllocation allocation;
 
@@ -3996,13 +4004,13 @@ gtk_text_view_update_child_allocation (GtkTextView      *text_view,
 }
 
 static void
-gtk_text_view_child_allocated (GtkTextLayout *layout,
-                               GtkWidget     *child,
-                               gint           x,
-                               gint           y,
-                               gpointer       data)
+gtk_anchored_child_allocated (GtkTextLayout *layout,
+                              GtkWidget     *child,
+                              gint           x,
+                              gint           y,
+                              gpointer       data)
 {
-  GtkTextViewChild *vc = NULL;
+  AnchoredChild *vc = NULL;
   GtkTextView *text_view = data;
   
   /* x,y is the position of the child from the top of the line, and
@@ -4025,73 +4033,144 @@ gtk_text_view_child_allocated (GtkTextLayout *layout,
 static void
 gtk_text_view_allocate_children (GtkTextView *text_view)
 {
-  GSList *tmp_list;
+  GtkTextViewPrivate *priv = text_view->priv;
+  const GList *iter;
 
   DV(g_print(G_STRLOC"\n"));
-  
-  tmp_list = text_view->priv->children;
-  while (tmp_list != NULL)
+
+  for (iter = priv->anchored_children.head; iter; iter = iter->next)
     {
-      GtkTextViewChild *child = tmp_list->data;
+      const AnchoredChild *child = iter->data;
+      GtkTextIter child_loc;
 
-      g_assert (child != NULL);
-          
-      if (child->anchor)
-        {
-          /* We need to force-validate the regions containing
-           * children.
-           */
-          GtkTextIter child_loc;
-          gtk_text_buffer_get_iter_at_child_anchor (get_buffer (text_view),
-                                                    &child_loc,
-                                                    child->anchor);
-
-         /* Since anchored children are only ever allocated from
-           * gtk_text_layout_get_line_display() we have to make sure
-          * that the display line caching in the layout doesn't 
-           * get in the way. Invalidating the layout around the anchor
-           * achieves this.
-          */ 
-         if (_gtk_widget_get_alloc_needed (child->widget))
-           {
-             GtkTextIter end = child_loc;
-             gtk_text_iter_forward_char (&end);
-             gtk_text_layout_invalidate (text_view->priv->layout, &child_loc, &end);
-           }
+      /* We need to force-validate the regions containing children. */
+      gtk_text_buffer_get_iter_at_child_anchor (get_buffer (text_view),
+                                                &child_loc,
+                                                child->anchor);
 
-          gtk_text_layout_validate_yrange (text_view->priv->layout,
-                                           &child_loc,
-                                           0, 1);
-        }
-      else
+      /* Since anchored children are only ever allocated from
+       * gtk_text_layout_get_line_display() we have to make sure
+       * that the display line caching in the layout doesn't 
+       * get in the way. Invalidating the layout around the anchor
+       * achieves this.
+       */ 
+      if (_gtk_widget_get_alloc_needed (child->widget))
         {
-          GtkAllocation allocation;
-          GtkRequisition child_req;
-             
-          allocation.x = child->x;
-          allocation.y = child->y;
-
-          if (child->type == GTK_TEXT_WINDOW_TEXT ||
-              child->type == GTK_TEXT_WINDOW_LEFT ||
-              child->type == GTK_TEXT_WINDOW_RIGHT)
-            allocation.y -= text_view->priv->yoffset;
-          if (child->type == GTK_TEXT_WINDOW_TEXT ||
-              child->type == GTK_TEXT_WINDOW_TOP ||
-              child->type == GTK_TEXT_WINDOW_BOTTOM)
-            allocation.x -= text_view->priv->xoffset;
-
-          gtk_widget_get_preferred_size (child->widget, &child_req, NULL);
-
-          allocation.width = child_req.width;
-          allocation.height = child_req.height;
-          
-          gtk_widget_size_allocate (child->widget, &allocation, -1);
+          GtkTextIter end = child_loc;
+          gtk_text_iter_forward_char (&end);
+          gtk_text_layout_invalidate (priv->layout, &child_loc, &end);
         }
 
-      tmp_list = tmp_list->next;
+      gtk_text_layout_validate_yrange (priv->layout, &child_loc, 0, 1);
+    }
+}
+
+static GtkTextViewChild **
+find_child_for_window_type (GtkTextView       *text_view,
+                            GtkTextWindowType  window_type)
+{
+  switch (window_type)
+    {
+    case GTK_TEXT_WINDOW_LEFT:
+      return &text_view->priv->left_child;
+    case GTK_TEXT_WINDOW_RIGHT:
+      return &text_view->priv->right_child;
+    case GTK_TEXT_WINDOW_TOP:
+      return &text_view->priv->top_child;
+    case GTK_TEXT_WINDOW_BOTTOM:
+      return &text_view->priv->bottom_child;
+    case GTK_TEXT_WINDOW_TEXT:
+      return &text_view->priv->center_child;
+    case GTK_TEXT_WINDOW_WIDGET:
+    default:
+      return NULL;
     }
 }
 
+/**
+ * gtk_text_view_get_gutter:
+ * @text_view: a #GtkTextView
+ * @win: a #GtkWindowType
+ *
+ * Gets a #GtkWidget that has previously been set with
+ * gtk_text_view_set_gutter().
+ *
+ * @win must be one of %GTK_TEXT_WINDOW_LEFT, %GTK_TEXT_WINDOW_RIGHT,
+ * %GTK_TEXT_WINDOW_TOP, or %GTK_TEXT_WINDOW_BOTTOM.
+ *
+ * Returns: (transfer none) (nullable): a #GtkWidget or %NULL
+ */
+GtkWidget *
+gtk_text_view_get_gutter (GtkTextView       *text_view,
+                          GtkTextWindowType  win)
+{
+  GtkTextViewChild **childp;
+
+  g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
+  g_return_val_if_fail (win == GTK_TEXT_WINDOW_LEFT ||
+                        win == GTK_TEXT_WINDOW_RIGHT ||
+                        win == GTK_TEXT_WINDOW_TOP ||
+                        win == GTK_TEXT_WINDOW_BOTTOM, NULL);
+
+  childp = find_child_for_window_type (text_view, win);
+
+  if (*childp != NULL)
+    return GTK_WIDGET (*childp);
+
+  return NULL;
+}
+
+/**
+ * gtk_text_view_set_gutter:
+ * @text_view: a #GtkTextView
+ * @win: a #GtkTextWindowType
+ * @widget: (nullable): a #GtkWidget or %NULL
+ *
+ * Places @widget into the gutter specified by @win.
+ *
+ * @win must be one of %GTK_TEXT_WINDOW_LEFT, %GTK_TEXT_WINDOW_RIGHT,
+ * %GTK_TEXT_WINDOW_TOP, or %GTK_TEXT_WINDOW_BOTTOM.
+ */
+void
+gtk_text_view_set_gutter (GtkTextView       *text_view,
+                          GtkTextWindowType  win,
+                          GtkWidget         *widget)
+{
+  GtkTextViewChild **childp;
+  GtkTextViewChild *old_child;
+  GtkTextViewChild *new_child;
+
+  g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
+  g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
+  g_return_if_fail (win == GTK_TEXT_WINDOW_LEFT ||
+                    win == GTK_TEXT_WINDOW_RIGHT ||
+                    win == GTK_TEXT_WINDOW_TOP ||
+                    win == GTK_TEXT_WINDOW_BOTTOM);
+
+  childp = find_child_for_window_type (text_view, win);
+  old_child = *childp;
+
+  if ((GtkWidget *)old_child == widget)
+    return;
+
+  if (old_child != NULL)
+    {
+      *childp = NULL;
+      gtk_widget_unparent (GTK_WIDGET (old_child));
+      g_object_unref (old_child);
+    }
+
+  if (widget == NULL)
+    return;
+
+  new_child = GTK_TEXT_VIEW_CHILD (gtk_text_view_child_new (win));
+  gtk_container_add (GTK_CONTAINER (new_child), widget);
+
+  *childp = g_object_ref (new_child);
+  gtk_widget_set_parent (GTK_WIDGET (new_child), GTK_WIDGET (text_view));
+  update_node_ordering (GTK_WIDGET (text_view));
+}
+
 static void
 gtk_text_view_size_allocate (GtkWidget *widget,
                              int        widget_width,
@@ -4115,6 +4194,8 @@ gtk_text_view_size_allocate (GtkWidget *widget,
 
   DV(g_print(G_STRLOC"\n"));
 
+  gtk_text_view_measure_borders (text_view, &priv->border_window_size);
+
   /* distribute width/height among child windows. Ensure all
    * windows get at least a 1x1 allocation.
    */
@@ -4148,24 +4229,37 @@ gtk_text_view_size_allocate (GtkWidget *widget,
   right_rect.x = text_rect.x + text_rect.width;
   bottom_rect.y = text_rect.y + text_rect.height;
 
-  text_window_size_allocate (priv->text_window,
-                             &text_rect);
+  text_window_size_allocate (priv->text_window, &text_rect);
 
-  if (priv->left_window)
-    text_window_size_allocate (priv->left_window,
-                               &left_rect);
+  if (priv->center_child)
+    {
+      _gtk_text_view_child_set_offset (priv->center_child, priv->xoffset, priv->yoffset);
+      gtk_widget_size_allocate (GTK_WIDGET (priv->center_child), &text_rect, -1);
+    }
+
+  if (priv->left_child)
+    {
+      _gtk_text_view_child_set_offset (priv->left_child, priv->xoffset, priv->yoffset);
+      gtk_widget_size_allocate (GTK_WIDGET (priv->left_child), &left_rect, -1);
+    }
 
-  if (priv->right_window)
-    text_window_size_allocate (priv->right_window,
-                               &right_rect);
+  if (priv->right_child)
+    {
+      _gtk_text_view_child_set_offset (priv->right_child, priv->xoffset, priv->yoffset);
+      gtk_widget_size_allocate (GTK_WIDGET (priv->right_child), &right_rect, -1);
+    }
 
-  if (priv->top_window)
-    text_window_size_allocate (priv->top_window,
-                               &top_rect);
+  if (priv->top_child)
+    {
+      _gtk_text_view_child_set_offset (priv->top_child, priv->xoffset, priv->yoffset);
+      gtk_widget_size_allocate (GTK_WIDGET (priv->top_child), &top_rect, -1);
+    }
 
-  if (priv->bottom_window)
-    text_window_size_allocate (priv->bottom_window,
-                               &bottom_rect);
+  if (priv->bottom_child)
+    {
+      _gtk_text_view_child_set_offset (priv->bottom_child, priv->xoffset, priv->yoffset);
+      gtk_widget_size_allocate (GTK_WIDGET (priv->bottom_child), &bottom_rect, -1);
+    }
 
   gtk_text_view_update_layout_width (text_view);
   
@@ -4398,33 +4492,24 @@ changed_handler (GtkTextLayout     *layout,
   DV(g_print(">Lines Validated ("G_STRLOC")\n"));
 
   if (gtk_widget_get_realized (widget))
-    {      
-      text_window_invalidate (priv->text_window);
+    {
+      gtk_widget_queue_draw (widget);
 
       DV(g_print(" invalidated rect: %d,%d %d x %d\n",
                  redraw_rect.x,
                  redraw_rect.y,
                  redraw_rect.width,
                  redraw_rect.height));
-      
-      if (priv->left_window)
-        text_window_invalidate (priv->left_window);
-      if (priv->right_window)
-        text_window_invalidate (priv->right_window);
-      if (priv->top_window)
-        text_window_invalidate (priv->top_window);
-      if (priv->bottom_window)
-        text_window_invalidate (priv->bottom_window);
 
       queue_update_im_spot_location (text_view);
     }
   
   if (old_height != new_height)
     {
-      GSList *tmp_list;
+      const GList *iter;
+      GtkTextIter first;
       int new_first_para_top;
       int old_first_para_top;
-      GtkTextIter first;
       
       /* If the bottom of the old area was above the top of the
        * screen, we need to scroll to keep the current top of the
@@ -4451,17 +4536,13 @@ changed_handler (GtkTextLayout     *layout,
 
       /* FIXME be smarter about which anchored widgets we update */
 
-      tmp_list = priv->children;
-      while (tmp_list != NULL)
+      for (iter = priv->anchored_children.head; iter; iter = iter->next)
         {
-          GtkTextViewChild *child = tmp_list->data;
-
-          if (child->anchor)
-            gtk_text_view_update_child_allocation (text_view, child);
-
-          tmp_list = tmp_list->next;
+          const AnchoredChild *ac = iter->data;
+          gtk_text_view_update_child_allocation (text_view, ac);
         }
-      gtk_widget_queue_resize(widget);
+
+      gtk_widget_queue_resize (widget);
     }
 }
 
@@ -4527,45 +4608,6 @@ gtk_text_view_map (GtkWidget *widget)
   GTK_WIDGET_CLASS (gtk_text_view_parent_class)->map (widget);
 }
 
-static void
-text_window_set_padding (GtkTextView     *text_view,
-                         GtkStyleContext *context)
-{
-  GtkTextViewPrivate *priv;
-  GtkBorder padding, border;
-
-  priv = text_view->priv;
-
-  gtk_style_context_get_padding (context, &padding);
-  gtk_style_context_get_border (context, &border);
-  padding.left += border.left;
-  padding.right += border.right;
-  padding.top += border.top;
-  padding.bottom += border.bottom;
-
-  if (padding.left != priv->left_padding ||
-      padding.right != priv->right_padding ||
-      padding.top != priv->top_padding ||
-      padding.bottom != priv->bottom_padding)
-    {
-      priv->xoffset += priv->left_padding;
-      priv->yoffset += priv->top_padding;
-
-      priv->top_margin = priv->top_margin;
-      priv->bottom_margin = priv->bottom_margin;
-      priv->left_margin = priv->left_margin;
-      priv->right_margin = priv->right_margin;
-
-      if (priv->layout && priv->layout->default_style)
-        {
-          priv->layout->right_padding = priv->right_padding;
-          priv->layout->left_padding = priv->left_padding;
-
-          gtk_text_layout_default_style_changed (priv->layout);
-        }
-    }
-}
-
 static void
 gtk_text_view_style_updated (GtkWidget *widget)
 {
@@ -4639,14 +4681,6 @@ gtk_text_view_state_flags_changed (GtkWidget     *widget,
   state &= ~GTK_STATE_FLAG_DROP_ACTIVE;
 
   gtk_css_node_set_state (priv->selection_node, state);
-  if (priv->left_window)
-    gtk_css_node_set_state (priv->left_window->css_node, state);
-  if (priv->right_window)
-    gtk_css_node_set_state (priv->right_window->css_node, state);
-  if (priv->top_window)
-    gtk_css_node_set_state (priv->top_window->css_node, state);
-  if (priv->bottom_window)
-    gtk_css_node_set_state (priv->bottom_window->css_node, state);
 
   gtk_widget_queue_draw (widget);
 }
@@ -5388,17 +5422,15 @@ draw_text (GtkWidget   *widget,
   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
   GtkTextViewPrivate *priv = text_view->priv;
   GtkStyleContext *context;
-  graphene_point_t translate = {0};
+  gboolean did_save = FALSE;
 
-  if (priv->left_window)
-    translate.x = priv->left_window->allocation.width;
-  if (priv->top_window)
-    translate.y = priv->top_window->allocation.height;
-
-  if (translate.x || translate.y)
+  if (priv->border_window_size.left || priv->border_window_size.top)
     {
+      did_save = TRUE;
       gtk_snapshot_save (snapshot);
-      gtk_snapshot_translate (snapshot, &translate);
+      gtk_snapshot_translate (snapshot,
+                              &GRAPHENE_POINT_INIT (priv->border_window_size.left,
+                                                    priv->border_window_size.top));
     }
 
   gtk_snapshot_push_clip (snapshot,
@@ -5439,64 +5471,44 @@ draw_text (GtkWidget   *widget,
 
   gtk_snapshot_pop (snapshot);
 
-  if (translate.x || translate.y)
+  if (did_save)
     gtk_snapshot_restore (snapshot);
 }
 
-static void
-paint_border_window (GtkTextView     *text_view,
-                     GtkSnapshot     *snapshot,
-                     GtkTextWindow   *text_window,
-                     GtkStyleContext *context)
+static inline void
+snapshot_text_view_child (GtkWidget        *widget,
+                          GtkTextViewChild *child,
+                          GtkSnapshot      *snapshot)
 {
-  gint x, y, w, h;
-
-  if (text_window == NULL)
-    return;
-
-  x = text_window_get_x (text_window);
-  y = text_window_get_y (text_window);
-  w = text_window_get_width (text_window);
-  h = text_window_get_height (text_window);
-
-  gtk_style_context_save_to_node (context, text_window->css_node);
-
-  gtk_snapshot_render_background (snapshot, context, x, y, w, h);
-
-  gtk_style_context_restore (context);
+  if (child != NULL)
+    gtk_widget_snapshot_child (widget, GTK_WIDGET (child), snapshot);
 }
 
 static void
 gtk_text_view_snapshot (GtkWidget   *widget,
                         GtkSnapshot *snapshot)
 {
-  GtkTextViewPrivate *priv = ((GtkTextView *)widget)->priv;
-  GSList *tmp_list;
-  GtkStyleContext *context;
-
-  context = gtk_widget_get_style_context (widget);
-
-  text_window_set_padding (GTK_TEXT_VIEW (widget), context);
+  GtkTextView *text_view = GTK_TEXT_VIEW (widget);
+  GtkTextViewPrivate *priv = text_view->priv;
+  const GList *iter;
 
   DV(g_print (">Exposed ("G_STRLOC")\n"));
 
   draw_text (widget, snapshot); 
 
-  paint_border_window (GTK_TEXT_VIEW (widget), snapshot, priv->left_window, context);
-  paint_border_window (GTK_TEXT_VIEW (widget), snapshot, priv->right_window, context);
-  paint_border_window (GTK_TEXT_VIEW (widget), snapshot, priv->top_window, context);
-  paint_border_window (GTK_TEXT_VIEW (widget), snapshot, priv->bottom_window, context);
+  snapshot_text_view_child (widget, priv->left_child, snapshot);
+  snapshot_text_view_child (widget, priv->right_child, snapshot);
+  snapshot_text_view_child (widget, priv->top_child, snapshot);
+  snapshot_text_view_child (widget, priv->bottom_child, snapshot);
+  snapshot_text_view_child (widget, priv->center_child, snapshot);
 
   /* Propagate exposes to all unanchored children. 
    * Anchored children are handled in gtk_text_view_paint(). 
    */
-  tmp_list = GTK_TEXT_VIEW (widget)->priv->children;
-  while (tmp_list != NULL)
+  for (iter = priv->anchored_children.head; iter; iter = iter->next)
     {
-      GtkTextViewChild *vc = tmp_list->data;
-
+      const AnchoredChild *vc = iter->data;
       gtk_widget_snapshot_child (widget, vc->widget, snapshot);
-      tmp_list = tmp_list->next;
     }
 }
 
@@ -5541,45 +5553,61 @@ static void
 gtk_text_view_add (GtkContainer *container,
                    GtkWidget    *child)
 {
-  /* This is pretty random. */
-  gtk_text_view_add_child_in_window (GTK_TEXT_VIEW (container),
-                                     child,
-                                     GTK_TEXT_WINDOW_WIDGET,
-                                     0, 0);
+  /* There isn't really a good default for what to do when
+   * using gtk_container_add() for @child. So we default to
+   * placing it at 0,0 in the text window.
+   */
+  gtk_text_view_add_overlay (GTK_TEXT_VIEW (container), child, 0, 0);
 }
 
 static void
 gtk_text_view_remove (GtkContainer *container,
                       GtkWidget    *child)
 {
-  GtkTextView *text_view;
-  GtkTextViewPrivate *priv;
-  GtkTextViewChild *vc;
-  GSList *iter;
+  GtkTextView *text_view = GTK_TEXT_VIEW (container);
+  GtkTextViewPrivate *priv = text_view->priv;
+  AnchoredChild *ac;
+
+  if (GTK_IS_TEXT_VIEW_CHILD (child))
+    {
+      GtkTextViewChild *vc = GTK_TEXT_VIEW_CHILD (child);
+      GtkTextViewChild **vcp;
+
+      if (vc == priv->left_child)
+        vcp = &priv->left_child;
+      else if (vc == priv->left_child)
+        vcp = &priv->left_child;
+      else if (vc == priv->top_child)
+        vcp = &priv->top_child;
+      else if (vc == priv->bottom_child)
+        vcp = &priv->bottom_child;
+      else if (vc == priv->center_child)
+        vcp = &priv->center_child;
+      else
+        vcp = NULL;
 
-  text_view = GTK_TEXT_VIEW (container);
-  priv = text_view->priv;
+      if (vcp)
+        {
+          *vcp = NULL;
+          gtk_widget_unparent (child);
+          g_object_unref (child);
+          return;
+        }
+    }
 
-  vc = NULL;
-  iter = priv->children;
+  ac = g_object_get_qdata (G_OBJECT (child), quark_text_view_child);
 
-  while (iter != NULL)
+  if (ac == NULL)
     {
-      vc = iter->data;
-
-      if (vc->widget == child)
-        break;
-
-      iter = iter->next;
+      g_warning ("%s is not a child of %s",
+                 G_OBJECT_TYPE_NAME (child),
+                 G_OBJECT_TYPE_NAME (text_view));
+      return;
     }
 
-  g_assert (iter != NULL); /* be sure we had the child in the list */
-
-  priv->children = g_slist_remove (priv->children, vc);
-
-  gtk_widget_unparent (vc->widget);
-
-  text_view_child_free (vc);
+  g_queue_unlink (&priv->anchored_children, &ac->link);
+  gtk_widget_unparent (ac->widget);
+  anchored_child_free (ac);
 }
 
 static void
@@ -5587,28 +5615,34 @@ gtk_text_view_forall (GtkContainer *container,
                       GtkCallback   callback,
                       gpointer      callback_data)
 {
-  GSList *iter;
+  const GList *iter;
   GtkTextView *text_view;
-  GSList *copy;
+  GtkTextViewPrivate *priv;
 
   g_return_if_fail (GTK_IS_TEXT_VIEW (container));
   g_return_if_fail (callback != NULL);
 
   text_view = GTK_TEXT_VIEW (container);
+  priv = text_view->priv;
 
-  copy = g_slist_copy (text_view->priv->children);
-  iter = copy;
-
+  if (priv->left_child)
+    callback (GTK_WIDGET (priv->left_child), callback_data);
+  if (priv->right_child)
+    callback (GTK_WIDGET (priv->right_child), callback_data);
+  if (priv->top_child)
+    callback (GTK_WIDGET (priv->top_child), callback_data);
+  if (priv->bottom_child)
+    callback (GTK_WIDGET (priv->bottom_child), callback_data);
+  if (priv->center_child)
+    callback (GTK_WIDGET (priv->center_child), callback_data);
+
+  iter = priv->anchored_children.head;
   while (iter != NULL)
     {
-      GtkTextViewChild *vc = iter->data;
-
-      (* callback) (vc->widget, callback_data);
-
+      const AnchoredChild *ac = iter->data;
       iter = iter->next;
+      callback (ac->widget, callback_data);
     }
-
-  g_slist_free (copy);
 }
 
 #define CURSOR_ON_MULTIPLIER 2
@@ -6708,8 +6742,7 @@ gtk_text_view_toggle_overwrite (GtkTextView *text_view)
     gtk_text_layout_set_overwrite_mode (priv->layout,
                                        priv->overwrite_mode && priv->editable);
 
-  if (priv->text_window)
-    text_window_invalidate (priv->text_window);
+  gtk_widget_queue_draw (GTK_WIDGET (text_view));
 
   gtk_text_view_pend_cursor_blink (text_view);
 
@@ -7442,7 +7475,7 @@ gtk_text_view_ensure_layout (GtkTextView *text_view)
     {
       GtkTextAttributes *style;
       PangoContext *ltr_context, *rtl_context;
-      GSList *tmp_list;
+      const GList *iter;
 
       DV(g_print(G_STRLOC"\n"));
       
@@ -7460,7 +7493,7 @@ gtk_text_view_ensure_layout (GtkTextView *text_view)
 
       g_signal_connect (priv->layout,
                        "allocate-child",
-                       G_CALLBACK (gtk_text_view_child_allocated),
+                       G_CALLBACK (gtk_anchored_child_allocated),
                        text_view);
       
       if (get_buffer (text_view))
@@ -7512,19 +7545,13 @@ gtk_text_view_ensure_layout (GtkTextView *text_view)
 
       /* Set layout for all anchored children */
 
-      tmp_list = priv->children;
-      while (tmp_list != NULL)
+      iter = priv->anchored_children.head;
+      while (iter != NULL)
         {
-          GtkTextViewChild *vc = tmp_list->data;
-
-          if (vc->anchor)
-            {
-              gtk_text_anchored_child_set_layout (vc->widget,
-                                                  priv->layout);
-              /* vc may now be invalid! */
-            }
-
-          tmp_list = tmp_list->next;
+          const AnchoredChild *ac = iter->data;
+          iter = iter->next;
+          gtk_text_anchored_child_set_layout (ac->widget, priv->layout);
+          /* ac may now be invalid! */
         }
     }
 }
@@ -7546,7 +7573,7 @@ gtk_text_view_destroy_layout (GtkTextView *text_view)
 
   if (priv->layout)
     {
-      GSList *tmp_list;
+      const GList *iter;
 
       gtk_text_view_remove_validate_idles (text_view);
 
@@ -7557,19 +7584,13 @@ gtk_text_view_destroy_layout (GtkTextView *text_view)
                                            changed_handler,
                                            text_view);
 
-      /* Remove layout from all anchored children */
-      tmp_list = priv->children;
-      while (tmp_list != NULL)
+      iter = priv->anchored_children.head;
+      while (iter != NULL)
         {
-          GtkTextViewChild *vc = tmp_list->data;
-
-          if (vc->anchor)
-            {
-              gtk_text_anchored_child_set_layout (vc->widget, NULL);
-              /* vc may now be invalid! */
-            }
-
-          tmp_list = tmp_list->next;
+          const AnchoredChild *ac = iter->data;
+          iter = iter->next;
+          gtk_text_anchored_child_set_layout (ac->widget, NULL);
+          /* vc may now be invalid! */
         }
 
       gtk_text_view_stop_cursor_blink (text_view);
@@ -8162,7 +8183,7 @@ gtk_text_view_set_vadjustment_values (GtkTextView *text_view)
   new_value = CLAMP (y, 0, new_upper - screen_height);
   if (new_value != old_value)
     gtk_adjustment_set_value (priv->vadjustment, new_value);
- }
+}
 
 static void
 gtk_text_view_value_changed (GtkAdjustment *adjustment,
@@ -8264,7 +8285,7 @@ gtk_text_view_value_changed (GtkAdjustment *adjustment,
     gtk_text_view_update_handles (text_view,
                                   _gtk_text_handle_get_mode (priv->text_handle));
 
-  if (priv->children)
+  if (priv->anchored_children.length > 0)
     gtk_widget_queue_allocate (GTK_WIDGET (text_view));
   else
     gtk_widget_queue_draw (GTK_WIDGET (text_view));
@@ -9037,7 +9058,7 @@ static void
 update_node_ordering (GtkWidget *widget)
 {
   GtkTextViewPrivate *priv = GTK_TEXT_VIEW (widget)->priv;
-  GtkCssNode *widget_node, *sibling;
+  GtkCssNode *widget_node, *sibling, *child_node;
 
   if (priv->text_window == NULL)
     return;
@@ -9045,38 +9066,44 @@ update_node_ordering (GtkWidget *widget)
   widget_node = gtk_widget_get_css_node (widget);
   sibling = priv->text_window->css_node;
 
-  if (priv->left_window)
+  if (priv->left_child)
     {
-      gtk_css_node_insert_before (widget_node, priv->left_window->css_node, sibling);
-      sibling = priv->left_window->css_node;
+      child_node = gtk_widget_get_css_node (GTK_WIDGET (priv->left_child));
+      gtk_css_node_insert_before (widget_node, child_node, sibling);
+      sibling = child_node;
     }
-  if (priv->top_window)
+
+  if (priv->top_child)
     {
-      gtk_css_node_insert_before (widget_node, priv->top_window->css_node, sibling);
+      child_node = gtk_widget_get_css_node (GTK_WIDGET (priv->top_child));
+      gtk_css_node_insert_before (widget_node, child_node, sibling);
     }
 
   sibling = priv->text_window->css_node;
-  if (priv->right_window)
+
+  if (priv->right_child)
     {
-      gtk_css_node_insert_after (widget_node, priv->right_window->css_node, sibling);
-      sibling = priv->right_window->css_node;
+      child_node = gtk_widget_get_css_node (GTK_WIDGET (priv->right_child));
+      gtk_css_node_insert_after (widget_node, child_node, sibling);
+      sibling = child_node;
     }
-  if (priv->bottom_window)
+
+  if (priv->bottom_child)
     {
-      gtk_css_node_insert_after (widget_node, priv->bottom_window->css_node, sibling);
+      child_node = gtk_widget_get_css_node (GTK_WIDGET (priv->bottom_child));
+      gtk_css_node_insert_after (widget_node, child_node, sibling);
     }
 }
 
 static GtkTextWindow*
-text_window_new (GtkTextWindowType  type,
-                 GtkWidget         *widget)
+text_window_new (GtkWidget *widget)
 {
   GtkTextWindow *win;
   GtkCssNode *widget_node;
 
   win = g_slice_new (GtkTextWindow);
 
-  win->type = type;
+  win->type = GTK_TEXT_WINDOW_TEXT;
   win->widget = widget;
   win->allocation.width = 0;
   win->allocation.height = 0;
@@ -9088,35 +9115,8 @@ text_window_new (GtkTextWindowType  type,
   gtk_css_node_set_parent (win->css_node, widget_node);
   gtk_css_node_set_state (win->css_node, gtk_css_node_get_state (widget_node));
   g_signal_connect_object (win->css_node, "style-changed", G_CALLBACK (node_style_changed_cb), widget, 0);
-  if (type == GTK_TEXT_WINDOW_TEXT)
-    {
-      gtk_css_node_set_name (win->css_node, I_("text"));
-    }
-  else
-    {
-      gtk_css_node_set_name (win->css_node, I_("border"));
-      switch (type)
-        {
-        case GTK_TEXT_WINDOW_LEFT:
-          gtk_css_node_add_class (win->css_node, g_quark_from_static_string (GTK_STYLE_CLASS_LEFT));
-          break;
-        case GTK_TEXT_WINDOW_RIGHT:
-          gtk_css_node_add_class (win->css_node, g_quark_from_static_string (GTK_STYLE_CLASS_RIGHT));
-          break;
-        case GTK_TEXT_WINDOW_TOP:
-          gtk_css_node_add_class (win->css_node, g_quark_from_static_string (GTK_STYLE_CLASS_TOP));
-          break;
-        case GTK_TEXT_WINDOW_BOTTOM:
-          gtk_css_node_add_class (win->css_node, g_quark_from_static_string (GTK_STYLE_CLASS_BOTTOM));
-          break;
-        case GTK_TEXT_WINDOW_PRIVATE:
-        case GTK_TEXT_WINDOW_WIDGET:
-        case GTK_TEXT_WINDOW_TEXT:
-        default:
-          /* no extra style class */
-          break;
-        }
-    }
+  gtk_css_node_set_name (win->css_node, I_("text"));
+
   g_object_unref (win->css_node);
 
   return win;
@@ -9137,24 +9137,6 @@ text_window_size_allocate (GtkTextWindow *win,
   win->allocation = *rect;
 }
 
-static void
-text_window_invalidate (GtkTextWindow *win)
-{
-  gtk_widget_queue_draw (win->widget);
-}
-
-static gint
-text_window_get_x (GtkTextWindow *win)
-{
-  return win->allocation.x;
-}
-
-static gint
-text_window_get_y (GtkTextWindow *win)
-{
-  return win->allocation.y;
-}
-
 static gint
 text_window_get_width (GtkTextWindow *win)
 {
@@ -9169,53 +9151,10 @@ text_window_get_height (GtkTextWindow *win)
 
 /* Windows */
 
-
-static GtkCssNode *
-gtk_text_view_get_css_node (GtkTextView       *text_view,
-                            GtkTextWindowType  win)
-{
-  GtkTextViewPrivate *priv = text_view->priv;
-
-  switch (win)
-    {
-    case GTK_TEXT_WINDOW_WIDGET:
-      return gtk_widget_get_css_node (GTK_WIDGET (text_view));
-
-    case GTK_TEXT_WINDOW_TEXT:
-      return priv->text_window->css_node;
-
-    case GTK_TEXT_WINDOW_LEFT:
-      if (priv->left_window)
-        return priv->left_window->css_node;
-      break;
-
-    case GTK_TEXT_WINDOW_RIGHT:
-      if (priv->right_window)
-        return priv->right_window->css_node;
-      break;
-
-    case GTK_TEXT_WINDOW_TOP:
-      if (priv->top_window)
-        return priv->top_window->css_node;
-      break;
-
-    case GTK_TEXT_WINDOW_BOTTOM:
-      if (priv->bottom_window)
-        return priv->bottom_window->css_node;
-      break;
-
-    case GTK_TEXT_WINDOW_PRIVATE:
-    default:
-      break;
-    }
-
-  return NULL;
-}
-
 /**
  * gtk_text_view_buffer_to_window_coords:
  * @text_view: a #GtkTextView
- * @win: a #GtkTextWindowType except #GTK_TEXT_WINDOW_PRIVATE
+ * @win: a #GtkTextWindowType
  * @buffer_x: buffer x coordinate
  * @buffer_y: buffer y coordinate
  * @window_x: (out) (allow-none): window x coordinate return location or %NULL
@@ -9268,10 +9207,6 @@ gtk_text_view_buffer_to_window_coords (GtkTextView      *text_view,
       buffer_y -= text_window_get_height (priv->text_window);
       break;
 
-    case GTK_TEXT_WINDOW_PRIVATE:
-      g_warning ("%s: can't get coords for private windows", G_STRFUNC);
-      break;
-
     default:
       g_warning ("%s: Unknown GtkTextWindowType", G_STRFUNC);
       break;
@@ -9286,7 +9221,7 @@ gtk_text_view_buffer_to_window_coords (GtkTextView      *text_view,
 /**
  * gtk_text_view_window_to_buffer_coords:
  * @text_view: a #GtkTextView
- * @win: a #GtkTextWindowType except #GTK_TEXT_WINDOW_PRIVATE
+ * @win: a #GtkTextWindowType
  * @window_x: window x coordinate
  * @window_y: window y coordinate
  * @buffer_x: (out) (allow-none): buffer x coordinate return location or %NULL
@@ -9336,10 +9271,6 @@ gtk_text_view_window_to_buffer_coords (GtkTextView      *text_view,
       window_y += text_window_get_height (priv->text_window);
       break;
 
-    case GTK_TEXT_WINDOW_PRIVATE:
-      g_warning ("%s: can't get coords for private windows", G_STRFUNC);
-      break;
-
     default:
       g_warning ("%s: Unknown GtkTextWindowType", G_STRFUNC);
       break;
@@ -9351,156 +9282,23 @@ gtk_text_view_window_to_buffer_coords (GtkTextView      *text_view,
     *buffer_y = window_y + priv->yoffset;
 }
 
-static void
-set_window_size (GtkTextView        *text_view,
-                 gint                size,
-                 GtkTextWindowType   type,
-                 GtkTextWindow     **winp,
-                 gint16             *sizep)
-{
-  if (*sizep == size)
-    return;
-  
-  if (size == 0)
-    {
-      text_window_free (*winp);
-      *winp = NULL;
-    }
-  else
-    {
-      if (*winp == NULL)
-        {
-          *winp = text_window_new (type, GTK_WIDGET (text_view));
-          /* if the widget is already realized we need to realize the child manually */
-          update_node_ordering (GTK_WIDGET (text_view));
-        }
-    }
-
-  *sizep = size;
-
-  gtk_widget_queue_resize (GTK_WIDGET (text_view));
-}
-
-/**
- * gtk_text_view_set_border_window_size:
- * @text_view: a #GtkTextView
- * @type: window to affect
- * @size: width or height of the window
- *
- * Sets the width of %GTK_TEXT_WINDOW_LEFT or %GTK_TEXT_WINDOW_RIGHT,
- * or the height of %GTK_TEXT_WINDOW_TOP or %GTK_TEXT_WINDOW_BOTTOM.
- * Automatically destroys the corresponding window if the size is set
- * to 0, and creates the window if the size is set to non-zero.  This
- * function can only be used for the “border windows,” it doesn’t work
- * with #GTK_TEXT_WINDOW_WIDGET, #GTK_TEXT_WINDOW_TEXT, or
- * #GTK_TEXT_WINDOW_PRIVATE.
- **/
-void
-gtk_text_view_set_border_window_size (GtkTextView      *text_view,
-                                      GtkTextWindowType type,
-                                      gint              size)
-{
-  GtkTextViewPrivate *priv = text_view->priv;
-
-  g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
-  g_return_if_fail (size >= 0);
-
-  switch (type)
-    {
-    case GTK_TEXT_WINDOW_LEFT:
-      set_window_size (text_view, size, GTK_TEXT_WINDOW_LEFT,
-                       &priv->left_window, &priv->border_window_size.left);
-      break;
-
-    case GTK_TEXT_WINDOW_RIGHT:
-      set_window_size (text_view, size, GTK_TEXT_WINDOW_RIGHT,
-                       &priv->right_window, &priv->border_window_size.right);
-      break;
-
-    case GTK_TEXT_WINDOW_TOP:
-      set_window_size (text_view, size, GTK_TEXT_WINDOW_TOP,
-                       &priv->top_window, &priv->border_window_size.top);
-      break;
-
-    case GTK_TEXT_WINDOW_BOTTOM:
-      set_window_size (text_view, size, GTK_TEXT_WINDOW_BOTTOM,
-                       &priv->bottom_window, &priv->border_window_size.bottom);
-      break;
-
-    case GTK_TEXT_WINDOW_PRIVATE:
-    case GTK_TEXT_WINDOW_WIDGET:
-    case GTK_TEXT_WINDOW_TEXT:
-    default:
-      g_warning ("Can only set size of left/right/top/bottom border windows with 
gtk_text_view_set_border_window_size()");
-      break;
-    }
-}
-
-/**
- * gtk_text_view_get_border_window_size:
- * @text_view: a #GtkTextView
- * @type: window to return size from
- *
- * Gets the width of the specified border window. See
- * gtk_text_view_set_border_window_size().
- *
- * Returns: width of window
- **/
-gint
-gtk_text_view_get_border_window_size (GtkTextView       *text_view,
-                                     GtkTextWindowType  type)
-{
-  GtkTextViewPrivate *priv = text_view->priv;
-
-  g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
-  
-  switch (type)
-    {
-    case GTK_TEXT_WINDOW_LEFT:
-      return priv->border_window_size.left;
-      
-    case GTK_TEXT_WINDOW_RIGHT:
-      return priv->border_window_size.right;
-      
-    case GTK_TEXT_WINDOW_TOP:
-      return priv->border_window_size.top;
-
-    case GTK_TEXT_WINDOW_BOTTOM:
-      return priv->border_window_size.bottom;
-      
-    case GTK_TEXT_WINDOW_PRIVATE:
-    case GTK_TEXT_WINDOW_WIDGET:
-    case GTK_TEXT_WINDOW_TEXT:
-    default:
-      g_warning ("Can only get size of left/right/top/bottom border windows with 
gtk_text_view_get_border_window_size()");
-      break;
-    }
-
-  return 0;
-}
-
 /*
  * Child widgets
  */
 
-static GtkTextViewChild*
-text_view_child_new_anchored (GtkWidget          *child,
-                              GtkTextChildAnchor *anchor,
-                              GtkTextLayout      *layout)
+static AnchoredChild *
+anchored_child_new (GtkWidget          *child,
+                    GtkTextChildAnchor *anchor,
+                    GtkTextLayout      *layout)
 {
-  GtkTextViewChild *vc;
-
-  vc = g_slice_new (GtkTextViewChild);
-
-  vc->type = GTK_TEXT_WINDOW_PRIVATE;
-  vc->widget = child;
-  vc->anchor = anchor;
+  AnchoredChild *vc;
 
+  vc = g_slice_new (AnchoredChild);
+  vc->link.data = vc;
+  vc->widget = g_object_ref (child);
+  vc->anchor = g_object_ref (anchor);
   vc->from_top_of_line = 0;
   vc->from_left_of_buffer = 0;
-  
-  g_object_ref (vc->widget);
-  g_object_ref (vc->anchor);
 
   g_object_set_qdata (G_OBJECT (child), quark_text_view_child, vc);
 
@@ -9509,64 +9307,31 @@ text_view_child_new_anchored (GtkWidget          *child,
   return vc;
 }
 
-static GtkTextViewChild*
-text_view_child_new_window (GtkWidget          *child,
-                            GtkTextWindowType   type,
-                            gint                x,
-                            gint                y)
-{
-  GtkTextViewChild *vc;
-
-  vc = g_slice_new (GtkTextViewChild);
-
-  vc->widget = child;
-  vc->anchor = NULL;
-
-  vc->from_top_of_line = 0;
-  vc->from_left_of_buffer = 0;
- 
-  g_object_ref (vc->widget);
-
-  vc->type = type;
-  vc->x = x;
-  vc->y = y;
-
-  g_object_set_qdata (G_OBJECT (child), quark_text_view_child, vc);
-  
-  return vc;
-}
-
 static void
-text_view_child_free (GtkTextViewChild *child)
+anchored_child_free (AnchoredChild *child)
 {
+  g_assert (child->link.prev == NULL);
+  g_assert (child->link.next == NULL);
+
   g_object_set_qdata (G_OBJECT (child->widget), quark_text_view_child, NULL);
 
-  if (child->anchor)
-    {
-      gtk_text_child_anchor_unregister_child (child->anchor,
-                                              child->widget);
-      g_object_unref (child->anchor);
-    }
+  gtk_text_child_anchor_unregister_child (child->anchor, child->widget);
 
+  g_object_unref (child->anchor);
   g_object_unref (child->widget);
 
-  g_slice_free (GtkTextViewChild, child);
+  g_slice_free (AnchoredChild, child);
 }
 
 static void
-add_child (GtkTextView      *text_view,
-           GtkTextViewChild *vc)
+add_child (GtkTextView   *text_view,
+           AnchoredChild *vc)
 {
-  GtkCssNode *parent;
-
-  text_view->priv->children = g_slist_prepend (text_view->priv->children, vc);
-
-  parent = gtk_text_view_get_css_node (text_view, vc->type);
-  if (parent == NULL)
-    parent = gtk_widget_get_css_node (GTK_WIDGET (text_view));
-
-  gtk_css_node_set_parent (gtk_widget_get_css_node (vc->widget), parent);
+  GtkTextViewPrivate *priv = text_view->priv;
 
+  g_queue_push_head_link (&priv->anchored_children, &vc->link);
+  gtk_css_node_set_parent (gtk_widget_get_css_node (vc->widget),
+                           priv->text_window->css_node);
   gtk_widget_set_parent (vc->widget, GTK_WIDGET (text_view));
 }
 
@@ -9583,7 +9348,7 @@ gtk_text_view_add_child_at_anchor (GtkTextView          *text_view,
                                    GtkWidget            *child,
                                    GtkTextChildAnchor   *anchor)
 {
-  GtkTextViewChild *vc;
+  AnchoredChild *vc;
 
   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
   g_return_if_fail (GTK_IS_WIDGET (child));
@@ -9592,8 +9357,7 @@ gtk_text_view_add_child_at_anchor (GtkTextView          *text_view,
 
   gtk_text_view_ensure_layout (text_view);
 
-  vc = text_view_child_new_anchored (child, anchor,
-                                     text_view->priv->layout);
+  vc = anchored_child_new (child, anchor, text_view->priv->layout);
 
   add_child (text_view, vc);
 
@@ -9601,82 +9365,81 @@ gtk_text_view_add_child_at_anchor (GtkTextView          *text_view,
   g_assert (gtk_widget_get_parent (child) == GTK_WIDGET (text_view));
 }
 
+static void
+ensure_child (GtkTextView        *text_view,
+              GtkTextViewChild  **child,
+              GtkTextWindowType   window_type)
+{
+  GtkCssNode *css_node;
+  GtkWidget *new_child;
+
+  if (*child != NULL)
+    return;
+
+  new_child = gtk_text_view_child_new (window_type);
+  css_node = gtk_widget_get_css_node (new_child);
+  gtk_css_node_set_parent (css_node,
+                           gtk_widget_get_css_node (GTK_WIDGET (text_view)));
+  *child = g_object_ref (GTK_TEXT_VIEW_CHILD (new_child));
+  gtk_widget_set_parent (GTK_WIDGET (new_child), GTK_WIDGET (text_view));
+}
+
 /**
- * gtk_text_view_add_child_in_window:
+ * gtk_text_view_add_overlay:
  * @text_view: a #GtkTextView
  * @child: a #GtkWidget
- * @which_window: which window the child should appear in
  * @xpos: X position of child in window coordinates
  * @ypos: Y position of child in window coordinates
  *
- * Adds a child at fixed coordinates in one of the text widget's
- * windows.
+ * Adds @child at a fixed coordinate in the #GtkTextView's text window. The
+ * @xpos and @ypos must be in buffer coordinates (see
+ * gtk_text_view_get_iter_location() to conver to buffer coordinates).
  *
- * The window must have nonzero size (see
- * gtk_text_view_set_border_window_size()). Note that the child
- * coordinates are given relative to scrolling. When
- * placing a child in #GTK_TEXT_WINDOW_WIDGET, scrolling is
- * irrelevant, the child floats above all scrollable areas. But when
- * placing a child in one of the scrollable windows (border windows or
- * text window) it will move with the scrolling as needed.
+ * @child will scroll with the text view.
+ *
+ * If instead you want a widget that will not move with the #GtkTextView
+ * contents see #GtkOverlay.
  */
 void
-gtk_text_view_add_child_in_window (GtkTextView       *text_view,
-                                   GtkWidget         *child,
-                                   GtkTextWindowType  which_window,
-                                   gint               xpos,
-                                   gint               ypos)
+gtk_text_view_add_overlay (GtkTextView *text_view,
+                           GtkWidget   *child,
+                           gint         xpos,
+                           gint         ypos)
 {
-  GtkTextViewChild *vc;
-
   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
   g_return_if_fail (GTK_IS_WIDGET (child));
   g_return_if_fail (gtk_widget_get_parent (child) == NULL);
 
-  vc = text_view_child_new_window (child, which_window,
-                                   xpos, ypos);
+  ensure_child (text_view,
+                &text_view->priv->center_child,
+                GTK_TEXT_WINDOW_TEXT);
 
-  add_child (text_view, vc);
-
-  g_assert (vc->widget == child);
-  g_assert (gtk_widget_get_parent (child) == GTK_WIDGET (text_view));
+  gtk_text_view_child_add_overlay (text_view->priv->center_child,
+                                   child, xpos, ypos);
 }
 
 /**
- * gtk_text_view_move_child:
+ * gtk_text_view_move_overlay:
  * @text_view: a #GtkTextView
- * @child: child widget already added to the text view
- * @xpos: new X position in window coordinates
- * @ypos: new Y position in window coordinates
+ * @child: a widget already added with gtk_text_view_add_overlay()
+ * @xpos: new X position in buffer coordinates
+ * @ypos: new Y position in buffer coordinates
  *
- * Updates the position of a child, as for gtk_text_view_add_child_in_window().
+ * Updates the position of a child, as for gtk_text_view_add_overlay().
  **/
 void
-gtk_text_view_move_child (GtkTextView *text_view,
-                          GtkWidget   *child,
-                          gint         xpos,
-                          gint         ypos)
+gtk_text_view_move_overlay (GtkTextView *text_view,
+                            GtkWidget   *child,
+                            gint         xpos,
+                            gint         ypos)
 {
-  GtkTextViewChild *vc;
-
   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
   g_return_if_fail (GTK_IS_WIDGET (child));
   g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (text_view));
 
-  vc = g_object_get_qdata (G_OBJECT (child), quark_text_view_child);
-
-  g_assert (vc != NULL);
-
-  if (vc->x == xpos &&
-      vc->y == ypos)
-    return;
-  
-  vc->x = xpos;
-  vc->y = ypos;
-
-  if (gtk_widget_get_visible (child) &&
-      gtk_widget_get_visible (GTK_WIDGET (text_view)))
-    gtk_widget_queue_resize (child);
+  if (text_view->priv->center_child == NULL)
+    gtk_text_view_child_move_overlay (text_view->priv->center_child,
+                                      child, xpos, ypos);
 }
 
 
diff --git a/gtk/gtktextview.h b/gtk/gtktextview.h
index 489b5ebf1f..d386aebf70 100644
--- a/gtk/gtktextview.h
+++ b/gtk/gtktextview.h
@@ -44,7 +44,6 @@ G_BEGIN_DECLS
 
 /**
  * GtkTextWindowType:
- * @GTK_TEXT_WINDOW_PRIVATE: Private value, used internally
  * @GTK_TEXT_WINDOW_WIDGET: Window that floats over scrolling areas.
  * @GTK_TEXT_WINDOW_TEXT: Scrollable text window.
  * @GTK_TEXT_WINDOW_LEFT: Left side border window.
@@ -56,8 +55,7 @@ G_BEGIN_DECLS
  */
 typedef enum
 {
-  GTK_TEXT_WINDOW_PRIVATE,
-  GTK_TEXT_WINDOW_WIDGET,
+  GTK_TEXT_WINDOW_WIDGET = 1,
   GTK_TEXT_WINDOW_TEXT,
   GTK_TEXT_WINDOW_LEFT,
   GTK_TEXT_WINDOW_RIGHT,
@@ -280,14 +278,6 @@ void gtk_text_view_window_to_buffer_coords (GtkTextView       *text_view,
                                             gint              *buffer_x,
                                             gint              *buffer_y);
 
-GDK_AVAILABLE_IN_ALL
-void gtk_text_view_set_border_window_size (GtkTextView       *text_view,
-                                           GtkTextWindowType  type,
-                                           gint               size);
-GDK_AVAILABLE_IN_ALL
-gint gtk_text_view_get_border_window_size (GtkTextView       *text_view,
-                                          GtkTextWindowType  type);
-
 GDK_AVAILABLE_IN_ALL
 gboolean gtk_text_view_forward_display_line           (GtkTextView       *text_view,
                                                        GtkTextIter       *iter);
@@ -316,24 +306,28 @@ void            gtk_text_view_reset_im_context                  (GtkTextView
 
 /* Adding child widgets */
 GDK_AVAILABLE_IN_ALL
-void gtk_text_view_add_child_at_anchor (GtkTextView          *text_view,
-                                        GtkWidget            *child,
-                                        GtkTextChildAnchor   *anchor);
+GtkWidget *gtk_text_view_get_gutter          (GtkTextView          *text_view,
+                                              GtkTextWindowType     win);
+GDK_AVAILABLE_IN_ALL
+void       gtk_text_view_set_gutter          (GtkTextView          *text_view,
+                                              GtkTextWindowType     win,
+                                              GtkWidget            *widget);
+GDK_AVAILABLE_IN_ALL
+void       gtk_text_view_add_child_at_anchor (GtkTextView          *text_view,
+                                              GtkWidget            *child,
+                                              GtkTextChildAnchor   *anchor);
 
 GDK_AVAILABLE_IN_ALL
-void gtk_text_view_add_child_in_window (GtkTextView          *text_view,
-                                        GtkWidget            *child,
-                                        GtkTextWindowType     which_window,
-                                        /* window coordinates */
-                                        gint                  xpos,
-                                        gint                  ypos);
+void       gtk_text_view_add_overlay         (GtkTextView          *text_view,
+                                              GtkWidget            *child,
+                                              gint                  xpos,
+                                              gint                  ypos);
 
 GDK_AVAILABLE_IN_ALL
-void gtk_text_view_move_child          (GtkTextView          *text_view,
-                                        GtkWidget            *child,
-                                        /* window coordinates */
-                                        gint                  xpos,
-                                        gint                  ypos);
+void       gtk_text_view_move_overlay        (GtkTextView          *text_view,
+                                              GtkWidget            *child,
+                                              gint                  xpos,
+                                              gint                  ypos);
 
 /* Default style settings (fallbacks if no tag affects the property) */
 
diff --git a/gtk/gtktextviewchild.c b/gtk/gtktextviewchild.c
new file mode 100644
index 0000000000..01ac6256bd
--- /dev/null
+++ b/gtk/gtktextviewchild.c
@@ -0,0 +1,517 @@
+/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
+/* GTK - The GIMP Toolkit
+ * gtk_text_view_child.c Copyright (C) 2019 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkcssnodeprivate.h"
+#include "gtkintl.h"
+#include "gtkprivate.h"
+#include "gtkstylecontext.h"
+#include "gtktextview.h"
+#include "gtktextviewchild.h"
+#include "gtktextviewchildprivate.h"
+#include "gtktypebuiltins.h"
+#include "gtkwidgetprivate.h"
+
+typedef struct
+{
+  GList      link;
+  GtkWidget *widget;
+  int        x;
+  int        y;
+} Overlay;
+
+struct _GtkTextViewChild
+{
+  GtkContainer       parent_instance;
+  GtkTextWindowType  window_type;
+  GQueue             overlays;
+  int                xoffset;
+  int                yoffset;
+  GtkWidget         *child;
+};
+
+enum {
+  PROP_0,
+  PROP_WINDOW_TYPE,
+  N_PROPS
+};
+
+G_DEFINE_TYPE (GtkTextViewChild, gtk_text_view_child, GTK_TYPE_CONTAINER)
+
+static GParamSpec *properties[N_PROPS];
+
+static Overlay *
+overlay_new (GtkWidget *widget,
+             int        x,
+             int        y)
+{
+  Overlay *overlay;
+
+  overlay = g_slice_new0 (Overlay);
+  overlay->link.data = overlay;
+  overlay->widget = g_object_ref (widget);
+  overlay->x = x;
+  overlay->y = y;
+
+  return overlay;
+}
+
+static void
+overlay_free (Overlay *overlay)
+{
+  g_assert (overlay->link.prev == NULL);
+  g_assert (overlay->link.next == NULL);
+
+  g_object_unref (overlay->widget);
+  g_slice_free (Overlay, overlay);
+}
+
+static void
+gtk_text_view_child_remove_overlay (GtkTextViewChild *self,
+                                    Overlay          *overlay)
+{
+  g_queue_unlink (&self->overlays, &overlay->link);
+  gtk_widget_unparent (overlay->widget);
+  overlay_free (overlay);
+}
+
+static Overlay *
+gtk_text_view_child_get_overlay (GtkTextViewChild *self,
+                                 GtkWidget        *widget)
+{
+  GList *iter;
+
+  for (iter = self->overlays.head; iter; iter = iter->next)
+    {
+      Overlay *overlay = iter->data;
+
+      if (overlay->widget == widget)
+        return overlay;
+    }
+
+  return NULL;
+}
+
+static void
+gtk_text_view_child_add (GtkContainer *container,
+                         GtkWidget    *widget)
+{
+  GtkTextViewChild *self = GTK_TEXT_VIEW_CHILD (container);
+
+  if (self->child != NULL)
+    {
+      g_warning ("%s allows a single child and already contains a %s",
+                 G_OBJECT_TYPE_NAME (self),
+                 G_OBJECT_TYPE_NAME (widget));
+      return;
+    }
+
+  self->child = g_object_ref (widget);
+  gtk_widget_set_parent (widget, GTK_WIDGET (self));
+}
+
+static void
+gtk_text_view_child_remove (GtkContainer *container,
+                            GtkWidget    *widget)
+{
+  GtkTextViewChild *self = GTK_TEXT_VIEW_CHILD (container);
+
+  if (widget == self->child)
+    {
+      self->child = NULL;
+      gtk_widget_unparent (widget);
+      g_object_unref (widget);
+    }
+  else
+    {
+      Overlay *overlay = gtk_text_view_child_get_overlay (self, widget);
+
+      if (overlay != NULL)
+        gtk_text_view_child_remove_overlay (self, overlay);
+    }
+}
+
+static void
+gtk_text_view_child_forall (GtkContainer *container,
+                            GtkCallback   callback,
+                            gpointer      callback_data)
+{
+  GtkTextViewChild *self = GTK_TEXT_VIEW_CHILD (container);
+  const GList *iter;
+
+  if (self->child != NULL)
+    callback (self->child, callback_data);
+
+  iter = self->overlays.head;
+  while (iter != NULL)
+    {
+      Overlay *overlay = iter->data;
+      iter = iter->next;
+      callback (overlay->widget, callback_data);
+    }
+}
+
+static void
+gtk_text_view_child_measure (GtkWidget      *widget,
+                             GtkOrientation  orientation,
+                             int             for_size,
+                             int            *min_size,
+                             int            *nat_size,
+                             int            *min_baseline,
+                             int            *nat_baseline)
+{
+  GtkTextViewChild *self = GTK_TEXT_VIEW_CHILD (widget);
+  const GList *iter;
+  int real_min_size = 0;
+  int real_nat_size = 0;
+
+  if (self->child != NULL)
+    gtk_widget_measure (self->child,
+                        orientation,
+                        for_size,
+                        &real_min_size,
+                        &real_nat_size,
+                        NULL,
+                        NULL);
+
+  for (iter = self->overlays.head; iter; iter = iter->next)
+    {
+      Overlay *overlay = iter->data;
+      int child_min_size = 0;
+      int child_nat_size = 0;
+
+      gtk_widget_measure (overlay->widget,
+                          orientation,
+                          for_size,
+                          &child_min_size,
+                          &child_nat_size,
+                          NULL,
+                          NULL);
+
+      if (child_min_size > real_min_size)
+        real_min_size = child_min_size;
+
+      if (child_nat_size > real_nat_size)
+        real_nat_size = child_nat_size;
+    }
+
+  if (min_size)
+    *min_size = real_min_size;
+
+  if (nat_size)
+    *nat_size = real_nat_size;
+
+  if (min_baseline)
+    *min_baseline = -1;
+
+  if (nat_baseline)
+    *nat_baseline = -1;
+}
+
+static void
+gtk_text_view_child_size_allocate (GtkWidget *widget,
+                                   int        width,
+                                   int        height,
+                                   int        baseline)
+{
+  GtkTextViewChild *self = GTK_TEXT_VIEW_CHILD (widget);
+  GtkRequisition min_req;
+  GdkRectangle rect;
+  const GList *iter;
+
+  GTK_WIDGET_CLASS (gtk_text_view_child_parent_class)->size_allocate (widget, width, height, baseline);
+
+  if (self->child != NULL)
+    {
+      rect.x = 0;
+      rect.y = 0;
+      rect.width = width;
+      rect.height = height;
+
+      gtk_widget_size_allocate (self->child, &rect, baseline);
+    }
+
+  for (iter = self->overlays.head; iter; iter = iter->next)
+    {
+      Overlay *overlay = iter->data;
+
+      gtk_widget_get_preferred_size (overlay->widget, &min_req, NULL);
+
+      rect.width = min_req.width;
+      rect.height = min_req.height;
+
+      if (self->window_type == GTK_TEXT_WINDOW_TEXT ||
+          self->window_type == GTK_TEXT_WINDOW_TOP ||
+          self->window_type == GTK_TEXT_WINDOW_BOTTOM)
+        rect.x = overlay->x - self->xoffset;
+      else
+        rect.x = overlay->x;
+
+      if (self->window_type == GTK_TEXT_WINDOW_TEXT ||
+          self->window_type == GTK_TEXT_WINDOW_RIGHT ||
+          self->window_type == GTK_TEXT_WINDOW_LEFT)
+        rect.y = overlay->y - self->yoffset;
+      else
+        rect.y = overlay->y;
+
+      gtk_widget_size_allocate (overlay->widget, &rect, -1);
+    }
+}
+
+static void
+gtk_text_view_child_snapshot (GtkWidget   *widget,
+                              GtkSnapshot *snapshot)
+{
+  GtkTextViewChild *self = GTK_TEXT_VIEW_CHILD (widget);
+  const GList *iter;
+
+  GTK_WIDGET_CLASS (gtk_text_view_child_parent_class)->snapshot (widget, snapshot);
+
+  if (self->child)
+    gtk_widget_snapshot_child (widget, self->child, snapshot);
+
+  for (iter = self->overlays.head; iter; iter = iter->next)
+    {
+      Overlay *overlay = iter->data;
+
+      gtk_widget_snapshot_child (widget, overlay->widget, snapshot);
+    }
+}
+
+static void
+gtk_text_view_child_constructed (GObject *object)
+{
+  GtkTextViewChild *self = GTK_TEXT_VIEW_CHILD (object);
+  GtkCssNode *css_node;
+
+  G_OBJECT_CLASS (gtk_text_view_child_parent_class)->constructed (object);
+
+  css_node = gtk_widget_get_css_node (GTK_WIDGET (self));
+
+  switch (self->window_type)
+    {
+    case GTK_TEXT_WINDOW_LEFT:
+      gtk_css_node_set_name (css_node, "border");
+      gtk_css_node_add_class (css_node, g_quark_from_static_string (GTK_STYLE_CLASS_LEFT));
+      break;
+
+    case GTK_TEXT_WINDOW_RIGHT:
+      gtk_css_node_set_name (css_node, "border");
+      gtk_css_node_add_class (css_node, g_quark_from_static_string (GTK_STYLE_CLASS_RIGHT));
+      break;
+
+    case GTK_TEXT_WINDOW_TOP:
+      gtk_css_node_set_name (css_node, "border");
+      gtk_css_node_add_class (css_node, g_quark_from_static_string (GTK_STYLE_CLASS_TOP));
+      break;
+
+    case GTK_TEXT_WINDOW_BOTTOM:
+      gtk_css_node_set_name (css_node, "border");
+      gtk_css_node_add_class (css_node, g_quark_from_static_string (GTK_STYLE_CLASS_BOTTOM));
+      break;
+
+    case GTK_TEXT_WINDOW_TEXT:
+      gtk_css_node_set_name (css_node, "text");
+      break;
+
+    case GTK_TEXT_WINDOW_WIDGET:
+    default:
+      break;
+    }
+}
+
+static void
+gtk_text_view_child_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  GtkTextViewChild *self = GTK_TEXT_VIEW_CHILD (object);
+
+  switch (prop_id)
+    {
+    case PROP_WINDOW_TYPE:
+      g_value_set_enum (value, self->window_type);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_text_view_child_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  GtkTextViewChild *self = GTK_TEXT_VIEW_CHILD (object);
+
+  switch (prop_id)
+    {
+    case PROP_WINDOW_TYPE:
+      self->window_type = g_value_get_enum (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_text_view_child_class_init (GtkTextViewChildClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+  object_class->constructed = gtk_text_view_child_constructed;
+  object_class->get_property = gtk_text_view_child_get_property;
+  object_class->set_property = gtk_text_view_child_set_property;
+
+  widget_class->measure = gtk_text_view_child_measure;
+  widget_class->size_allocate = gtk_text_view_child_size_allocate;
+  widget_class->snapshot = gtk_text_view_child_snapshot;
+
+  container_class->add = gtk_text_view_child_add;
+  container_class->remove = gtk_text_view_child_remove;
+  container_class->forall = gtk_text_view_child_forall;
+
+  /**
+   * GtkTextViewChild:window-type:
+   *
+   * The "window-type" property is the #GtkTextWindowType of the
+   * #GtkTextView that the child is attached.
+   */
+  properties[PROP_WINDOW_TYPE] =
+    g_param_spec_enum ("window-type",
+                       P_("Window Type"),
+                       P_("The GtkTextWindowType"),
+                       GTK_TYPE_TEXT_WINDOW_TYPE,
+                       GTK_TEXT_WINDOW_TEXT,
+                       GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_EXPLICIT_NOTIFY);
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gtk_text_view_child_init (GtkTextViewChild *self)
+{
+  self->window_type = GTK_TEXT_WINDOW_TEXT;
+
+  gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN);
+}
+
+GtkWidget *
+gtk_text_view_child_new (GtkTextWindowType window_type)
+{
+  g_return_val_if_fail (window_type == GTK_TEXT_WINDOW_LEFT ||
+                        window_type == GTK_TEXT_WINDOW_RIGHT ||
+                        window_type == GTK_TEXT_WINDOW_TOP ||
+                        window_type == GTK_TEXT_WINDOW_BOTTOM ||
+                        window_type == GTK_TEXT_WINDOW_TEXT,
+                        NULL);
+
+  return g_object_new (GTK_TYPE_TEXT_VIEW_CHILD,
+                       "window-type", window_type,
+                       NULL);
+}
+
+void
+gtk_text_view_child_add_overlay (GtkTextViewChild *self,
+                                 GtkWidget        *widget,
+                                 int               xpos,
+                                 int               ypos)
+{
+  Overlay *overlay;
+
+  g_return_if_fail (GTK_IS_TEXT_VIEW_CHILD (self));
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  overlay = overlay_new (widget, xpos, ypos);
+  g_queue_push_tail (&self->overlays, &overlay->link);
+  gtk_widget_set_parent (widget, GTK_WIDGET (self));
+}
+
+void
+gtk_text_view_child_move_overlay (GtkTextViewChild *self,
+                                  GtkWidget        *widget,
+                                  int               xpos,
+                                  int               ypos)
+{
+  Overlay *overlay;
+
+  g_return_if_fail (GTK_IS_TEXT_VIEW_CHILD (self));
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  overlay = gtk_text_view_child_get_overlay (self, widget);
+
+  if (overlay != NULL)
+    {
+      overlay->x = xpos;
+      overlay->y = ypos;
+
+      if (gtk_widget_get_visible (GTK_WIDGET (self)) &&
+          gtk_widget_get_visible (widget))
+        gtk_widget_queue_allocate (GTK_WIDGET (self));
+    }
+}
+
+GtkTextWindowType
+gtk_text_view_child_get_window_type (GtkTextViewChild *self)
+{
+  g_return_val_if_fail (GTK_IS_TEXT_VIEW_CHILD (self), 0);
+
+  return self->window_type;
+}
+
+void
+_gtk_text_view_child_set_offset (GtkTextViewChild *self,
+                                 int               xoffset,
+                                 int               yoffset)
+{
+  gboolean changed = FALSE;
+
+  g_return_if_fail (GTK_IS_TEXT_VIEW_CHILD (self));
+
+  if (self->window_type == GTK_TEXT_WINDOW_TEXT ||
+      self->window_type == GTK_TEXT_WINDOW_TOP ||
+      self->window_type == GTK_TEXT_WINDOW_BOTTOM)
+    {
+      if (self->xoffset != xoffset)
+        {
+          self->xoffset = xoffset;
+          changed = TRUE;
+        }
+    }
+
+  if (self->window_type == GTK_TEXT_WINDOW_TEXT ||
+      self->window_type == GTK_TEXT_WINDOW_LEFT ||
+      self->window_type == GTK_TEXT_WINDOW_RIGHT)
+    {
+      if (self->yoffset != yoffset)
+        {
+          self->yoffset = yoffset;
+          changed = TRUE;
+        }
+    }
+
+  if (changed)
+    gtk_widget_queue_draw (GTK_WIDGET (self));
+}
diff --git a/gtk/gtktextviewchild.h b/gtk/gtktextviewchild.h
new file mode 100644
index 0000000000..4328d9c5b5
--- /dev/null
+++ b/gtk/gtktextviewchild.h
@@ -0,0 +1,48 @@
+/* GTK - The GIMP Toolkit
+ * gtktextviewchild.h Copyright (C) 2019 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_TEXT_VIEW_CHILD_H__
+#define __GTK_TEXT_VIEW_CHILD_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtktextview.h>
+
+#define GTK_TYPE_TEXT_VIEW_CHILD (gtk_text_view_child_get_type())
+
+G_GNUC_INTERNAL
+G_DECLARE_FINAL_TYPE (GtkTextViewChild, gtk_text_view_child, GTK, TEXT_VIEW_CHILD, GtkContainer)
+
+G_GNUC_INTERNAL
+GtkWidget         *gtk_text_view_child_new             (GtkTextWindowType  window_type);
+G_GNUC_INTERNAL
+GtkTextWindowType  gtk_text_view_child_get_window_type (GtkTextViewChild *self);
+G_GNUC_INTERNAL
+void               gtk_text_view_child_add_overlay     (GtkTextViewChild  *self,
+                                                        GtkWidget         *widget,
+                                                        int                xpos,
+                                                        int                ypos);
+G_GNUC_INTERNAL
+void               gtk_text_view_child_move_overlay    (GtkTextViewChild  *self,
+                                                        GtkWidget         *widget,
+                                                        int                xpos,
+                                                        int                ypos);
+
+#endif /* __GTK_TEXT_VIEW_CHILD_H__ */
diff --git a/gtk/gtktextviewchildprivate.h b/gtk/gtktextviewchildprivate.h
new file mode 100644
index 0000000000..5a04e17f2c
--- /dev/null
+++ b/gtk/gtktextviewchildprivate.h
@@ -0,0 +1,27 @@
+/* GTK - The GIMP Toolkit
+ * gtktextviewchild-private.h Copyright (C) 2019 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_TEXT_VIEW_CHILD_PRIVATE_H__
+#define __GTK_TEXT_VIEW_CHILD_PRIVATE_H__
+
+#include "gtktextviewchild.h"
+
+void _gtk_text_view_child_set_offset (GtkTextViewChild *child,
+                                      int               xoffset,
+                                      int               yoffset);
+
+#endif /* __GTK_TEXT_VIEW_CHILD_PRIVATE_H__ */
diff --git a/gtk/meson.build b/gtk/meson.build
index 0cb3fbb9af..ae399247b6 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -145,6 +145,7 @@ gtk_private_sources = files([
   'gtkstylecascade.c',
   'gtkstyleproperty.c',
   'gtktextbtree.c',
+  'gtktextviewchild.c',
   'gtktrashmonitor.c',
   'gtktreedatalist.c',
 ])
diff --git a/tests/testtextview.c b/tests/testtextview.c
index d746ec2419..c0458e2dd9 100644
--- a/tests/testtextview.c
+++ b/tests/testtextview.c
@@ -193,10 +193,9 @@ main (int argc, char **argv)
 
   gtk_container_add (GTK_CONTAINER (window), sw);
   gtk_container_add (GTK_CONTAINER (sw), textview);
-  gtk_text_view_add_child_in_window (GTK_TEXT_VIEW (textview),
-                                     button,
-                                     GTK_TEXT_WINDOW_TEXT,
-                                     50, 150);
+  gtk_text_view_add_overlay (GTK_TEXT_VIEW (textview),
+                             button,
+                             50, 150);
 
   gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (textview),
                                      button2, anchor);
diff --git a/testsuite/reftests/textview-border-windows.c b/testsuite/reftests/textview-border-windows.c
index 019b654907..7a6646393a 100644
--- a/testsuite/reftests/textview-border-windows.c
+++ b/testsuite/reftests/textview-border-windows.c
@@ -17,12 +17,23 @@
 
 #include <gtk/gtk.h>
 
+static void
+set_border_window_size (GtkTextView       *text_view,
+                        GtkTextWindowType  win,
+                        gint               size)
+{
+  GtkWidget *label;
+
+  label = gtk_label_new (NULL);
+  gtk_widget_set_size_request (label, size, size);
+  gtk_text_view_set_gutter (text_view, win, label);
+}
 
 G_MODULE_EXPORT void
 add_border_windows (GtkTextView *text_view)
 {
-  gtk_text_view_set_border_window_size (text_view, GTK_TEXT_WINDOW_LEFT, 30);
-  gtk_text_view_set_border_window_size (text_view, GTK_TEXT_WINDOW_RIGHT, 30);
-  gtk_text_view_set_border_window_size (text_view, GTK_TEXT_WINDOW_TOP, 30);
-  gtk_text_view_set_border_window_size (text_view, GTK_TEXT_WINDOW_BOTTOM, 30);
+  set_border_window_size (text_view, GTK_TEXT_WINDOW_LEFT, 30);
+  set_border_window_size (text_view, GTK_TEXT_WINDOW_RIGHT, 30);
+  set_border_window_size (text_view, GTK_TEXT_WINDOW_TOP, 30);
+  set_border_window_size (text_view, GTK_TEXT_WINDOW_BOTTOM, 30);
 }


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