diff --git a/gtk/gtklistitem.c b/gtk/gtklistitem.c index b1461dd386..cc51953835 100644 --- a/gtk/gtklistitem.c +++ b/gtk/gtklistitem.c @@ -23,7 +23,9 @@ #include "gtkbinlayout.h" #include "gtkcssnodeprivate.h" +#include "gtkgestureclick.h" #include "gtkintl.h" +#include "gtkmain.h" #include "gtkselectionmodel.h" /* for GTK_INVALID_LIST_POSITION */ #include "gtkwidget.h" #include "gtkwidgetprivate.h" @@ -234,19 +236,98 @@ gtk_list_item_class_init (GtkListItemClass *klass) } static void -gtk_list_item_init (GtkListItem *self) +gtk_list_item_click_gesture_pressed (GtkGestureClick *gesture, + int n_press, + double x, + double y, + GtkListItem *self) { - self->selectable = TRUE; + GtkWidget *widget = GTK_WIDGET (self); + GdkModifierType state; + GdkModifierType mask; + gboolean extend = FALSE, modify = FALSE; + + if (!self->selectable) + { + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); + return; + } + + if (gtk_get_current_event_state (&state)) + { + mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_MODIFY_SELECTION); + if ((state & mask) == mask) + modify = TRUE; + mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_EXTEND_SELECTION); + if ((state & mask) == mask) + extend = TRUE; + } + + gtk_widget_activate_action (GTK_WIDGET (self), + "list.select-item", + "(ubb)", + self->position, modify, extend); + + gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_ACTIVE, FALSE); + + if (gtk_widget_get_focus_on_click (widget)) + gtk_widget_grab_focus (widget); } -GtkWidget * +static void +gtk_list_item_click_gesture_released (GtkGestureClick *gesture, + int n_press, + double x, + double y, + GtkListItem *self) +{ + gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE); +} + +static void +gtk_list_item_click_gesture_canceled (GtkGestureClick *gesture, + GdkEventSequence *sequence, + GtkListItem *self) +{ + gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE); +} + +static void +gtk_list_item_init (GtkListItem *self) +{ + GtkGesture *gesture; + + self->selectable = TRUE; + gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE); + + gesture = gtk_gesture_click_new (); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), + GTK_PHASE_BUBBLE); + gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), + FALSE); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), + GDK_BUTTON_PRIMARY); + g_signal_connect (gesture, "pressed", + G_CALLBACK (gtk_list_item_click_gesture_pressed), self); + g_signal_connect (gesture, "released", + G_CALLBACK (gtk_list_item_click_gesture_released), self); + g_signal_connect (gesture, "cancel", + G_CALLBACK (gtk_list_item_click_gesture_canceled), self); + gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture)); +} + +GtkListItem * gtk_list_item_new (const char *css_name) { + GtkListItem *result; + g_return_val_if_fail (css_name != NULL, NULL); - return g_object_new (GTK_TYPE_LIST_ITEM, - "css-name", css_name, - NULL); + result = g_object_new (GTK_TYPE_LIST_ITEM, + "css-name", css_name, + NULL); + + return result; } /** @@ -449,5 +530,7 @@ gtk_list_item_set_selectable (GtkListItem *self, self->selectable = selectable; + gtk_widget_set_can_focus (GTK_WIDGET (self), self->selectable); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTABLE]); } diff --git a/gtk/gtklistitemfactory.c b/gtk/gtklistitemfactory.c index 6b7c2b3550..9115b1c3f8 100644 --- a/gtk/gtklistitemfactory.c +++ b/gtk/gtklistitemfactory.c @@ -85,19 +85,14 @@ gtk_list_item_factory_new (GtkListItemSetupFunc setup_func, return self; } -GtkListItem * -gtk_list_item_factory_create (GtkListItemFactory *self) +void +gtk_list_item_factory_setup (GtkListItemFactory *self, + GtkListItem *list_item) { - GtkWidget *result; - - g_return_val_if_fail (GTK_IS_LIST_ITEM_FACTORY (self), NULL); - - result = gtk_list_item_new ("row"); + g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self)); if (self->setup_func) - self->setup_func (GTK_LIST_ITEM (result), self->user_data); - - return GTK_LIST_ITEM (result); + self->setup_func (list_item, self->user_data); } void diff --git a/gtk/gtklistitemfactoryprivate.h b/gtk/gtklistitemfactoryprivate.h index 3e815fa131..dc538f09fa 100644 --- a/gtk/gtklistitemfactoryprivate.h +++ b/gtk/gtklistitemfactoryprivate.h @@ -43,7 +43,8 @@ GtkListItemFactory * gtk_list_item_factory_new (GtkListItemSetu gpointer user_data, GDestroyNotify user_destroy); -GtkListItem * gtk_list_item_factory_create (GtkListItemFactory *self); +void gtk_list_item_factory_setup (GtkListItemFactory *self, + GtkListItem *list_item); void gtk_list_item_factory_bind (GtkListItemFactory *self, GtkListItem *list_item, diff --git a/gtk/gtklistitemmanager.c b/gtk/gtklistitemmanager.c index e7c11f483f..00bce1a10b 100644 --- a/gtk/gtklistitemmanager.c +++ b/gtk/gtklistitemmanager.c @@ -21,6 +21,7 @@ #include "gtklistitemmanagerprivate.h" +#include "gtklistitemprivate.h" #include "gtkwidgetprivate.h" struct _GtkListItemManager @@ -260,7 +261,8 @@ gtk_list_item_manager_acquire_list_item (GtkListItemManager *self, g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL); g_return_val_if_fail (prev_sibling == NULL || GTK_IS_WIDGET (prev_sibling), NULL); - result = gtk_list_item_factory_create (self->factory); + result = gtk_list_item_new ("row"); + gtk_list_item_factory_setup (self->factory, result); item = g_list_model_get_item (G_LIST_MODEL (self->model), position); selected = gtk_selection_model_is_selected (self->model, position); diff --git a/gtk/gtklistitemprivate.h b/gtk/gtklistitemprivate.h index 08ed82f162..3a2ac463d1 100644 --- a/gtk/gtklistitemprivate.h +++ b/gtk/gtklistitemprivate.h @@ -22,9 +22,11 @@ #include "gtklistitem.h" +#include "gtklistitemmanagerprivate.h" + G_BEGIN_DECLS -GtkWidget * gtk_list_item_new (const char *css_name); +GtkListItem * gtk_list_item_new (const char *css_name); void gtk_list_item_set_item (GtkListItem *self, gpointer item); diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c index 494a2a591b..36c18d698b 100644 --- a/gtk/gtklistview.c +++ b/gtk/gtklistview.c @@ -1201,6 +1201,34 @@ gtk_list_view_set_property (GObject *object, } } +static void +gtk_list_view_select_item (GtkWidget *widget, + const char *action_name, + GVariant *parameter) +{ + GtkListView *self = GTK_LIST_VIEW (widget); + GtkSelectionModel *selection_model; + guint pos; + gboolean modify, extend; + + selection_model = gtk_list_item_manager_get_model (self->item_manager); + g_variant_get (parameter, "(ubb)", &pos, &modify, &extend); + + /* XXX: handle extend by tracking the item to extend from */ + + if (modify) + { + if (gtk_selection_model_is_selected (selection_model, pos)) + gtk_selection_model_unselect_item (selection_model, pos); + else + gtk_selection_model_select_item (selection_model, pos, FALSE); + } + else + { + gtk_selection_model_select_item (selection_model, pos, TRUE); + } +} + static void gtk_list_view_class_init (GtkListViewClass *klass) { @@ -1245,6 +1273,29 @@ gtk_list_view_class_init (GtkListViewClass *klass) g_object_class_install_properties (gobject_class, N_PROPS, properties); + /** + * GtkListView|list.select-item: + * @position: position of item to select + * @modify: %TRUE to toggle the existing selection, %FALSE to select + * @extend: %TRUE to extend the selection + * + * Changes selection. + * + * If @extend is %TRUE and the model supports selecting ranges, the + * affected items are all items from the last selected item to the item + * in @position. + * If @extend is %FALSE or selecting ranges is not supported, only the + * item in @position is affected. + * + * If @modify is %TRUE, the affected items will be set to the same state. + * If @modify is %FALSE, the affected items will be selected and + * all other items will be deselected. + */ + gtk_widget_class_install_action (widget_class, + "list.select-item", + "(ubb)", + gtk_list_view_select_item); + gtk_widget_class_set_css_name (widget_class, I_("list")); }