From 64aa281c9787d0d8af07048e9d72b04b808aa09e Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Wed, 24 Jun 2020 05:02:04 +0200 Subject: [PATCH] listbase: Allocate rubberband according to list coords The rubberband is now handled on the list coordinate system. When starting the rubberband, we track the item under the pointer and follow it when it is moving. This may lead to the rubberband start position changing position and while this may be confusing, it alerts users to the fact that something crazy is going on. --- gtk/gtklistbase.c | 130 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 96 insertions(+), 34 deletions(-) diff --git a/gtk/gtklistbase.c b/gtk/gtklistbase.c index 70036eedf3..2568b0604e 100644 --- a/gtk/gtklistbase.c +++ b/gtk/gtklistbase.c @@ -41,24 +41,18 @@ typedef struct _RubberbandData RubberbandData; struct _RubberbandData { - GtkWidget *widget; + GtkWidget *widget; /* The rubberband widget */ + + GtkListItemTracker *start_tracker; /* The item we started dragging on */ + double start_align_across; /* alignment in horizontal direction */ + double start_align_along; /* alignment in vertical direction */ + GtkBitset *active; - double x1, y1; - double x2, y2; + double pointer_x, pointer_y; /* mouse coordinates in widget space */ gboolean modify; gboolean extend; }; -static void -rubberband_data_free (gpointer data) -{ - RubberbandData *rdata = data; - - g_clear_pointer (&rdata->widget, gtk_widget_unparent); - g_clear_pointer (&rdata->active, gtk_bitset_unref); - g_free (rdata); -} - typedef struct _GtkListBasePrivate GtkListBasePrivate; struct _GtkListBasePrivate @@ -1387,30 +1381,82 @@ gtk_list_base_size_allocate_child (GtkListBase *self, gtk_widget_size_allocate (child, &child_allocation, -1); } +static void +gtk_list_base_widget_to_list (GtkListBase *self, + double x_widget, + double y_widget, + int *across_out, + int *along_out) +{ + GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self); + GtkWidget *widget = GTK_WIDGET (self); + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + x_widget = gtk_widget_get_width (widget) - x_widget; + + gtk_list_base_get_adjustment_values (self, OPPOSITE_ORIENTATION (priv->orientation), across_out, NULL, NULL); + gtk_list_base_get_adjustment_values (self, priv->orientation, along_out, NULL, NULL); + + if (priv->orientation == GTK_ORIENTATION_VERTICAL) + { + *across_out += x_widget; + *along_out += y_widget; + } + else + { + *across_out += y_widget; + *along_out += x_widget; + } +} + void gtk_list_base_allocate_rubberband (GtkListBase *self) { GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self); - GdkRectangle rect; - double x, y; - int min, nat; + GtkRequisition min_size; + int x1, x2, y1, y2; + int offset_x, offset_y; if (!priv->rubberband) return; - gtk_widget_measure (priv->rubberband->widget, - GTK_ORIENTATION_HORIZONTAL, -1, - &min, &nat, NULL, NULL); + if (priv->rubberband->start_tracker == NULL) + { + x1 = 0; + y1 = 0; + } + else + { + guint pos = gtk_list_item_tracker_get_position (priv->item_manager, priv->rubberband->start_tracker); - x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]); - y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]); + if (gtk_list_base_get_allocation_along (self, pos, &y1, &y2) && + gtk_list_base_get_allocation_across (self, pos, &x1, &x2)) + { + x1 += x2 * priv->rubberband->start_align_across; + y1 += y2 * priv->rubberband->start_align_along; + } + else + { + x1 = 0; + y1 = 0; + } + } - rect.x = MIN (priv->rubberband->x1, priv->rubberband->x2) - x; - rect.y = MIN (priv->rubberband->y1, priv->rubberband->y2) - y; - rect.width = ABS (priv->rubberband->x1 - priv->rubberband->x2) + 1; - rect.height = ABS (priv->rubberband->y1 - priv->rubberband->y2) + 1; + gtk_list_base_widget_to_list (self, + priv->rubberband->pointer_x, priv->rubberband->pointer_y, + &x2, &y2); - gtk_widget_size_allocate (priv->rubberband->widget, &rect, -1); + gtk_widget_get_preferred_size (priv->rubberband->widget, &min_size, NULL); + + gtk_list_base_get_adjustment_values (self, OPPOSITE_ORIENTATION (priv->orientation), &offset_x, NULL, NULL); + gtk_list_base_get_adjustment_values (self, priv->orientation, &offset_y, NULL, NULL); + + gtk_list_base_size_allocate_child (self, + priv->rubberband->widget, + MIN (x1, x2) - offset_x, + MIN (y1, y2) - offset_y, + MAX (min_size.width, ABS (x1 - x2) + 1), + MAX (min_size.height, ABS (y1 - y2) + 1)); } static void @@ -1421,18 +1467,29 @@ gtk_list_base_start_rubberband (GtkListBase *self, gboolean extend) { GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self); - double value_x, value_y; + cairo_rectangle_int_t item_area; + int list_x, list_y; + guint pos; if (priv->rubberband) return; - value_x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]); - value_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]); + gtk_list_base_widget_to_list (self, x, y, &list_x, &list_y); + if (!gtk_list_base_get_position_from_allocation (self, list_x, list_y, &pos, &item_area)) + { + g_warning ("Could not start rubberbanding: No item\n"); + return; + } priv->rubberband = g_new0 (RubberbandData, 1); - priv->rubberband->x1 = priv->rubberband->x2 = x + value_x; - priv->rubberband->y1 = priv->rubberband->y2 = y + value_y; + priv->rubberband->start_tracker = gtk_list_item_tracker_new (priv->item_manager); + gtk_list_item_tracker_set_position (priv->item_manager, priv->rubberband->start_tracker, pos, 0, 0); + priv->rubberband->start_align_across = (double) (list_x - item_area.x) / item_area.width; + priv->rubberband->start_align_along = (double) (list_y - item_area.y) / item_area.height; + + priv->rubberband->pointer_x = x; + priv->rubberband->pointer_y = y; priv->rubberband->modify = modify; priv->rubberband->extend = extend; @@ -1487,7 +1544,12 @@ gtk_list_base_stop_rubberband (GtkListBase *self) gtk_bitset_unref (mask); } - g_clear_pointer (&priv->rubberband, rubberband_data_free); + gtk_list_item_tracker_free (priv->item_manager, priv->rubberband->start_tracker); + g_clear_pointer (&priv->rubberband->widget, gtk_widget_unparent); + g_clear_pointer (&priv->rubberband->active, gtk_bitset_unref); + g_free (priv->rubberband); + priv->rubberband = NULL; + remove_autoscroll (self); gtk_widget_queue_draw (GTK_WIDGET (self)); @@ -1503,8 +1565,8 @@ gtk_list_base_update_rubberband (GtkListBase *self, if (!priv->rubberband) return; - priv->rubberband->x2 = x + gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]); - priv->rubberband->y2 = y + gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]); + priv->rubberband->pointer_x = x; + priv->rubberband->pointer_y = y; gtk_list_base_update_rubberband_selection (self);