PROP_0,
PROP_ACTIVATABLE,
PROP_CHILD,
+ PROP_FOCUSABLE,
PROP_ITEM,
PROP_POSITION,
PROP_SELECTABLE,
g_value_set_object (value, self->child);
break;
+ case PROP_FOCUSABLE:
+ g_value_set_boolean (value, self->focusable);
+ break;
+
case PROP_ITEM:
if (self->owner)
g_value_set_object (value, gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self->owner)));
gtk_list_item_set_child (self, g_value_get_object (value));
break;
+ case PROP_FOCUSABLE:
+ gtk_list_item_set_focusable (self, g_value_get_boolean (value));
+ break;
+
case PROP_SELECTABLE:
gtk_list_item_set_selectable (self, g_value_get_boolean (value));
break;
GTK_TYPE_WIDGET,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+ /**
+ * GtkListItem:focusable: (attributes org.gtk.Property.get=gtk_list_item_get_focusable org.gtk.Property.set=gtk_list_item_set_focusable)
+ *
+ * If the item can be focused with the keyboard.
+ *
+ * Since: 4.12
+ */
+ properties[PROP_FOCUSABLE] =
+ g_param_spec_boolean ("focusable", NULL, NULL,
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
/**
* GtkListItem:item: (attributes org.gtk.Property.get=gtk_list_item_get_item)
*
{
self->selectable = TRUE;
self->activatable = TRUE;
+ self->focusable = TRUE;
}
GtkListItem *
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIVATABLE]);
}
+
+/**
+ * gtk_list_item_get_focusable: (attributes org.gtk.Method.get_property=focusable)
+ * @self: a `GtkListItem`
+ *
+ * Checks if a list item has been set to be focusable via
+ * gtk_list_item_set_focusable().
+ *
+ * Returns: %TRUE if the item is focusable
+ *
+ * Since: 4.12
+ */
+gboolean
+gtk_list_item_get_focusable (GtkListItem *self)
+{
+ g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE);
+
+ return self->focusable;
+}
+
+/**
+ * gtk_list_item_set_focusable: (attributes org.gtk.Method.set_property=focusable)
+ * @self: a `GtkListItem`
+ * @focusable: if the item should be focusable
+ *
+ * Sets @self to be focusable.
+ *
+ * If an item is focusable, it can be focused using the keyboard.
+ * This works similar to [method@Gtk.Widget.set_focusable].
+ *
+ * Note that if items are not focusable, the keyboard cannot be used to activate
+ * them and selecting only works if one of the listitem's children is focusable.
+ *
+ * By default, list items are focusable.
+ *
+ * Since: 4.12
+ */
+void
+gtk_list_item_set_focusable (GtkListItem *self,
+ gboolean focusable)
+{
+ g_return_if_fail (GTK_IS_LIST_ITEM (self));
+
+ if (self->focusable == focusable)
+ return;
+
+ self->focusable = focusable;
+
+ if (self->owner)
+ gtk_widget_set_focusable (GTK_WIDGET (self->owner), focusable);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FOCUSABLE]);
+}
#include "gtklistitemprivate.h"
#include "gtklistbaseprivate.h"
#include "gtkwidget.h"
+#include "gtkwidgetprivate.h"
G_DEFINE_TYPE (GtkListItemWidget, gtk_list_item_widget, GTK_TYPE_LIST_FACTORY_WIDGET)
gtk_list_item_widget_focus (GtkWidget *widget,
GtkDirectionType direction)
{
- GtkWidget *child, *focus_child;
-
- /* The idea of this function is the following:
- * 1. If any child can take focus, do not ever attempt
- * to take focus.
- * 2. Otherwise, if this item is selectable or activatable,
- * allow focusing this widget.
- *
- * This makes sure every item in a list is focusable for
- * activation and selection handling, but no useless widgets
- * get focused and moving focus is as fast as possible.
- */
-
- focus_child = gtk_widget_get_focus_child (widget);
- if (focus_child && gtk_widget_child_focus (focus_child, direction))
- return TRUE;
+ GtkWidget *child = gtk_widget_get_first_child (widget);
- for (child = focus_child ? gtk_widget_get_next_sibling (focus_child)
- : gtk_widget_get_first_child (widget);
- child;
- child = gtk_widget_get_next_sibling (child))
+ if (gtk_widget_get_focus_child (widget))
{
- if (gtk_widget_child_focus (child, direction))
- return TRUE;
+ /* focus is in the child */
+ if (direction == GTK_DIR_TAB_BACKWARD)
+ return gtk_widget_grab_focus_self (widget);
+ else
+ return FALSE;
+ }
+ else if (gtk_widget_is_focus (widget))
+ {
+ /* The widget has focus */
+ if (direction == GTK_DIR_TAB_FORWARD)
+ {
+ if (child)
+ return gtk_widget_child_focus (child, direction);
+ }
+
+ return FALSE;
+ }
+ else
+ {
+ /* focus coming in from the outside */
+ if (direction == GTK_DIR_TAB_BACKWARD)
+ {
+ if (child &&
+ gtk_widget_child_focus (child, direction))
+ return TRUE;
+
+ return gtk_widget_grab_focus_self (widget);
+ }
+ else
+ {
+ if (gtk_widget_grab_focus_self (widget))
+ return TRUE;
+
+ if (child &&
+ gtk_widget_child_focus (child, direction))
+ return TRUE;
+
+ return FALSE;
+ }
}
-
- if (focus_child)
- return FALSE;
-
- if (gtk_widget_is_focus (widget))
- return FALSE;
-
- return gtk_widget_grab_focus (widget);
}
static gboolean
gtk_list_item_widget_grab_focus (GtkWidget *widget)
{
- GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
GtkWidget *child;
- for (child = gtk_widget_get_first_child (widget);
- child;
- child = gtk_widget_get_next_sibling (child))
- {
- if (gtk_widget_grab_focus (child))
- return TRUE;
- }
+ if (GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->grab_focus (widget))
+ return TRUE;
- if (!gtk_list_factory_widget_get_selectable (GTK_LIST_FACTORY_WIDGET (self)))
- return FALSE;
+ child = gtk_widget_get_first_child (widget);
+ if (child && gtk_widget_grab_focus (child))
+ return TRUE;
- return GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->grab_focus (widget);
+ return FALSE;
}
static gpointer
gtk_list_factory_widget_set_activatable (fw, list_item->activatable);
gtk_list_factory_widget_set_selectable (fw, list_item->selectable);
+ gtk_widget_set_focusable (GTK_WIDGET (self), list_item->focusable);
gtk_list_item_do_notify (list_item,
gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self)) != NULL,
gtk_list_factory_widget_set_activatable (fw, FALSE);
gtk_list_factory_widget_set_selectable (fw, FALSE);
+ gtk_widget_set_focusable (GTK_WIDGET (self), TRUE);
gtk_list_item_do_notify (list_item,
gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self)) != NULL,