summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2019-10-25 07:39:57 +0200
committerBenjamin Otte <otte@redhat.com>2019-10-28 10:49:02 +0100
commitf2044539ee7cb44bd58474aa546e9ce15994167c (patch)
tree4dd43780183b7b9a11b7a9e042909d61c354aa78
parent165d4e8307cb065b3d8874ef868e92b499fe2aee (diff)
downloadgtk+-f2044539ee7cb44bd58474aa546e9ce15994167c.tar.gz
listbase: Add vfuncs to convert positions to/from coordinates
... and use that to implement PageUp/PageDown. With that, all keyboard handling has been moved to GtkListBase.
-rw-r--r--gtk/gtkgridview.c280
-rw-r--r--gtk/gtklistbase.c296
-rw-r--r--gtk/gtklistbaseprivate.h17
-rw-r--r--gtk/gtklistview.c240
4 files changed, 500 insertions, 333 deletions
diff --git a/gtk/gtkgridview.c b/gtk/gtkgridview.c
index 4633a4972c..43ced73280 100644
--- a/gtk/gtkgridview.c
+++ b/gtk/gtkgridview.c
@@ -21,7 +21,6 @@
#include "gtkgridview.h"
-#include "gtkbindings.h"
#include "gtkintl.h"
#include "gtklistbaseprivate.h"
#include "gtklistitemfactory.h"
@@ -276,35 +275,46 @@ gtk_grid_view_get_cell_at_y (GtkGridView *self,
return cell;
}
-/*<private>
- * gtk_grid_view_get_size_at_position:
- * @self: a #GtkGridView
- * @position: position of the item
- * @offset: (out caller-allocates) (optional): stores the y coordinate
- * of the cell (x coordinate for horizontal grids)
- * @size: (out caller-allocates) (optional): stores the height
- * of the cell (width for horizontal grids)
- *
- * Computes where the cell at @position is allocated.
- *
- * If position is larger than the number of items, %FALSE will be returned.
- * In particular that means that for an emtpy grid, %FALSE is returned
- * for any value.
- *
- * Returns: (nullable): %TRUE if the cell existed, %FALSE otherwise
- **/
+static void
+gtk_grid_view_set_anchor (GtkGridView *self,
+ guint position,
+ double xalign,
+ gboolean xstart,
+ double yalign,
+ gboolean ystart)
+{
+ gtk_list_item_tracker_set_position (self->item_manager,
+ self->anchor,
+ position,
+ (ceil (GTK_GRID_VIEW_MAX_VISIBLE_ROWS * yalign) + 1) * self->max_columns,
+ (ceil (GTK_GRID_VIEW_MAX_VISIBLE_ROWS * (1 - yalign)) + 1) * self->max_columns);
+
+ if (self->anchor_xalign != xalign ||
+ self->anchor_xstart != xstart ||
+ self->anchor_yalign != yalign ||
+ self->anchor_ystart != ystart)
+ {
+ self->anchor_xalign = xalign;
+ self->anchor_xstart = xstart;
+ self->anchor_yalign = yalign;
+ self->anchor_ystart = ystart;
+ gtk_widget_queue_allocate (GTK_WIDGET (self));
+ }
+}
+
static gboolean
-gtk_grid_view_get_size_at_position (GtkGridView *self,
- guint position,
+gtk_grid_view_get_allocation_along (GtkListBase *base,
+ guint pos,
int *offset,
int *size)
{
+ GtkGridView *self = GTK_GRID_VIEW (base);
Cell *cell, *tmp;
int y;
cell = gtk_list_item_manager_get_root (self->item_manager);
y = 0;
- position -= position % self->n_columns;
+ pos -= pos % self->n_columns;
while (cell)
{
@@ -312,19 +322,19 @@ gtk_grid_view_get_size_at_position (GtkGridView *self,
if (tmp)
{
CellAugment *aug = gtk_list_item_manager_get_item_augment (self->item_manager, tmp);
- if (position < aug->parent.n_items)
+ if (pos < aug->parent.n_items)
{
cell = tmp;
continue;
}
- position -= aug->parent.n_items;
+ pos -= aug->parent.n_items;
y += aug->size;
}
- if (position < cell->parent.n_items)
+ if (pos < cell->parent.n_items)
break;
y += cell->size;
- position -= cell->parent.n_items;
+ pos -= cell->parent.n_items;
cell = gtk_rb_tree_node_get_right (cell);
}
@@ -347,15 +357,15 @@ gtk_grid_view_get_size_at_position (GtkGridView *self,
guint skip;
/* skip remaining items at end of row */
- if (position % self->n_columns)
+ if (pos % self->n_columns)
{
- skip = position % self->n_columns;
+ skip = pos % self->n_columns;
n_items -= skip;
- position -= skip;
+ pos -= skip;
}
/* Skip all the rows this index doesn't go into */
- skip = position / self->n_columns;
+ skip = pos / self->n_columns;
n_items -= skip * self->n_columns;
y += skip * self->unknown_row_height;
@@ -373,31 +383,68 @@ gtk_grid_view_get_size_at_position (GtkGridView *self,
return TRUE;
}
-static void
-gtk_grid_view_set_anchor (GtkGridView *self,
- guint position,
- double xalign,
- gboolean xstart,
- double yalign,
- gboolean ystart)
+static gboolean
+gtk_grid_view_get_allocation_across (GtkListBase *base,
+ guint pos,
+ int *offset,
+ int *size)
{
- gtk_list_item_tracker_set_position (self->item_manager,
- self->anchor,
- position,
- (ceil (GTK_GRID_VIEW_MAX_VISIBLE_ROWS * yalign) + 1) * self->max_columns,
- (ceil (GTK_GRID_VIEW_MAX_VISIBLE_ROWS * (1 - yalign)) + 1) * self->max_columns);
+ GtkGridView *self = GTK_GRID_VIEW (base);
+ guint start;
- if (self->anchor_xalign != xalign ||
- self->anchor_xstart != xstart ||
- self->anchor_yalign != yalign ||
- self->anchor_ystart != ystart)
+ pos %= self->n_columns;
+ start = ceil (self->column_width * pos);
+
+ if (offset)
+ *offset = start;
+ if (size)
+ *size = ceil (self->column_width * (pos + 1)) - start;
+
+ return TRUE;
+}
+
+static gboolean
+gtk_grid_view_get_position_from_allocation (GtkListBase *base,
+ int across,
+ int along,
+ guint *position,
+ cairo_rectangle_int_t *area)
+{
+ GtkGridView *self = GTK_GRID_VIEW (base);
+ int offset, size;
+ guint pos, n_items;
+
+ if (across >= self->column_width * self->n_columns)
+ return FALSE;
+
+ n_items = self->model ? g_list_model_get_n_items (self->model) : 0;
+ if (!gtk_grid_view_get_cell_at_y (self,
+ along,
+ &pos,
+ &offset,
+ &size))
+ return FALSE;
+
+ pos += floor (across / self->column_width);
+
+ if (pos >= n_items)
{
- self->anchor_xalign = xalign;
- self->anchor_xstart = xstart;
- self->anchor_yalign = yalign;
- self->anchor_ystart = ystart;
- gtk_widget_queue_allocate (GTK_WIDGET (self));
+ /* Ugh, we're in the last row and don't have enough items
+ * to fill the row.
+ * Do it the hard way then... */
+ pos = n_items - 1;
+ }
+
+ *position = pos;
+ if (area)
+ {
+ area->x = ceil (self->column_width * (pos % self->n_columns));
+ area->width = ceil (self->column_width * (1 + pos % self->n_columns)) - area->x;
+ area->y = along - offset;
+ area->height = size;
}
+
+ return TRUE;
}
static guint
@@ -589,7 +636,7 @@ gtk_grid_view_update_adjustment (GtkGridView *self,
cell = gtk_list_item_manager_get_root (self->item_manager);
g_assert (cell);
aug = gtk_list_item_manager_get_item_augment (self->item_manager, cell);
- if (!gtk_grid_view_get_size_at_position (self,
+ if (!gtk_list_base_get_allocation_along (GTK_LIST_BASE (self),
anchor_pos,
&value,
&cell_size))
@@ -1191,7 +1238,7 @@ gtk_grid_view_scroll_to_item (GtkWidget *widget,
g_variant_get (parameter, "u", &pos);
/* figure out primary orientation and if position is valid */
- if (!gtk_grid_view_get_size_at_position (self, pos, &start, &end))
+ if (!gtk_list_base_get_allocation_along (GTK_LIST_BASE (self), pos, &start, &end))
return;
end += start;
@@ -1240,136 +1287,20 @@ gtk_grid_view_activate_item (GtkWidget *widget,
}
static void
-gtk_grid_view_move_cursor_page_up (GtkWidget *widget,
- GVariant *args,
- gpointer unused)
-{
- GtkGridView *self = GTK_GRID_VIEW (widget);
- gboolean select, modify, extend;
- int offset, start, size, page_size;
- guint pos, new_pos;
-
- pos = gtk_list_item_tracker_get_position (self->item_manager, self->anchor);
- if (pos < self->n_columns) /* already on first row */
- return;
- if (!gtk_grid_view_get_size_at_position (self, pos, &start, &size))
- return;
- gtk_list_base_get_adjustment_values (GTK_LIST_BASE (self),
- gtk_list_base_get_orientation (GTK_LIST_BASE (self)),
- NULL, NULL, &page_size);
- if (!gtk_grid_view_get_cell_at_y (self,
- MAX (0, start + size - page_size),
- &new_pos,
- &offset,
- NULL))
- return;
- /* gtk_grid_view_get_cell_at_y() returns first column positions, we want to keep columns */
- new_pos += pos % self->n_columns;
- if (offset > 0)
- new_pos += self->n_columns;
- if (new_pos >= pos)
- new_pos = pos - self->n_columns;
-
- g_variant_get (args, "(bbb)", &select, &modify, &extend);
-
- gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), new_pos, select, modify, extend);
-}
-
-static void
-gtk_grid_view_move_cursor_page_down (GtkWidget *widget,
- GVariant *args,
- gpointer unused)
-{
- GtkGridView *self = GTK_GRID_VIEW (widget);
- gboolean select, modify, extend;
- int start, page_size;
- guint pos, new_pos, n_items;
-
- pos = gtk_list_item_tracker_get_position (self->item_manager, self->anchor);
- n_items = g_list_model_get_n_items (self->model);
- if (n_items == 0 || pos / self->n_columns >= (n_items - 1) / self->n_columns)
- return;
- if (!gtk_grid_view_get_size_at_position (self, pos, &start, NULL))
- return;
- gtk_list_base_get_adjustment_values (GTK_LIST_BASE (self),
- gtk_list_base_get_orientation (GTK_LIST_BASE (self)),
- NULL, NULL, &page_size);
- if (gtk_grid_view_get_cell_at_y (self,
- start + page_size,
- &new_pos,
- NULL, NULL))
- {
- /* We want a fully visible row and we just checked for the row that covers the
- * pixels more than a page down */
- if (new_pos >= self->n_columns)
- new_pos -= self->n_columns;
- }
- else
- {
- /* scroll to last row if there's nothing in the place we checked */
- new_pos = (n_items - 1);
- new_pos -= new_pos % self->n_columns;
- }
-
- /* gtk_grid_view_get_cell_at_y() returns first column positions, we want to keep columns */
- new_pos += pos % self->n_columns;
- /* We want to scroll at least one row */
- if (new_pos <= pos)
- new_pos = pos + self->n_columns;
- /* And finally, we need to check we've not scrolled to a cell in the last row that isn't
- * covered because n_items is not a multiple of n_columns */
- if (new_pos >= n_items)
- new_pos = n_items - 1;
-
- g_variant_get (args, "(bbb)", &select, &modify, &extend);
-
- gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), new_pos, select, modify, extend);
-}
-
-static void
-gtk_grid_view_add_custom_move_binding (GtkBindingSet *binding_set,
- guint keyval,
- GtkBindingCallback callback)
-{
- gtk_binding_entry_add_callback (binding_set,
- keyval,
- 0,
- callback,
- g_variant_new ("(bbb)", TRUE, FALSE, FALSE),
- NULL, NULL);
- gtk_binding_entry_add_callback (binding_set,
- keyval,
- GDK_CONTROL_MASK,
- callback,
- g_variant_new ("(bbb)", FALSE, FALSE, FALSE),
- NULL, NULL);
- gtk_binding_entry_add_callback (binding_set,
- keyval,
- GDK_SHIFT_MASK,
- callback,
- g_variant_new ("(bbb)", TRUE, FALSE, TRUE),
- NULL, NULL);
- gtk_binding_entry_add_callback (binding_set,
- keyval,
- GDK_CONTROL_MASK | GDK_SHIFT_MASK,
- callback,
- g_variant_new ("(bbb)", TRUE, TRUE, TRUE),
- NULL, NULL);
-}
-
-static void
gtk_grid_view_class_init (GtkGridViewClass *klass)
{
GtkListBaseClass *list_base_class = GTK_LIST_BASE_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GtkBindingSet *binding_set;
list_base_class->list_item_name = "flowboxchild";
list_base_class->list_item_size = sizeof (Cell);
list_base_class->list_item_augment_size = sizeof (CellAugment);
list_base_class->list_item_augment_func = cell_augment;
list_base_class->adjustment_value_changed = gtk_grid_view_adjustment_value_changed;
+ list_base_class->get_allocation_along = gtk_grid_view_get_allocation_along;
+ list_base_class->get_allocation_across = gtk_grid_view_get_allocation_across;
+ list_base_class->get_position_from_allocation = gtk_grid_view_get_position_from_allocation;
list_base_class->move_focus_along = gtk_grid_view_move_focus_along;
list_base_class->move_focus_across = gtk_grid_view_move_focus_across;
@@ -1482,13 +1413,6 @@ gtk_grid_view_class_init (GtkGridViewClass *klass)
"u",
gtk_grid_view_scroll_to_item);
- binding_set = gtk_binding_set_by_class (klass);
-
- gtk_grid_view_add_custom_move_binding (binding_set, GDK_KEY_Page_Up, gtk_grid_view_move_cursor_page_up);
- gtk_grid_view_add_custom_move_binding (binding_set, GDK_KEY_KP_Page_Up, gtk_grid_view_move_cursor_page_up);
- gtk_grid_view_add_custom_move_binding (binding_set, GDK_KEY_Page_Down, gtk_grid_view_move_cursor_page_down);
- gtk_grid_view_add_custom_move_binding (binding_set, GDK_KEY_KP_Page_Down, gtk_grid_view_move_cursor_page_down);
-
gtk_widget_class_set_css_name (widget_class, I_("flowbox"));
}
diff --git a/gtk/gtklistbase.c b/gtk/gtklistbase.c
index 593e816e1f..8e07b6c8b8 100644
--- a/gtk/gtklistbase.c
+++ b/gtk/gtklistbase.c
@@ -157,8 +157,83 @@ gtk_list_base_move_focus (GtkListBase *self,
}
/*
+ * gtk_list_base_get_allocation_along:
+ * @self: a #GtkListBase
+ * @pos: item to get the size of
+ * @offset: (out caller-allocates) (allow-none) set to the offset
+ * of the top/left of the item
+ * @size: (out caller-allocates) (allow-none) set to the size of
+ * the item in the direction
+ *
+ * Computes the allocation of the item in the direction along the sizing
+ * axis.
+ *
+ * Returns: %TRUE if the item exists and has an allocation, %FALSE otherwise
+ **/
+gboolean
+gtk_list_base_get_allocation_along (GtkListBase *self,
+ guint pos,
+ int *offset,
+ int *size)
+{
+ return GTK_LIST_BASE_GET_CLASS (self)->get_allocation_along (self, pos, offset, size);
+}
+
+/*
+ * gtk_list_base_get_allocation_across:
+ * @self: a #GtkListBase
+ * @pos: item to get the size of
+ * @offset: (out caller-allocates) (allow-none) set to the offset
+ * of the top/left of the item
+ * @size: (out caller-allocates) (allow-none) set to the size of
+ * the item in the direction
+ *
+ * Computes the allocation of the item in the direction across to the sizing
+ * axis.
+ *
+ * Returns: %TRUE if the item exists and has an allocation, %FALSE otherwise
+ **/
+static gboolean
+gtk_list_base_get_allocation_across (GtkListBase *self,
+ guint pos,
+ int *offset,
+ int *size)
+{
+ return GTK_LIST_BASE_GET_CLASS (self)->get_allocation_across (self, pos, offset, size);
+}
+
+/*
+ * gtk_list_base_get_position_from_allocation:
+ * @self: a #GtkListBase
+ * @across: position in pixels in the direction cross to the list
+ * @along: position in pixels in the direction of the list
+ * @pos: (out caller-allocates): set to the looked up position
+ * @area: (out caller-allocates) (allow-none): set to the area occupied
+ * by the returned position.
+ *
+ * Given a coordinate in list coordinates, determine the position of the
+ * item that occupies that position.
+ *
+ * It is possible for @area to not include the point given by (across, along).
+ * This will happen for example in the last row of a gridview, where the
+ * last item will be returned for the whole width, even if there are empty
+ * cells.
+ *
+ * Returns: %TRUE on success or %FALSE if no position occupies the given offset.
+ **/
+static guint
+gtk_list_base_get_position_from_allocation (GtkListBase *self,
+ int across,
+ int along,
+ guint *pos,
+ cairo_rectangle_int_t *area)
+{
+ return GTK_LIST_BASE_GET_CLASS (self)->get_position_from_allocation (self, across, along, pos, area);
+}
+
+/*
* gtk_list_base_select_item:
- * @self: a %GtkListBase
+ * @self: a #GtkListBase
* @pos: item to select
* @modify: %TRUE if the selection should be modified, %FALSE
* if a new selection should be done. This is usually set
@@ -562,6 +637,221 @@ gtk_list_base_move_cursor_to_start (GtkWidget *widget,
gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), 0, select, modify, extend);
}
+#if 0
+static void
+gtk_grid_view_compute_scroll_align (GtkGridView *self,
+ GtkOrientation orientation,
+ int cell_start,
+ int cell_end,
+ double current_align,
+ gboolean current_start,
+ double *new_align,
+ gboolean *new_start)
+{
+ int visible_start, visible_size, visible_end;
+ int cell_size;
+
+ gtk_list_base_get_adjustment_values (GTK_LIST_BASE (self),
+ orientation,
+ &visible_start, NULL, &visible_size);
+ visible_end = visible_start + visible_size;
+ cell_size = cell_end - cell_start;
+
+ if (cell_size <= visible_size)
+ {
+ if (cell_start < visible_start)
+ {
+ *new_align = 0.0;
+ *new_start = TRUE;
+ }
+ else if (cell_end > visible_end)
+ {
+ *new_align = 1.0;
+ *new_start = FALSE;
+ }
+ else
+ {
+ /* XXX: start or end here? */
+ *new_start = TRUE;
+ *new_align = (double) (cell_start - visible_start) / visible_size;
+ }
+ }
+ else
+ {
+ /* This is the unlikely case of the cell being higher than the visible area */
+ if (cell_start > visible_start)
+ {
+ *new_align = 0.0;
+ *new_start = TRUE;
+ }
+ else if (cell_end < visible_end)
+ {
+ *new_align = 1.0;
+ *new_start = FALSE;
+ }
+ else
+ {
+ /* the cell already covers the whole screen */
+ *new_align = current_align;
+ *new_start = current_start;
+ }
+ }
+}
+
+static void
+gtk_grid_view_update_focus_tracker (GtkGridView *self)
+{
+ GtkWidget *focus_child;
+ guint pos;
+
+ focus_child = gtk_widget_get_focus_child (GTK_WIDGET (self));
+ if (!GTK_IS_LIST_ITEM (focus_child))
+ return;
+
+ pos = gtk_list_item_get_position (GTK_LIST_ITEM (focus_child));
+ if (pos != gtk_list_item_tracker_get_position (self->item_manager, self->focus))
+ {
+ gtk_list_item_tracker_set_position (self->item_manager,
+ self->focus,
+ pos,
+ self->max_columns,
+ self->max_columns);
+ }
+}
+
+static void
+gtk_grid_view_scroll_to_item (GtkWidget *widget,
+ const char *action_name,
+ GVariant *parameter)
+{
+ GtkGridView *self = GTK_GRID_VIEW (widget);
+ int start, end;
+ double xalign, yalign;
+ gboolean xstart, ystart;
+ guint pos;
+
+ if (!g_variant_check_format_string (parameter, "u", FALSE))
+ return;
+
+ g_variant_get (parameter, "u", &pos);
+
+ /* figure out primary orientation and if position is valid */
+ if (!gtk_grid_view_get_size_at_position (self, pos, &start, &end))
+ return;
+
+ end += start;
+ gtk_grid_view_compute_scroll_align (self,
+ gtk_list_base_get_orientation (GTK_LIST_BASE (self)),
+ start, end,
+ self->anchor_yalign, self->anchor_ystart,
+ &yalign, &ystart);
+
+ /* now do the same thing with the other orientation */
+ start = floor (self->column_width * (pos % self->n_columns));
+ end = floor (self->column_width * ((pos % self->n_columns) + 1));
+ gtk_grid_view_compute_scroll_align (self,
+ gtk_list_base_get_opposite_orientation (GTK_LIST_BASE (self)),
+ start, end,
+ self->anchor_xalign, self->anchor_xstart,
+ &xalign, &xstart);
+
+ gtk_grid_view_set_anchor (self, pos, xalign, xstart, yalign, ystart);
+
+ /* HACK HACK HACK
+ *
+ * GTK has no way to track the focused child. But we now that when a listitem
+ * gets focus, it calls this action. So we update our focus tracker from here
+ * because it's the closest we can get to accurate tracking.
+ */
+ gtk_grid_view_update_focus_tracker (self);
+}
+#endif
+
+static void
+gtk_list_base_move_cursor_page_up (GtkWidget *widget,
+ GVariant *args,
+ gpointer unused)
+{
+ GtkListBase *self = GTK_LIST_BASE (widget);
+ GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
+ gboolean select, modify, extend;
+ cairo_rectangle_int_t area, new_area;
+ int page_size;
+ guint pos, new_pos;
+
+ pos = gtk_list_base_get_focus_position (self);
+ page_size = gtk_adjustment_get_page_size (priv->adjustment[priv->orientation]);
+ if (!gtk_list_base_get_allocation_along (self, pos, &area.y, &area.height) ||
+ !gtk_list_base_get_allocation_across (self, pos, &area.x, &area.width))
+ return;
+ if (!gtk_list_base_get_position_from_allocation (self,
+ area.x + area.width / 2,
+ MAX (0, area.y + area.height - page_size),
+ &new_pos,
+ &new_area))
+ return;
+
+ /* We want the whole row to be visible */
+ if (new_area.y < MAX (0, area.y + area.height - page_size))
+ new_pos = gtk_list_base_move_focus_along (self, new_pos, 1);
+ /* But we definitely want to move if we can */
+ if (new_pos >= pos)
+ {
+ new_pos = gtk_list_base_move_focus_along (self, new_pos, -1);
+ if (new_pos == pos)
+ return;
+ }
+
+ g_variant_get (args, "(bbb)", &select, &modify, &extend);
+
+ gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), new_pos, select, modify, extend);
+}
+
+static void
+gtk_list_base_move_cursor_page_down (GtkWidget *widget,
+ GVariant *args,
+ gpointer unused)
+{
+ GtkListBase *self = GTK_LIST_BASE (widget);
+ GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
+ gboolean select, modify, extend;
+ cairo_rectangle_int_t area, new_area;
+ int page_size, end;
+ guint pos, new_pos;
+
+ pos = gtk_list_base_get_focus_position (self);
+ page_size = gtk_adjustment_get_page_size (priv->adjustment[priv->orientation]);
+ end = gtk_adjustment_get_upper (priv->adjustment[priv->orientation]);
+ if (end == 0)
+ return;
+
+ if (!gtk_list_base_get_allocation_along (self, pos, &area.y, &area.height) ||
+ !gtk_list_base_get_allocation_across (self, pos, &area.x, &area.width))
+ return;
+
+ if (!gtk_list_base_get_position_from_allocation (self,
+ area.x + area.width / 2,
+ MIN (end, area.y + page_size) - 1,
+ &new_pos,
+ &new_area))
+ return;
+
+ /* We want the whole row to be visible */
+ if (new_area.y + new_area.height > MIN (end, area.y + page_size))
+ new_pos = gtk_list_base_move_focus_along (self, new_pos, -1);
+ /* But we definitely want to move if we can */
+ if (new_pos <= pos)
+ {
+ new_pos = gtk_list_base_move_focus_along (self, new_pos, 1);
+ if (new_pos == pos)
+ return;
+ }
+
+ g_variant_get (args, "(bbb)", &select, &modify, &extend);
+
+ gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), new_pos, select, modify, extend);
+}
+
static void
gtk_list_base_move_cursor_to_end (GtkWidget *widget,
GVariant *args,
@@ -761,6 +1051,10 @@ gtk_list_base_class_init (GtkListBaseClass *klass)
gtk_list_base_add_custom_move_binding (binding_set, GDK_KEY_KP_Home, gtk_list_base_move_cursor_to_start);
gtk_list_base_add_custom_move_binding (binding_set, GDK_KEY_End, gtk_list_base_move_cursor_to_end);
gtk_list_base_add_custom_move_binding (binding_set, GDK_KEY_KP_End, gtk_list_base_move_cursor_to_end);
+ gtk_list_base_add_custom_move_binding (binding_set, GDK_KEY_Page_Up, gtk_list_base_move_cursor_page_up);
+ gtk_list_base_add_custom_move_binding (binding_set, GDK_KEY_KP_Page_Up, gtk_list_base_move_cursor_page_up);
+ gtk_list_base_add_custom_move_binding (binding_set, GDK_KEY_Page_Down, gtk_list_base_move_cursor_page_down);
+ gtk_list_base_add_custom_move_binding (binding_set, GDK_KEY_KP_Page_Down, gtk_list_base_move_cursor_page_down);
gtk_binding_entry_add_action (binding_set, GDK_KEY_a, GDK_CONTROL_MASK, "list.select-all", NULL);
gtk_binding_entry_add_action (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK, "list.select-all", NULL);
diff --git a/gtk/gtklistbaseprivate.h b/gtk/gtklistbaseprivate.h
index 189538a700..8f3054acaa 100644
--- a/gtk/gtklistbaseprivate.h
+++ b/gtk/gtklistbaseprivate.h
@@ -41,6 +41,19 @@ struct _GtkListBaseClass
void (* adjustment_value_changed) (GtkListBase *self,
GtkOrientation orientation);
+ gboolean (* get_allocation_along) (GtkListBase *self,
+ guint pos,
+ int *offset,
+ int *size);
+ gboolean (* get_allocation_across) (GtkListBase *self,
+ guint pos,
+ int *offset,
+ int *size);
+ gboolean (* get_position_from_allocation) (GtkListBase *self,
+ int across,
+ int along,
+ guint *pos,
+ cairo_rectangle_int_t *area);
guint (* move_focus_along) (GtkListBase *self,
guint pos,
int steps);
@@ -55,6 +68,10 @@ guint gtk_list_base_get_focus_position (GtkListBase
GtkListItemManager * gtk_list_base_get_manager (GtkListBase *self);
GtkScrollablePolicy gtk_list_base_get_scroll_policy (GtkListBase *self,
GtkOrientation orientation);
+gboolean gtk_list_base_get_allocation_along (GtkListBase *base,
+ guint pos,
+ int *offset,
+ int *size);
void gtk_list_base_get_adjustment_values (GtkListBase *self,
GtkOrientation orientation,
int *value,
diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c
index 033f4eab8f..69e81afbe7 100644
--- a/gtk/gtklistview.c
+++ b/gtk/gtklistview.c
@@ -21,7 +21,6 @@
#include "gtklistview.h"
-#include "gtkbindings.h"
#include "gtkintl.h"
#include "gtklistbaseprivate.h"
#include "gtklistitemmanagerprivate.h"
@@ -264,37 +263,6 @@ list_row_get_y (GtkListView *self,
return y ;
}
-static gboolean
-gtk_list_view_get_size_at_position (GtkListView *self,
- guint pos,
- int *offset,
- int *height)
-{
- ListRow *row;
- guint skip;
- int y;
-
- row = gtk_list_item_manager_get_nth (self->item_manager, pos, &skip);
- if (row == NULL)
- {
- if (offset)
- *offset = 0;
- if (height)
- *height = 0;
- return FALSE;
- }
-
- y = list_row_get_y (self, row);
- y += skip * row->height;
-
- if (offset)
- *offset = y;
- if (height)
- *height = row->height;
-
- return TRUE;
-}
-
static int
gtk_list_view_get_list_height (GtkListView *self)
{
@@ -393,6 +361,54 @@ gtk_list_view_adjustment_value_changed (GtkListBase *base,
gtk_widget_queue_allocate (GTK_WIDGET (self));
}
+static gboolean
+gtk_list_view_get_allocation_along (GtkListBase *base,
+ guint pos,
+ int *offset,
+ int *size)
+{
+ GtkListView *self = GTK_LIST_VIEW (base);
+ ListRow *row;
+ guint skip;
+ int y;
+
+ row = gtk_list_item_manager_get_nth (self->item_manager, pos, &skip);
+ if (row == NULL)
+ {
+ if (offset)
+ *offset = 0;
+ if (size)
+ *size = 0;
+ return FALSE;
+ }
+
+ y = list_row_get_y (self, row);
+ y += skip * row->height;
+
+ if (offset)
+ *offset = y;
+ if (size)
+ *size = row->height;
+
+ return TRUE;
+}
+
+static gboolean
+gtk_list_view_get_allocation_across (GtkListBase *base,
+ guint pos,
+ int *offset,
+ int *size)
+{
+ GtkListView *self = GTK_LIST_VIEW (base);
+
+ if (offset)
+ *offset = 0;
+ if (size)
+ *size = self->list_width;
+
+ return TRUE;
+}
+
static guint
gtk_list_view_move_focus_along (GtkListBase *base,
guint pos,
@@ -411,6 +427,39 @@ gtk_list_view_move_focus_along (GtkListBase *base,
return pos;
}
+static gboolean
+gtk_list_view_get_position_from_allocation (GtkListBase *base,
+ int across,
+ int along,
+ guint *pos,
+ cairo_rectangle_int_t *area)
+{
+ GtkListView *self = GTK_LIST_VIEW (base);
+ ListRow *row;
+ int remaining;
+
+ if (across >= self->list_width)
+ return FALSE;
+
+ row = gtk_list_view_get_row_at_y (self, along, &remaining);
+ if (row == NULL)
+ return FALSE;
+
+ *pos = gtk_list_item_manager_get_item_position (self->item_manager, row);
+ g_assert (remaining < row->height * row->parent.n_items);
+ *pos += remaining / row->height;
+
+ if (area)
+ {
+ area->x = 0;
+ area->width = self->list_width;
+ area->y = along - remaining % row->height;
+ area->height = row->height;
+ }
+
+ return TRUE;
+}
+
static guint
gtk_list_view_move_focus_across (GtkListBase *base,
guint pos,
@@ -435,7 +484,7 @@ gtk_list_view_update_adjustments (GtkListView *self,
upper = gtk_list_view_get_list_height (self);
anchor_pos = gtk_list_item_tracker_get_position (self->item_manager, self->anchor);
- if (!gtk_list_view_get_size_at_position (self,
+ if (!gtk_list_base_get_allocation_along (GTK_LIST_BASE (self),
anchor_pos,
&offset,
&size))
@@ -893,7 +942,7 @@ gtk_list_view_scroll_to_item (GtkWidget *widget,
g_variant_get (parameter, "u", &pos);
/* figure out primary orientation and if position is valid */
- if (!gtk_list_view_get_size_at_position (self, pos, &start, &end))
+ if (!gtk_list_base_get_allocation_along (GTK_LIST_BASE (self), pos, &start, &end))
return;
end += start;
@@ -933,130 +982,20 @@ gtk_list_view_activate_item (GtkWidget *widget,
}
static void
-gtk_list_view_move_cursor_page_up (GtkWidget *widget,
- GVariant *args,
- gpointer unused)
-{
- GtkListView *self = GTK_LIST_VIEW (widget);
- gboolean select, modify, extend;
- guint start, pos, n_items;
- ListRow *row;
- int pixels, offset;
-
- start = gtk_list_item_tracker_get_position (self->item_manager, self->focus);
- row = gtk_list_item_manager_get_nth (self->item_manager, start, NULL);
- if (row == NULL)
- return;
- n_items = self->model ? g_list_model_get_n_items (self->model) : 0;
- /* check that we can go at least one row up */
- if (n_items == 0 || start == 0)
- return;
-
- pixels = gtk_widget_get_size (widget, gtk_list_base_get_orientation (GTK_LIST_BASE (self)));
- pixels -= row->height;
-
- pos = gtk_list_view_get_position_at_y (self,
- MAX (0, list_row_get_y (self, row) - pixels),
- &offset,
- NULL);
- /* there'll always be rows between 0 and this row */
- g_assert (pos < n_items);
- /* if row is too high, go one row less */
- if (offset > 0)
- pos++;
- /* but go at least 1 row */
- if (pos >= start)
- pos = start - 1;
-
- g_variant_get (args, "(bbb)", &select, &modify, &extend);
-
- gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), pos, select, modify, extend);
-}
-
-static void
-gtk_list_view_move_cursor_page_down (GtkWidget *widget,
- GVariant *args,
- gpointer unused)
-{
- GtkListView *self = GTK_LIST_VIEW (widget);
- gboolean select, modify, extend;
- guint start, pos, n_items;
- ListRow *row;
- int pixels, offset;
-
- start = gtk_list_item_tracker_get_position (self->item_manager, self->focus);
- row = gtk_list_item_manager_get_nth (self->item_manager, start, NULL);
- if (row == NULL)
- return;
- n_items = self->model ? g_list_model_get_n_items (self->model) : 0;
- /* check that we can go at least one row down */
- if (n_items == 0 || start >= n_items - 1)
- return;
-
- pixels = gtk_widget_get_size (widget, gtk_list_base_get_orientation (GTK_LIST_BASE (self)));
-
- pos = gtk_list_view_get_position_at_y (self,
- list_row_get_y (self, row) + pixels,
- &offset,
- NULL);
- if (pos >= n_items)
- pos = n_items - 1;
- /* if row is too high, go one row less */
- else if (pos > 0 && offset > 0)
- pos--;
- /* but go at least 1 row */
- if (pos <= start)
- pos = start + 1;
-
- g_variant_get (args, "(bbb)", &select, &modify, &extend);
-
- gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), pos, select, modify, extend);
-}
-
-static void
-gtk_list_view_add_custom_move_binding (GtkBindingSet *binding_set,
- guint keyval,
- GtkBindingCallback callback)
-{
- gtk_binding_entry_add_callback (binding_set,
- keyval,
- 0,
- callback,
- g_variant_new ("(bbb)", TRUE, FALSE, FALSE),
- NULL, NULL);
- gtk_binding_entry_add_callback (binding_set,
- keyval,
- GDK_CONTROL_MASK,
- callback,
- g_variant_new ("(bbb)", FALSE, FALSE, FALSE),
- NULL, NULL);
- gtk_binding_entry_add_callback (binding_set,
- keyval,
- GDK_SHIFT_MASK,
- callback,
- g_variant_new ("(bbb)", TRUE, FALSE, TRUE),
- NULL, NULL);
- gtk_binding_entry_add_callback (binding_set,
- keyval,
- GDK_CONTROL_MASK | GDK_SHIFT_MASK,
- callback,
- g_variant_new ("(bbb)", TRUE, TRUE, TRUE),
- NULL, NULL);
-}
-
-static void
gtk_list_view_class_init (GtkListViewClass *klass)
{
GtkListBaseClass *list_base_class = GTK_LIST_BASE_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GtkBindingSet *binding_set;
list_base_class->list_item_name = "row";
list_base_class->list_item_size = sizeof (ListRow);
list_base_class->list_item_augment_size = sizeof (ListRowAugment);
list_base_class->list_item_augment_func = list_row_augment;
list_base_class->adjustment_value_changed = gtk_list_view_adjustment_value_changed;
+ list_base_class->get_allocation_along = gtk_list_view_get_allocation_along;
+ list_base_class->get_allocation_across = gtk_list_view_get_allocation_across;
+ list_base_class->get_position_from_allocation = gtk_list_view_get_position_from_allocation;
list_base_class->move_focus_along = gtk_list_view_move_focus_along;
list_base_class->move_focus_across = gtk_list_view_move_focus_across;
@@ -1153,13 +1092,6 @@ gtk_list_view_class_init (GtkListViewClass *klass)
"u",
gtk_list_view_scroll_to_item);
- binding_set = gtk_binding_set_by_class (klass);
-
- gtk_list_view_add_custom_move_binding (binding_set, GDK_KEY_Page_Up, gtk_list_view_move_cursor_page_up);
- gtk_list_view_add_custom_move_binding (binding_set, GDK_KEY_KP_Page_Up, gtk_list_view_move_cursor_page_up);
- gtk_list_view_add_custom_move_binding (binding_set, GDK_KEY_Page_Down, gtk_list_view_move_cursor_page_down);
- gtk_list_view_add_custom_move_binding (binding_set, GDK_KEY_KP_Page_Down, gtk_list_view_move_cursor_page_down);
-
gtk_widget_class_set_css_name (widget_class, I_("list"));
}