diff options
author | Benjamin Otte <otte@redhat.com> | 2019-10-15 23:06:36 +0200 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2019-10-26 08:51:18 +0200 |
commit | 16be945de252e9e1876d11a0dde6fe91cce89b84 (patch) | |
tree | 1e0bd752b7ae943442258b392559e20148b1e584 | |
parent | 3a27672f957fb079ffaeaba478d26a646c364704 (diff) | |
download | gtk+-16be945de252e9e1876d11a0dde6fe91cce89b84.tar.gz |
listview: Implement activation
- a GtkListview::activate signal
- a GtkListItem::activatable property
- activate list items on double clicks and <Enter> presses
-rw-r--r-- | gtk/gtklistitem.c | 174 | ||||
-rw-r--r-- | gtk/gtklistitem.h | 24 | ||||
-rw-r--r-- | gtk/gtklistview.c | 60 | ||||
-rw-r--r-- | gtk/gtktypes.h | 1 |
4 files changed, 238 insertions, 21 deletions
diff --git a/gtk/gtklistitem.c b/gtk/gtklistitem.c index e6aebb7db0..860e8f269f 100644 --- a/gtk/gtklistitem.c +++ b/gtk/gtklistitem.c @@ -21,6 +21,7 @@ #include "gtklistitemprivate.h" +#include "gtkbindings.h" #include "gtkcssnodeprivate.h" #include "gtkeventcontrollerkey.h" #include "gtkgestureclick.h" @@ -59,13 +60,22 @@ struct _GtkListItem GObject *item; guint position; + guint activatable : 1; guint selectable : 1; guint selected : 1; }; +struct _GtkListItemClass +{ + GtkBinClass parent_class; + + void (* activate_signal) (GtkListItem *self); +}; + enum { PROP_0, + PROP_ACTIVATABLE, PROP_ITEM, PROP_POSITION, PROP_SELECTABLE, @@ -74,9 +84,28 @@ enum N_PROPS }; +enum +{ + ACTIVATE_SIGNAL, + LAST_SIGNAL +}; + G_DEFINE_TYPE (GtkListItem, gtk_list_item, GTK_TYPE_BIN) static GParamSpec *properties[N_PROPS] = { NULL, }; +static guint signals[LAST_SIGNAL] = { 0 }; + +static void +gtk_list_item_activate_signal (GtkListItem *self) +{ + if (!self->activatable) + return; + + gtk_widget_activate_action (GTK_WIDGET (self), + "list.activate-item", + "u", + self->position); +} static gboolean gtk_list_item_focus (GtkWidget *widget, @@ -147,6 +176,10 @@ gtk_list_item_get_property (GObject *object, switch (property_id) { + case PROP_ACTIVATABLE: + g_value_set_boolean (value, self->activatable); + break; + case PROP_ITEM: g_value_set_object (value, self->item); break; @@ -179,6 +212,10 @@ gtk_list_item_set_property (GObject *object, switch (property_id) { + case PROP_ACTIVATABLE: + gtk_list_item_set_activatable (self, g_value_get_boolean (value)); + break; + case PROP_SELECTABLE: gtk_list_item_set_selectable (self, g_value_get_boolean (value)); break; @@ -194,6 +231,9 @@ gtk_list_item_class_init (GtkListItemClass *klass) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkBindingSet *binding_set; + + klass->activate_signal = gtk_list_item_activate_signal; widget_class->focus = gtk_list_item_focus; widget_class->grab_focus = gtk_list_item_grab_focus; @@ -203,6 +243,18 @@ gtk_list_item_class_init (GtkListItemClass *klass) gobject_class->set_property = gtk_list_item_set_property; /** + * GtkListItem:activatable: + * + * If the item can be activated by the user + */ + properties[PROP_ACTIVATABLE] = + g_param_spec_boolean ("activatable", + P_("Activatable"), + P_("If the item can be activated by the user"), + TRUE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + /** * GtkListItem:item: * * Displayed item @@ -252,6 +304,36 @@ gtk_list_item_class_init (GtkListItemClass *klass) g_object_class_install_properties (gobject_class, N_PROPS, properties); + /** + * GtkListItem::activate-signal: + * + * This is a keybinding signal, which will cause this row to be activated. + * + * Do not use it, it is an implementation detail. + * + * If you want to be notified when the user activates a listitem (by key or not), + * look at the list widget this item is contained in. + */ + signals[ACTIVATE_SIGNAL] = + g_signal_new (I_("activate-keybinding"), + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkListItemClass, activate_signal), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + widget_class->activate_signal = signals[ACTIVATE_SIGNAL]; + + binding_set = gtk_binding_set_by_class (klass); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0, + "activate-keybinding", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0, + "activate-keybinding", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0, + "activate-keybinding", 0); + /* This gets overwritten by gtk_list_item_new() but better safe than sorry */ gtk_widget_class_set_css_name (widget_class, I_("row")); } @@ -264,30 +346,45 @@ gtk_list_item_click_gesture_pressed (GtkGestureClick *gesture, GtkListItem *self) { GtkWidget *widget = GTK_WIDGET (self); - GdkModifierType state; - GdkModifierType mask; - gboolean extend = FALSE, modify = FALSE; - if (!self->selectable) + if (!self->selectable && !self->activatable) { gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); return; } - if (gtk_get_current_event_state (&state)) + if (self->selectable) { - 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; + GdkModifierType state; + GdkModifierType mask; + gboolean extend = FALSE, modify = FALSE; + + 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_activate_action (GTK_WIDGET (self), - "list.select-item", - "(ubb)", - self->position, modify, extend); + if (self->activatable) + { + if (n_press == 2) + { + gtk_widget_activate_action (GTK_WIDGET (self), + "list.activate-item", + "u", + self->position); + } + } gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_ACTIVE, FALSE); @@ -336,6 +433,7 @@ gtk_list_item_init (GtkListItem *self) GtkGesture *gesture; self->selectable = TRUE; + self->activatable = TRUE; gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE); gesture = gtk_gesture_click_new (); @@ -525,3 +623,49 @@ gtk_list_item_set_selectable (GtkListItem *self, g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTABLE]); } + +/** + * gtk_list_item_get_activatable: + * @self: a #GtkListItem + * + * Checks if a list item has been set to be activatable via + * gtk_list_item_set_activatable(). + * + * Returns: %TRUE if the item is activatable + **/ +gboolean +gtk_list_item_get_activatable (GtkListItem *self) +{ + g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE); + + return self->activatable; +} + +/** + * gtk_list_item_set_activatable: + * @self: a #GtkListItem + * @activatable: if the item should be activatable + * + * Sets @self to be activatable. + * + * If an item is activatable, double-clicking on the item, using + * the <Return> key or calling gtk_widget_activate() will activate + * the item. Activating instructs the containing view to handle + * activation. #GtkListView for example will be emitting the + * GtkListView::activate signal. + * + * By default, list items are activatable + **/ +void +gtk_list_item_set_activatable (GtkListItem *self, + gboolean activatable) +{ + g_return_if_fail (GTK_IS_LIST_ITEM (self)); + + if (self->activatable == activatable) + return; + + self->activatable = activatable; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIVATABLE]); +} diff --git a/gtk/gtklistitem.h b/gtk/gtklistitem.h index a783b8af56..d45ae82b24 100644 --- a/gtk/gtklistitem.h +++ b/gtk/gtklistitem.h @@ -28,22 +28,34 @@ G_BEGIN_DECLS -GDK_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (GtkListItem, gtk_list_item, GTK, LIST_ITEM, GtkBin) - #define GTK_TYPE_LIST_ITEM (gtk_list_item_get_type ()) +#define GTK_LIST_ITEM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_ITEM, GtkListItem)) +#define GTK_LIST_ITEM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_ITEM, GtkListItemClass)) +#define GTK_IS_LIST_ITEM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_ITEM)) +#define GTK_IS_LIST_ITEM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_ITEM)) +#define GTK_LIST_ITEM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_ITEM, GtkListItemClass)) + +typedef struct _GtkListItemClass GtkListItemClass; + +GDK_AVAILABLE_IN_ALL +GType gtk_list_item_get_type (void) G_GNUC_CONST; GDK_AVAILABLE_IN_ALL gpointer gtk_list_item_get_item (GtkListItem *self); GDK_AVAILABLE_IN_ALL -guint gtk_list_item_get_position (GtkListItem *self); +guint gtk_list_item_get_position (GtkListItem *self) G_GNUC_PURE; GDK_AVAILABLE_IN_ALL -gboolean gtk_list_item_get_selected (GtkListItem *self); +gboolean gtk_list_item_get_selected (GtkListItem *self) G_GNUC_PURE; GDK_AVAILABLE_IN_ALL -gboolean gtk_list_item_get_selectable (GtkListItem *self); +gboolean gtk_list_item_get_selectable (GtkListItem *self) G_GNUC_PURE; GDK_AVAILABLE_IN_ALL void gtk_list_item_set_selectable (GtkListItem *self, gboolean selectable); +GDK_AVAILABLE_IN_ALL +gboolean gtk_list_item_get_activatable (GtkListItem *self) G_GNUC_PURE; +GDK_AVAILABLE_IN_ALL +void gtk_list_item_set_activatable (GtkListItem *self, + gboolean activatable); G_END_DECLS diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c index 87a80513de..85a4a5744f 100644 --- a/gtk/gtklistview.c +++ b/gtk/gtklistview.c @@ -101,11 +101,17 @@ enum N_PROPS }; +enum { + ACTIVATE, + LAST_SIGNAL +}; + G_DEFINE_TYPE_WITH_CODE (GtkListView, gtk_list_view, GTK_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL) G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL)) static GParamSpec *properties[N_PROPS] = { NULL, }; +static guint signals[LAST_SIGNAL] = { 0 }; static void G_GNUC_UNUSED dump (GtkListView *self) @@ -890,6 +896,24 @@ gtk_list_view_scroll_to_item (GtkWidget *widget, } static void +gtk_list_view_activate_item (GtkWidget *widget, + const char *action_name, + GVariant *parameter) +{ + GtkListView *self = GTK_LIST_VIEW (widget); + guint pos; + + if (!g_variant_check_format_string (parameter, "u", FALSE)) + return; + + g_variant_get (parameter, "u", &pos); + if (self->model == NULL || pos >= g_list_model_get_n_items (self->model)) + return; + + g_signal_emit (widget, signals[ACTIVATE], 0, pos); +} + +static void gtk_list_view_class_init (GtkListViewClass *klass) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); @@ -972,6 +996,42 @@ gtk_list_view_class_init (GtkListViewClass *klass) g_object_class_install_properties (gobject_class, N_PROPS, properties); /** + * GtkListView::activate: + * @self: The #GtkListView + * @position: position of item to activate + * + * The ::activate signal is emitted when a row has been activated by the user, + * usually via activating the GtkListView|list.activate-item action. + * + * This allows for a convenient way to handle activation in a listview. + * See gtk_list_item_set_activatable() for details on how to use this signal. + */ + signals[ACTIVATE] = + g_signal_new (I_("activate"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, + G_TYPE_UINT); + g_signal_set_va_marshaller (signals[ACTIVATE], + G_TYPE_FROM_CLASS (gobject_class), + g_cclosure_marshal_VOID__UINTv); + + /** + * GtkListView|list.activate-item: + * @position: position of item to activate + * + * Activates the item given in @position by emitting the GtkListView::activate + * signal. + */ + gtk_widget_class_install_action (widget_class, + "list.activate-item", + "u", + gtk_list_view_activate_item); + + /** * GtkListView|list.select-item: * @position: position of item to select * @modify: %TRUE to toggle the existing selection, %FALSE to select diff --git a/gtk/gtktypes.h b/gtk/gtktypes.h index 1f0438ed80..8adc3fbd9f 100644 --- a/gtk/gtktypes.h +++ b/gtk/gtktypes.h @@ -39,6 +39,7 @@ typedef struct _GtkClipboard GtkClipboard; typedef struct _GtkEventController GtkEventController; typedef struct _GtkGesture GtkGesture; typedef struct _GtkLayoutManager GtkLayoutManager; +typedef struct _GtkListItem GtkListItem; typedef struct _GtkListItemFactory GtkListItemFactory; typedef struct _GtkNative GtkNative; typedef struct _GtkRequisition GtkRequisition; |