diff --git a/gtk/gtklistitem.c b/gtk/gtklistitem.c index 08e866f908..25e11a1c44 100644 --- a/gtk/gtklistitem.c +++ b/gtk/gtklistitem.c @@ -61,6 +61,7 @@ struct _GtkListItem GtkWidget *child; guint position; + guint activatable : 1; guint selectable : 1; guint selected : 1; }; @@ -68,11 +69,14 @@ struct _GtkListItem struct _GtkListItemClass { GtkWidgetClass parent_class; + + void (* activate_signal) (GtkListItem *self); }; enum { PROP_0, + PROP_ACTIVATABLE, PROP_CHILD, PROP_ITEM, PROP_POSITION, @@ -82,9 +86,28 @@ enum N_PROPS }; +enum +{ + ACTIVATE_SIGNAL, + LAST_SIGNAL +}; + G_DEFINE_TYPE (GtkListItem, gtk_list_item, GTK_TYPE_WIDGET) 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, @@ -152,6 +175,10 @@ gtk_list_item_get_property (GObject *object, switch (property_id) { + case PROP_ACTIVATABLE: + g_value_set_boolean (value, self->activatable); + break; + case PROP_CHILD: g_value_set_object (value, self->child); break; @@ -188,6 +215,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_CHILD: gtk_list_item_set_child (self, g_value_get_object (value)); break; @@ -208,6 +239,8 @@ gtk_list_item_class_init (GtkListItemClass *klass) GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + klass->activate_signal = gtk_list_item_activate_signal; + widget_class->focus = gtk_list_item_focus; widget_class->grab_focus = gtk_list_item_grab_focus; @@ -215,6 +248,18 @@ gtk_list_item_class_init (GtkListItemClass *klass) gobject_class->get_property = gtk_list_item_get_property; 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:child: * @@ -277,6 +322,34 @@ 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]; + + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Return, 0, + "activate-keybinding", 0); + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_ISO_Enter, 0, + "activate-keybinding", 0); + gtk_widget_class_add_binding_signal (widget_class, 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")); gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); @@ -290,26 +363,41 @@ gtk_list_item_click_gesture_pressed (GtkGestureClick *gesture, GtkListItem *self) { GtkWidget *widget = GTK_WIDGET (self); - GdkModifierType state; - GdkEvent *event; - gboolean extend, modify; - if (!self->selectable) + if (!self->selectable && !self->activatable) { gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); return; } - event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), - gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture))); - state = gdk_event_get_modifier_state (event); - extend = (state & GDK_SHIFT_MASK) != 0; - modify = (state & GDK_CONTROL_MASK) != 0; + if (self->selectable) + { + GdkModifierType state; + GdkEvent *event; + gboolean extend, modify; - gtk_widget_activate_action (GTK_WIDGET (self), - "list.select-item", - "(ubb)", - self->position, modify, extend); + event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), + gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture))); + state = gdk_event_get_modifier_state (event); + extend = (state & GDK_SHIFT_MASK) != 0; + modify = (state & GDK_CONTROL_MASK) != 0; + + gtk_widget_activate_action (widget, + "list.select-item", + "(ubb)", + self->position, modify, extend); + } + + if (self->activatable) + { + if (n_press == 2) + { + gtk_widget_activate_action (widget, + "list.activate-item", + "u", + self->position); + } + } gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_ACTIVE, FALSE); @@ -354,6 +442,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 (); @@ -592,3 +681,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 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 aa423218a1..3eb58677b5 100644 --- a/gtk/gtklistitem.h +++ b/gtk/gtklistitem.h @@ -38,20 +38,25 @@ G_BEGIN_DECLS typedef struct _GtkListItem GtkListItem; 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); GDK_AVAILABLE_IN_ALL void gtk_list_item_set_child (GtkListItem *self, 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) @@ -889,6 +895,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) { @@ -971,6 +995,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 diff --git a/gtk/gtktypes.h b/gtk/gtktypes.h index 774568a1d6..f8e68a0e07 100644 --- a/gtk/gtktypes.h +++ b/gtk/gtktypes.h @@ -41,6 +41,7 @@ typedef struct _GtkCssStyleChange GtkCssStyleChange; 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;