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.
This commit is contained in:
Benjamin Otte 2020-06-24 05:02:04 +02:00
parent c2b0330c56
commit 64aa281c97

View File

@ -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);