[gtk/wip/otte/listview: 79/155] listview: Implement activation
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/otte/listview: 79/155] listview: Implement activation
- Date: Tue, 4 Feb 2020 19:41:47 +0000 (UTC)
commit 99a725e4b80930fe1b3922b579da60cace408f2a
Author: Benjamin Otte <otte redhat com>
Date: Tue Oct 15 23:06:36 2019 +0200
listview: Implement activation
- a GtkListview::activate signal
- a GtkListItem::activatable property
- activate list items on double clicks and <Enter> presses
gtk/gtklistitem.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++++-----
gtk/gtklistitem.h | 13 +++--
gtk/gtklistview.c | 60 +++++++++++++++++++
gtk/gtktypes.h | 1 +
4 files changed, 224 insertions(+), 19 deletions(-)
---
diff --git a/gtk/gtklistitem.c b/gtk/gtklistitem.c
index 46d6dc09e0c..2673e46efa7 100644
--- a/gtk/gtklistitem.c
+++ b/gtk/gtklistitem.c
@@ -21,6 +21,7 @@
#include "gtklistitemprivate.h"
+#include "gtkbindings.h"
#include "gtkbinlayout.h"
#include "gtkcssnodeprivate.h"
#include "gtkeventcontrollerkey.h"
@@ -61,6 +62,7 @@ struct _GtkListItem
GtkWidget *child;
guint position;
+ guint activatable : 1;
guint selectable : 1;
guint selected : 1;
};
@@ -68,11 +70,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 +87,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 +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_CHILD:
g_value_set_object (value, self->child);
break;
@@ -188,6 +216,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;
@@ -207,6 +239,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;
@@ -215,6 +250,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 +324,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"));
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
@@ -290,30 +367,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);
@@ -362,6 +454,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 ();
@@ -600,3 +693,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 aa423218a19..3eb58677b55 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 87a80513de8..85a4a5744f0 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 587ce4a8c4d..12ee05189b4 100644
--- a/gtk/gtktypes.h
+++ b/gtk/gtktypes.h
@@ -40,6 +40,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;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]