diff options
author | Benjamin Otte <otte@redhat.com> | 2019-10-25 07:39:57 +0200 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2019-10-28 10:49:02 +0100 |
commit | f2044539ee7cb44bd58474aa546e9ce15994167c (patch) | |
tree | 4dd43780183b7b9a11b7a9e042909d61c354aa78 | |
parent | 165d4e8307cb065b3d8874ef868e92b499fe2aee (diff) | |
download | gtk+-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.c | 280 | ||||
-rw-r--r-- | gtk/gtklistbase.c | 296 | ||||
-rw-r--r-- | gtk/gtklistbaseprivate.h | 17 | ||||
-rw-r--r-- | gtk/gtklistview.c | 240 |
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")); } |