From fea2a82ef6d66d6de8fb13657614534bd6e1f5fc Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 3 Oct 2019 19:21:45 -0700 Subject: [PATCH] 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 | 516 ++++++++ gtk/gtktextviewchildprivate.h | 48 + gtk/meson.build | 1 + tests/testtextview.c | 7 +- testsuite/reftests/textview-border-windows.c | 19 +- 9 files changed, 1094 insertions(+), 747 deletions(-) create mode 100644 gtk/gtktextviewchild.c create mode 100644 gtk/gtktextviewchildprivate.h 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..c82fcf792e 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) + + if (old_buffer != NULL) { - /* Destroy all anchored children */ - GSList *tmp_list; - GSList *copy; - - copy = g_slist_copy (priv->children); - tmp_list = copy; - while (tmp_list != 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) + gtk_text_view_measure_borders (text_view, &borders); + + if (priv->center_child) + gtk_widget_measure (GTK_WIDGET (priv->center_child), + orientation, for_size, + &min, &nat, NULL, NULL); + + for (list = priv->anchored_children.head; list; list = list->next) { - 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; - } + const AnchoredChild *child = list->data; + int child_min = 0; + int child_nat = 0; - nat = min; - - tmp_list = priv->children; - while (tmp_list != NULL) - { - GtkTextViewChild *child = tmp_list->data; - int child_min = 0, 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. */ + 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)) { - /* 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); - } - - gtk_text_layout_validate_yrange (text_view->priv->layout, - &child_loc, - 0, 1); - } - else - { - 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->right_window) - text_window_size_allocate (priv->right_window, - &right_rect); + 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->top_window) - text_window_size_allocate (priv->top_window, - &top_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->bottom_window) - text_window_size_allocate (priv->bottom_window, - &bottom_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_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; - text_view = GTK_TEXT_VIEW (container); - priv = text_view->priv; - - vc = NULL; - iter = priv->children; - - while (iter != NULL) + if (GTK_IS_TEXT_VIEW_CHILD (child)) { - vc = iter->data; + GtkTextViewChild *vc = GTK_TEXT_VIEW_CHILD (child); + GtkTextViewChild **vcp; - if (vc->widget == child) - break; + 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; - iter = iter->next; + if (vcp) + { + *vcp = NULL; + gtk_widget_unparent (child); + g_object_unref (child); + return; + } } - g_assert (iter != NULL); /* be sure we had the child in the list */ + ac = g_object_get_qdata (G_OBJECT (child), quark_text_view_child); - priv->children = g_slist_remove (priv->children, vc); + if (ac == NULL) + { + g_warning ("%s is not a child of %s", + G_OBJECT_TYPE_NAME (child), + G_OBJECT_TYPE_NAME (text_view)); + return; + } - 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..062424d341 --- /dev/null +++ b/gtk/gtktextviewchild.c @@ -0,0 +1,516 @@ +/* -*- 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 . + */ + +#include "config.h" + +#include "gtkcssnodeprivate.h" +#include "gtkintl.h" +#include "gtkprivate.h" +#include "gtkstylecontext.h" +#include "gtktextview.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/gtktextviewchildprivate.h b/gtk/gtktextviewchildprivate.h new file mode 100644 index 0000000000..a7396a48d3 --- /dev/null +++ b/gtk/gtktextviewchildprivate.h @@ -0,0 +1,48 @@ +/* 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 . + */ + +#ifndef __GTK_TEXT_VIEW_CHILD_PRIVATE_H__ +#define __GTK_TEXT_VIEW_CHILD_PRIVATE_H__ + +#include +#include + +#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); +G_GNUC_INTERNAL +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 +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); }