gridview: Add a focus tracker
... and use that to properly update selections when moving around with the arrow keys.
This commit is contained in:
committed by
Matthias Clasen
parent
e9cc7e0917
commit
26065eb200
@@ -25,6 +25,7 @@
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistitemfactory.h"
|
||||
#include "gtklistitemmanagerprivate.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkorientableprivate.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkscrollable.h"
|
||||
@@ -78,6 +79,8 @@ struct _GtkGridView
|
||||
guint anchor_ystart : 1;
|
||||
/* the last item that was selected - basically the location to extend selections from */
|
||||
GtkListItemTracker *selected;
|
||||
/* the item that has input focus */
|
||||
GtkListItemTracker *focus;
|
||||
};
|
||||
|
||||
struct _Cell
|
||||
@@ -396,6 +399,66 @@ gtk_grid_view_select_item (GtkGridView *self,
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_grid_view_focus (GtkWidget *widget,
|
||||
GtkDirectionType direction)
|
||||
{
|
||||
GtkGridView *self = GTK_GRID_VIEW (widget);
|
||||
GtkWidget *old_focus_child, *new_focus_child;
|
||||
|
||||
old_focus_child = gtk_widget_get_focus_child (widget);
|
||||
|
||||
if (old_focus_child == NULL &&
|
||||
(direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_TAB_BACKWARD))
|
||||
{
|
||||
Cell *cell;
|
||||
guint pos;
|
||||
|
||||
/* When tabbing into the listview, don't focus the first/last item,
|
||||
* but keep the previously focused item
|
||||
*/
|
||||
pos = gtk_list_item_tracker_get_position (self->item_manager, self->focus);
|
||||
cell = gtk_list_item_manager_get_nth (self->item_manager, pos, NULL);
|
||||
if (cell && gtk_widget_grab_focus (cell->parent.widget))
|
||||
goto moved_focus;
|
||||
}
|
||||
|
||||
if (!GTK_WIDGET_CLASS (gtk_grid_view_parent_class)->focus (widget, direction))
|
||||
return FALSE;
|
||||
|
||||
moved_focus:
|
||||
new_focus_child = gtk_widget_get_focus_child (widget);
|
||||
|
||||
if (old_focus_child != new_focus_child &&
|
||||
GTK_IS_LIST_ITEM (new_focus_child))
|
||||
{
|
||||
gboolean extend = FALSE, modify = FALSE;
|
||||
|
||||
if (old_focus_child)
|
||||
{
|
||||
GdkSeat *seat = gdk_display_get_default_seat (gtk_widget_get_display (widget));
|
||||
if (seat)
|
||||
{
|
||||
GdkDevice *keyboard = gdk_seat_get_keyboard (seat);
|
||||
if (keyboard)
|
||||
{
|
||||
GdkModifierType state = gdk_device_get_modifier_state (keyboard);
|
||||
|
||||
extend = (state & GDK_SHIFT_MASK) != 0;
|
||||
modify = (state & GDK_CONTROL_MASK) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gtk_grid_view_select_item (self,
|
||||
gtk_list_item_get_position (GTK_LIST_ITEM (new_focus_child)),
|
||||
modify,
|
||||
extend);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_grid_view_adjustment_is_flipped (GtkGridView *self,
|
||||
GtkOrientation orientation)
|
||||
@@ -1041,6 +1104,11 @@ gtk_grid_view_dispose (GObject *object)
|
||||
gtk_list_item_tracker_free (self->item_manager, self->selected);
|
||||
self->selected = NULL;
|
||||
}
|
||||
if (self->focus)
|
||||
{
|
||||
gtk_list_item_tracker_free (self->item_manager, self->focus);
|
||||
self->focus = NULL;
|
||||
}
|
||||
g_clear_object (&self->item_manager);
|
||||
|
||||
G_OBJECT_CLASS (gtk_grid_view_parent_class)->dispose (object);
|
||||
@@ -1275,6 +1343,27 @@ gtk_grid_view_compute_scroll_align (GtkGridView *self,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -1312,6 +1401,14 @@ gtk_grid_view_scroll_to_item (GtkWidget *widget,
|
||||
&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);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1339,6 +1436,7 @@ gtk_grid_view_class_init (GtkGridViewClass *klass)
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
gpointer iface;
|
||||
|
||||
widget_class->focus = gtk_grid_view_focus;
|
||||
widget_class->measure = gtk_grid_view_measure;
|
||||
widget_class->size_allocate = gtk_grid_view_size_allocate;
|
||||
|
||||
@@ -1541,6 +1639,7 @@ gtk_grid_view_init (GtkGridView *self)
|
||||
self->anchor_xstart = TRUE;
|
||||
self->anchor_ystart = TRUE;
|
||||
self->selected = gtk_list_item_tracker_new (self->item_manager);
|
||||
self->focus = gtk_list_item_tracker_new (self->item_manager);
|
||||
|
||||
self->min_columns = 1;
|
||||
self->max_columns = DEFAULT_MAX_COLUMNS;
|
||||
@@ -1745,6 +1844,7 @@ gtk_grid_view_set_max_columns (GtkGridView *self,
|
||||
self->anchor_xstart,
|
||||
self->anchor_yalign,
|
||||
self->anchor_ystart);
|
||||
gtk_grid_view_update_focus_tracker (self);
|
||||
|
||||
gtk_widget_queue_resize (GTK_WIDGET (self));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user