summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2019-10-15 23:06:36 +0200
committerBenjamin Otte <otte@redhat.com>2019-10-26 08:51:18 +0200
commit16be945de252e9e1876d11a0dde6fe91cce89b84 (patch)
tree1e0bd752b7ae943442258b392559e20148b1e584
parent3a27672f957fb079ffaeaba478d26a646c364704 (diff)
downloadgtk+-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.c174
-rw-r--r--gtk/gtklistitem.h24
-rw-r--r--gtk/gtklistview.c60
-rw-r--r--gtk/gtktypes.h1
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;