listview: Track focus movements and update selection

When focus moves via tab/arrow, we need to select the new item.
This commit is contained in:
Benjamin Otte 2019-10-15 23:45:33 +02:00 committed by Matthias Clasen
parent eda547fc4e
commit b9eb818b57

View File

@ -24,6 +24,7 @@
#include "gtkadjustment.h"
#include "gtkintl.h"
#include "gtklistitemmanagerprivate.h"
#include "gtkmain.h"
#include "gtkorientableprivate.h"
#include "gtkprivate.h"
#include "gtkrbtreeprivate.h"
@ -583,6 +584,119 @@ gtk_list_view_size_allocate (GtkWidget *widget,
}
}
static void
gtk_list_view_select_item (GtkListView *self,
guint pos,
gboolean modify,
gboolean extend)
{
GtkSelectionModel *selection_model;
gboolean success = FALSE;
selection_model = gtk_list_item_manager_get_model (self->item_manager);
if (extend)
{
guint start_pos = gtk_list_item_tracker_get_position (self->item_manager, self->selected);
if (start_pos != GTK_INVALID_LIST_POSITION)
{
guint max = MAX (start_pos, pos);
guint min = MIN (start_pos, pos);
if (modify)
{
if (gtk_selection_model_is_selected (selection_model, start_pos))
{
success = gtk_selection_model_select_range (selection_model,
min,
max - min + 1,
FALSE);
}
else
{
success = gtk_selection_model_unselect_range (selection_model,
min,
max - min + 1);
}
}
else
{
success = gtk_selection_model_select_range (selection_model,
min,
max - min + 1,
TRUE);
}
}
/* If there's no range to select or selecting ranges isn't supported
* by the model, fall through to normal setting.
*/
}
if (success)
return;
if (modify)
{
if (gtk_selection_model_is_selected (selection_model, pos))
success = gtk_selection_model_unselect_item (selection_model, pos);
else
success = gtk_selection_model_select_item (selection_model, pos, FALSE);
}
else
{
success = gtk_selection_model_select_item (selection_model, pos, TRUE);
}
if (success)
{
gtk_list_item_tracker_set_position (self->item_manager,
self->selected,
pos,
0, 0);
}
}
static gboolean
gtk_list_view_focus (GtkWidget *widget,
GtkDirectionType direction)
{
GtkListView *self = GTK_LIST_VIEW (widget);
GtkWidget *old_focus_child, *new_focus_child;
old_focus_child = gtk_widget_get_focus_child (widget);
if (!GTK_WIDGET_CLASS (gtk_list_view_parent_class)->focus (widget, direction))
return FALSE;
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_list_view_select_item (self,
gtk_list_item_get_position (GTK_LIST_ITEM (new_focus_child)),
modify,
extend);
}
return TRUE;
}
static void
gtk_list_view_clear_adjustment (GtkListView *self,
GtkOrientation orientation)
@ -778,75 +892,17 @@ gtk_list_view_set_property (GObject *object,
}
static void
gtk_list_view_select_item (GtkWidget *widget,
const char *action_name,
GVariant *parameter)
gtk_list_view_select_item_action (GtkWidget *widget,
const char *action_name,
GVariant *parameter)
{
GtkListView *self = GTK_LIST_VIEW (widget);
GtkSelectionModel *selection_model;
guint pos;
gboolean modify, extend;
gboolean success = FALSE;
selection_model = gtk_list_item_manager_get_model (self->item_manager);
g_variant_get (parameter, "(ubb)", &pos, &modify, &extend);
if (extend)
{
guint start_pos = gtk_list_item_tracker_get_position (self->item_manager, self->selected);
if (start_pos != GTK_INVALID_LIST_POSITION)
{
guint max = MAX (start_pos, pos);
guint min = MIN (start_pos, pos);
if (modify)
{
if (gtk_selection_model_is_selected (selection_model, start_pos))
{
success = gtk_selection_model_select_range (selection_model,
min,
max - min + 1,
FALSE);
}
else
{
success = gtk_selection_model_unselect_range (selection_model,
min,
max - min + 1);
}
}
else
{
success = gtk_selection_model_select_range (selection_model,
min,
max - min + 1,
TRUE);
}
}
/* If there's no range to select or selecting ranges isn't supported
* by the model, fall through to normal setting.
*/
}
if (success)
return;
if (modify)
{
if (gtk_selection_model_is_selected (selection_model, pos))
success = gtk_selection_model_unselect_item (selection_model, pos);
else
success = gtk_selection_model_select_item (selection_model, pos, FALSE);
}
else
{
success = gtk_selection_model_select_item (selection_model, pos, TRUE);
}
if (success)
{
gtk_list_item_tracker_set_position (self->item_manager,
self->selected,
pos,
0, 0);
}
gtk_list_view_select_item (self, pos, modify, extend);
}
static void
@ -922,6 +978,7 @@ gtk_list_view_class_init (GtkListViewClass *klass)
widget_class->measure = gtk_list_view_measure;
widget_class->size_allocate = gtk_list_view_size_allocate;
widget_class->focus = gtk_list_view_focus;
gobject_class->dispose = gtk_list_view_dispose;
gobject_class->finalize = gtk_list_view_finalize;
@ -1052,7 +1109,7 @@ gtk_list_view_class_init (GtkListViewClass *klass)
gtk_widget_class_install_action (widget_class,
"list.select-item",
"(ubb)",
gtk_list_view_select_item);
gtk_list_view_select_item_action);
/**
* GtkListView|list.scroll-to-item: