[gtk/wip/otte/listview] listview: Change anchor handling again
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/otte/listview] listview: Change anchor handling again
- Date: Wed, 26 Sep 2018 00:19:50 +0000 (UTC)
commit 35c41dec2b4e72b2dd9def78b1984762cefac216
Author: Benjamin Otte <otte redhat com>
Date: Tue Sep 25 00:16:27 2018 +0200
listview: Change anchor handling again
The anchor is now a tuple of { listitem, align }.
Using the actual list item allows keeping the anchor across changes
in position (ie when lists get resorted) while still being able to fall
back to positions (list items store their position) when an item gets
removed.
The align value is in the range [0..1] and defines where in the visible
area to do the alignment.
0.0 means to align the top of the row with the top of the visible area,
1.0 aligns the bottom of the widget with the visible area and 0.5 keeps
the center of the widget at the center of the visible area.
It works conceptually the same as percentages in CSS background-position
(where the background area and the background image's size are matched
the same way) or CSS transform-origin.
gtk/gtklistitemmanager.c | 24 +++++++
gtk/gtklistitemmanagerprivate.h | 3 +
gtk/gtklistview.c | 154 +++++++++++++++++++++++++---------------
3 files changed, 122 insertions(+), 59 deletions(-)
---
diff --git a/gtk/gtklistitemmanager.c b/gtk/gtklistitemmanager.c
index 861784ad2e..df0022661f 100644
--- a/gtk/gtklistitemmanager.c
+++ b/gtk/gtklistitemmanager.c
@@ -210,6 +210,30 @@ gtk_list_item_manager_end_change (GtkListItemManager *self,
g_slice_free (GtkListItemManagerChange, change);
}
+/*
+ * gtk_list_item_manager_change_contains:
+ * @change: a #GtkListItemManagerChange
+ * @list_item: The item that may have been released into this change set
+ *
+ * Checks if @list_item has been released as part of @change but not been
+ * reacquired yet.
+ *
+ * This is useful to test before calling gtk_list_item_manager_end_change()
+ * if special actions need to be performed when important list items - like
+ * the focused item - are about to be deleted.
+ *
+ * Returns: %TRUE if the item is part of this change
+ **/
+gboolean
+gtk_list_item_manager_change_contains (GtkListItemManagerChange *change,
+ GtkWidget *list_item)
+{
+ g_return_val_if_fail (change != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_LIST_ITEM (list_item), FALSE);
+
+ return g_hash_table_lookup (change->items, gtk_list_item_get_item (GTK_LIST_ITEM (list_item))) ==
list_item;
+}
+
/*
* gtk_list_item_manager_acquire_list_item:
* @self: a #GtkListItemManager
diff --git a/gtk/gtklistitemmanagerprivate.h b/gtk/gtklistitemmanagerprivate.h
index d1cdfaf819..cf7721bc6c 100644
--- a/gtk/gtklistitemmanagerprivate.h
+++ b/gtk/gtklistitemmanagerprivate.h
@@ -55,6 +55,9 @@ GtkListItemManagerChange *
gtk_list_item_manager_begin_change (GtkListItemManager *self);
void gtk_list_item_manager_end_change (GtkListItemManager *self,
GtkListItemManagerChange *change);
+gboolean gtk_list_item_manager_change_contains (GtkListItemManagerChange *change,
+ GtkWidget *list_item);
+
GtkWidget * gtk_list_item_manager_acquire_list_item (GtkListItemManager *self,
guint position,
GtkWidget *next_sibling);
diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c
index 454ca0ecd7..f287cd669e 100644
--- a/gtk/gtklistview.c
+++ b/gtk/gtklistview.c
@@ -55,10 +55,8 @@ struct _GtkListView
int list_width;
/* managing the visible region */
- guint anchor_pos;
- int anchor_dy;
- guint first_visible_pos;
- guint lasst_visible_pos;
+ GtkWidget *anchor;
+ int anchor_align;
};
struct _ListRow
@@ -275,6 +273,54 @@ gtk_list_view_get_list_height (GtkListView *self)
return aug->height;
}
+static void
+gtk_list_view_set_anchor (GtkListView *self,
+ guint position,
+ double align)
+{
+ ListRow *row;
+
+ g_assert (align >= 0.0 && align <= 1.0);
+
+ row = gtk_list_view_get_row (self, position, NULL);
+ if (row == NULL)
+ {
+ /* like, if the list is empty */
+ self->anchor = NULL;
+ self->anchor_align = 0.0;
+ return;
+ }
+
+ self->anchor = row->widget;
+ self->anchor_align = align;
+
+ gtk_widget_queue_allocate (GTK_WIDGET (self));
+}
+
+static void
+gtk_list_view_adjustment_value_changed_cb (GtkAdjustment *adjustment,
+ GtkListView *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;
+
+ gtk_list_view_set_anchor (self, pos, 0);
+ }
+ else
+ {
+ gtk_widget_queue_allocate (GTK_WIDGET (self));
+ }
+}
+
static void
gtk_list_view_update_adjustments (GtkListView *self,
GtkOrientation orientation)
@@ -297,15 +343,21 @@ gtk_list_view_update_adjustments (GtkListView *self,
page_size = gtk_widget_get_height (GTK_WIDGET (self));
upper = gtk_list_view_get_list_height (self);
- row = gtk_list_view_get_row (self, self->anchor_pos, NULL);
+ if (self->anchor)
+ row = gtk_list_view_get_row (self, gtk_list_item_get_position (GTK_LIST_ITEM (self->anchor)), NULL);
+ else
+ row = NULL;
if (row)
value = list_row_get_y (self, row);
else
value = 0;
- value += self->anchor_dy;
+ value += self->anchor_align * (page_size - (row ? row->height : 0));
}
upper = MAX (upper, page_size);
+ g_signal_handlers_block_by_func (self->adjustment[orientation],
+ gtk_list_view_adjustment_value_changed_cb,
+ self);
gtk_adjustment_configure (self->adjustment[orientation],
value,
0,
@@ -313,6 +365,9 @@ gtk_list_view_update_adjustments (GtkListView *self,
page_size * 0.1,
page_size * 0.9,
page_size);
+ g_signal_handlers_unblock_by_func (self->adjustment[orientation],
+ gtk_list_view_adjustment_value_changed_cb,
+ self);
}
static void
@@ -404,7 +459,7 @@ gtk_list_view_size_allocate (GtkWidget *widget,
/* step 4: actually allocate the widgets */
child_allocation.x = - gtk_adjustment_get_value (self->adjustment[GTK_ORIENTATION_HORIZONTAL]);
- child_allocation.y = - gtk_adjustment_get_value (self->adjustment[GTK_ORIENTATION_VERTICAL]);
+ child_allocation.y = - round (gtk_adjustment_get_value (self->adjustment[GTK_ORIENTATION_VERTICAL]));
child_allocation.width = self->list_width;
for (row = gtk_css_rb_tree_get_first (self->rows);
row != NULL;
@@ -450,6 +505,23 @@ gtk_list_view_pick (GtkWidget *widget,
}
}
+static guint
+gtk_list_view_get_anchor (GtkListView *self,
+ double *align)
+{
+ guint anchor_pos;
+
+ if (self->anchor)
+ anchor_pos = gtk_list_item_get_position (GTK_LIST_ITEM (self->anchor));
+ else
+ anchor_pos = 0;
+
+ if (align)
+ *align = self->anchor_align;
+
+ return anchor_pos;
+}
+
static void
gtk_list_view_remove_rows (GtkListView *self,
GtkListItemManagerChange *change,
@@ -457,24 +529,11 @@ gtk_list_view_remove_rows (GtkListView *self,
guint n_rows)
{
ListRow *row;
- guint i, n_remaining;
+ guint i;
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++)
@@ -496,18 +555,11 @@ gtk_list_view_add_rows (GtkListView *self,
guint n_rows)
{
ListRow *row;
- guint i, n_total;
+ guint i;
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++)
@@ -566,6 +618,15 @@ gtk_list_view_model_items_changed_cb (GListModel *model,
if (removed != added)
gtk_list_view_update_rows (self, position + added);
+ if (gtk_list_item_manager_change_contains (change, self->anchor))
+ {
+ guint anchor_pos = gtk_list_item_get_position (GTK_LIST_ITEM (self->anchor));
+
+ /* removed cannot be NULL or the anchor wouldn't have been removed */
+ anchor_pos = position + (anchor_pos - position) * added / removed;
+ gtk_list_view_set_anchor (self, anchor_pos, self->anchor_align);
+ }
+
gtk_list_item_manager_end_change (self->item_manager, change);
}
@@ -583,35 +644,6 @@ gtk_list_view_clear_model (GtkListView *self)
g_clear_object (&self->model);
}
-static void
-gtk_list_view_adjustment_value_changed_cb (GtkAdjustment *adjustment,
- GtkListView *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
gtk_list_view_clear_adjustment (GtkListView *self,
GtkOrientation orientation)
@@ -889,6 +921,7 @@ gtk_list_view_set_model (GtkListView *self,
self);
gtk_list_view_add_rows (self, NULL, 0, g_list_model_get_n_items (model));
+ gtk_list_view_set_anchor (self, 0, 0);
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
@@ -902,13 +935,15 @@ gtk_list_view_set_functions (GtkListView *self,
GDestroyNotify user_destroy)
{
GtkListItemFactory *factory;
- guint n_items;
+ guint n_items, anchor;
+ double anchor_align;
g_return_if_fail (GTK_IS_LIST_VIEW (self));
g_return_if_fail (setup_func || bind_func);
g_return_if_fail (user_data != NULL || user_destroy == NULL);
n_items = self->model ? g_list_model_get_n_items (self->model) : 0;
+ anchor = gtk_list_view_get_anchor (self, &anchor_align);
gtk_list_view_remove_rows (self, NULL, 0, n_items);
factory = gtk_list_item_factory_new (setup_func, bind_func, user_data, user_destroy);
@@ -916,5 +951,6 @@ gtk_list_view_set_functions (GtkListView *self,
g_object_unref (factory);
gtk_list_view_add_rows (self, NULL, 0, n_items);
+ gtk_list_view_set_anchor (self, anchor, anchor_align);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]