listview: Implement an anchor

The anchor selection is very basic: just anchor the top row.

That's vastly better than any other widget already though.
This commit is contained in:
Benjamin Otte 2018-09-19 07:08:30 +02:00 committed by Matthias Clasen
parent d03a55599b
commit b3c150e929

View File

@ -51,6 +51,12 @@ struct _GtkListView
GtkRbTree *rows;
int list_width;
/* managing the visible region */
guint anchor_pos;
int anchor_dy;
guint first_visible_pos;
guint lasst_visible_pos;
};
struct _ListRow
@ -157,6 +163,112 @@ gtk_list_view_get_row (GtkListView *self,
return row;
}
static guint
list_row_get_position (GtkListView *self,
ListRow *row)
{
ListRow *parent, *left;
int pos;
left = gtk_rb_tree_node_get_left (row);
if (left)
{
ListRowAugment *aug = gtk_rb_tree_get_augment (self->rows, left);
pos = aug->n_rows;
}
else
{
pos = 0;
}
for (parent = gtk_rb_tree_node_get_parent (row);
parent != NULL;
parent = gtk_rb_tree_node_get_parent (row))
{
left = gtk_rb_tree_node_get_left (parent);
if (left != row)
{
if (left)
{
ListRowAugment *aug = gtk_rb_tree_get_augment (self->rows, left);
pos += aug->n_rows;
}
pos += parent->n_rows;
}
row = parent;
}
return pos;
}
static ListRow *
gtk_list_view_get_row_at_y (GtkListView *self,
int y,
int *offset)
{
ListRow *row, *tmp;
row = gtk_rb_tree_get_root (self->rows);
while (row)
{
tmp = gtk_rb_tree_node_get_left (row);
if (tmp)
{
ListRowAugment *aug = gtk_rb_tree_get_augment (self->rows, tmp);
if (y < aug->height)
{
row = tmp;
continue;
}
y -= aug->height;
}
if (y < row->height)
break;
y -= row->height;
row = gtk_rb_tree_node_get_right (row);
}
if (offset)
*offset = row ? y : 0;
return row;
}
static int
list_row_get_y (GtkListView *self,
ListRow *row)
{
ListRow *parent;
int y = 0;
y = 0;
for (parent = gtk_rb_tree_node_get_parent (row);
parent != NULL;
parent = gtk_rb_tree_node_get_parent (row))
{
ListRow *left = gtk_rb_tree_node_get_left (parent);
if (left != row)
{
if (left)
{
ListRowAugment *aug = gtk_rb_tree_get_augment (self->rows, left);
y += aug->height;
}
y += parent->height;
}
row = parent;
}
return y ;
}
static int
gtk_list_view_get_list_height (GtkListView *self)
{
@ -184,15 +296,23 @@ gtk_list_view_update_adjustments (GtkListView *self,
value = 0;
if (_gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL)
value = upper - page_size - value;
value = gtk_adjustment_get_value (self->adjustment[orientation]);
}
else
{
ListRow *row;
page_size = gtk_widget_get_height (GTK_WIDGET (self));
upper = gtk_list_view_get_list_height (self);
value = 0;
row = gtk_list_view_get_row (self, self->anchor_pos, NULL);
if (row)
value = list_row_get_y (self, row);
else
value = 0;
value += self->anchor_dy;
}
upper = MAX (upper, page_size);
value = gtk_adjustment_get_value (self->adjustment[orientation]);
gtk_adjustment_configure (self->adjustment[orientation],
value,
@ -310,11 +430,24 @@ gtk_list_view_remove_rows (GtkListView *self,
guint n_rows)
{
ListRow *row;
guint i;
guint i, n_remaining;
if (n_rows == 0)
return;
n_remaining = self->model ? g_list_model_get_n_items (self->model) : 0;
if (self->anchor_pos >= position + n_rows)
{
self->anchor_pos -= n_rows;
}
else if (self->anchor_pos >= position)
{
self->anchor_pos = position;
if (self->anchor_pos > 0 && self->anchor_pos >= n_remaining)
self->anchor_pos = n_remaining - 1;
self->anchor_dy = 0;
}
row = gtk_list_view_get_row (self, position, NULL);
for (i = 0; i < n_rows; i++)
@ -333,11 +466,18 @@ gtk_list_view_add_rows (GtkListView *self,
guint n_rows)
{
ListRow *row;
guint i;
guint i, n_total;
if (n_rows == 0)
return;
n_total = self->model ? g_list_model_get_n_items (self->model) : 0;
if (self->anchor_pos >= position)
{
if (n_total != n_rows) /* the model was not empty before */
self->anchor_pos += n_rows;
}
row = gtk_list_view_get_row (self, position, NULL);
for (i = 0; i < n_rows; i++)
@ -385,7 +525,29 @@ static void
gtk_list_view_adjustment_value_changed_cb (GtkAdjustment *adjustment,
GtkListView *self)
{
gtk_widget_queue_allocate (GTK_WIDGET (self));
if (adjustment == self->adjustment[GTK_ORIENTATION_VERTICAL])
{
ListRow *row;
guint pos;
int dy;
row = gtk_list_view_get_row_at_y (self, gtk_adjustment_get_value (adjustment), &dy);
if (row)
pos = list_row_get_position (self, row);
else
pos = 0;
if (pos != self->anchor_pos || dy != self->anchor_dy)
{
self->anchor_pos = pos;
self->anchor_dy = dy;
gtk_widget_queue_allocate (GTK_WIDGET (self));
}
}
else
{
gtk_widget_queue_allocate (GTK_WIDGET (self));
}
}
static void