From 608be08b0d64dd0f7e4c7d6b91810cb2b2106d6a Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Mon, 20 Mar 2023 06:15:18 +0100 Subject: [PATCH] listview: Add private GtkListFactoryWidget This is a split of GtkListItemWidget into the generic parts of factory using widgets. On top of it there's GtkListItemWidget, which takes care of GtkListItem. --- gtk/gtkcolumnlistitemfactory.c | 2 +- gtk/gtkcolumnview.c | 2 +- gtk/gtkdropdown.c | 2 +- gtk/gtkgridview.c | 6 +- gtk/gtklistfactorywidget.c | 643 ++++++++++++++++++++++++++++++ gtk/gtklistfactorywidgetprivate.h | 86 ++++ gtk/gtklistitem.c | 5 +- gtk/gtklistitemwidget.c | 532 ++---------------------- gtk/gtklistitemwidgetprivate.h | 19 +- gtk/gtklistview.c | 6 +- gtk/meson.build | 1 + 11 files changed, 790 insertions(+), 514 deletions(-) create mode 100644 gtk/gtklistfactorywidget.c create mode 100644 gtk/gtklistfactorywidgetprivate.h diff --git a/gtk/gtkcolumnlistitemfactory.c b/gtk/gtkcolumnlistitemfactory.c index e60057ab10..d36d911f14 100644 --- a/gtk/gtkcolumnlistitemfactory.c +++ b/gtk/gtkcolumnlistitemfactory.c @@ -119,7 +119,7 @@ gtk_column_list_item_factory_update (GtkListItemFactory *factory, gtk_list_item_get_item (list_item), gtk_list_item_get_selected (list_item)); - cell_item = gtk_list_item_widget_get_list_item (GTK_LIST_ITEM_WIDGET (child)); + cell_item = gtk_list_factory_widget_get_object (GTK_LIST_FACTORY_WIDGET (child)); if (cell_item) { selectable &= gtk_list_item_get_selectable (cell_item); diff --git a/gtk/gtkcolumnview.c b/gtk/gtkcolumnview.c index ff325f0a5b..ba1be8f42e 100644 --- a/gtk/gtkcolumnview.c +++ b/gtk/gtkcolumnview.c @@ -146,7 +146,7 @@ gtk_column_list_view_create_list_widget (GtkListBase *base) "row", GTK_ACCESSIBLE_ROLE_ROW); - gtk_list_item_widget_set_single_click_activate (GTK_LIST_ITEM_WIDGET (result), self->single_click_activate); + gtk_list_factory_widget_set_single_click_activate (GTK_LIST_FACTORY_WIDGET (result), self->single_click_activate); return GTK_LIST_ITEM_BASE (result); } diff --git a/gtk/gtkdropdown.c b/gtk/gtkdropdown.c index e0eea43d70..3416bad72f 100644 --- a/gtk/gtkdropdown.c +++ b/gtk/gtkdropdown.c @@ -915,7 +915,7 @@ gtk_drop_down_set_factory (GtkDropDown *self, if (!g_set_object (&self->factory, factory)) return; - gtk_list_item_widget_set_factory (GTK_LIST_ITEM_WIDGET (self->button_item), factory); + gtk_list_factory_widget_set_factory (GTK_LIST_FACTORY_WIDGET (self->button_item), factory); if (self->list_factory == NULL) gtk_list_view_set_factory (GTK_LIST_VIEW (self->popup_list), factory); diff --git a/gtk/gtkgridview.c b/gtk/gtkgridview.c index 991b76a5be..2fcccb58d3 100644 --- a/gtk/gtkgridview.c +++ b/gtk/gtkgridview.c @@ -268,7 +268,7 @@ gtk_grid_view_create_list_widget (GtkListBase *base) "child", GTK_ACCESSIBLE_ROLE_GRID_CELL); - gtk_list_item_widget_set_single_click_activate (GTK_LIST_ITEM_WIDGET (result), self->single_click_activate); + gtk_list_factory_widget_set_single_click_activate (GTK_LIST_FACTORY_WIDGET (result), self->single_click_activate); return GTK_LIST_ITEM_BASE (result); } @@ -1230,7 +1230,7 @@ gtk_grid_view_set_factory (GtkGridView *self, tile = gtk_rb_tree_node_get_next (tile)) { if (tile->widget) - gtk_list_item_widget_set_factory (GTK_LIST_ITEM_WIDGET (tile->widget), factory); + gtk_list_factory_widget_set_factory (GTK_LIST_FACTORY_WIDGET (tile->widget), factory); } @@ -1357,7 +1357,7 @@ gtk_grid_view_set_single_click_activate (GtkGridView *self, tile = gtk_rb_tree_node_get_next (tile)) { if (tile->widget) - gtk_list_item_widget_set_single_click_activate (GTK_LIST_ITEM_WIDGET (tile->widget), single_click_activate); + gtk_list_factory_widget_set_single_click_activate (GTK_LIST_FACTORY_WIDGET (tile->widget), single_click_activate); } diff --git a/gtk/gtklistfactorywidget.c b/gtk/gtklistfactorywidget.c new file mode 100644 index 0000000000..8e0197e6ee --- /dev/null +++ b/gtk/gtklistfactorywidget.c @@ -0,0 +1,643 @@ +/* + * Copyright © 2023 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Benjamin Otte + */ + +#include "config.h" + +#include "gtklistfactorywidgetprivate.h" + +#include "gtkbinlayout.h" +#include "gtkeventcontrollermotion.h" +#include "gtkgestureclick.h" +#include "gtklistitemfactoryprivate.h" +#include "gtklistbaseprivate.h" +#include "gtkwidget.h" + +typedef struct _GtkListFactoryWidgetPrivate GtkListFactoryWidgetPrivate; +struct _GtkListFactoryWidgetPrivate +{ + GtkListItemFactory *factory; + + gpointer object; + gboolean single_click_activate; + gboolean selectable; + gboolean activatable; +}; + +enum { + PROP_0, + PROP_ACTIVATABLE, + PROP_FACTORY, + PROP_SELECTABLE, + PROP_SINGLE_CLICK_ACTIVATE, + + N_PROPS +}; + +enum +{ + ACTIVATE_SIGNAL, + LAST_SIGNAL +}; + +G_DEFINE_TYPE_WITH_PRIVATE (GtkListFactoryWidget, gtk_list_factory_widget, GTK_TYPE_LIST_ITEM_BASE) + +static GParamSpec *properties[N_PROPS] = { NULL, }; +static guint signals[LAST_SIGNAL] = { 0 }; + +static void +gtk_list_factory_widget_activate_signal (GtkListFactoryWidget *self) +{ + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + + if (priv->activatable) + return; + + gtk_widget_activate_action (GTK_WIDGET (self), + "list.activate-item", + "u", + gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self))); +} + +static gpointer +gtk_list_factory_widget_default_create_object (GtkListFactoryWidget *self) +{ + g_assert_not_reached (); + return NULL; +} + +static void +gtk_list_factory_widget_default_setup_object (GtkListFactoryWidget *self, + gpointer object) +{ + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + + priv->object = object; +} + +static void +gtk_list_factory_widget_setup_func (gpointer object, + gpointer data) +{ + GTK_LIST_FACTORY_WIDGET_GET_CLASS (data)->setup_object (data, object); +} + +static void +gtk_list_factory_widget_setup_factory (GtkListFactoryWidget *self) +{ + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + gpointer object; + + object = GTK_LIST_FACTORY_WIDGET_GET_CLASS (self)->create_object (self); + + gtk_list_item_factory_setup (priv->factory, + object, + gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self)) != NULL, + gtk_list_factory_widget_setup_func, + self); + + g_assert (priv->object == object); +} + +static void +gtk_list_factory_widget_default_teardown_object (GtkListFactoryWidget *self, + gpointer object) +{ + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + + priv->object = NULL; +} + +static void +gtk_list_factory_widget_teardown_func (gpointer object, + gpointer data) +{ + GTK_LIST_FACTORY_WIDGET_GET_CLASS (data)->teardown_object (data, object); +} + +static void +gtk_list_factory_widget_teardown_factory (GtkListFactoryWidget *self) +{ + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + gpointer item = priv->object; + + gtk_list_item_factory_teardown (priv->factory, + item, + gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self)) != NULL, + gtk_list_factory_widget_teardown_func, + self); + + g_assert (priv->object == NULL); + g_object_unref (item); +} + +static void +gtk_list_factory_widget_default_update_object (GtkListFactoryWidget *self, + gpointer object, + guint position, + gpointer item, + gboolean selected) +{ + GTK_LIST_ITEM_BASE_CLASS (gtk_list_factory_widget_parent_class)->update (GTK_LIST_ITEM_BASE (self), + position, + item, + selected); +} + +typedef struct { + GtkListFactoryWidget *widget; + guint position; + gpointer item; + gboolean selected; +} GtkListFactoryWidgetUpdate; + +static void +gtk_list_factory_widget_update_func (gpointer object, + gpointer data) +{ + GtkListFactoryWidgetUpdate *update = data; + + GTK_LIST_FACTORY_WIDGET_GET_CLASS (update->widget)->update_object (update->widget, + object, + update->position, + update->item, + update->selected); +} + +static void +gtk_list_factory_widget_update (GtkListItemBase *base, + guint position, + gpointer item, + gboolean selected) +{ + GtkListFactoryWidget *self = GTK_LIST_FACTORY_WIDGET (base); + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + GtkListFactoryWidgetUpdate update = { self, position, item, selected }; + + if (priv->object) + { + gtk_list_item_factory_update (priv->factory, + priv->object, + gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self)) != NULL, + item != NULL, + gtk_list_factory_widget_update_func, + &update); + } + else + { + gtk_list_factory_widget_update_func (NULL, &update); + } +} + +static void +gtk_list_factory_widget_root (GtkWidget *widget) +{ + GtkListFactoryWidget *self = GTK_LIST_FACTORY_WIDGET (widget); + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + + GTK_WIDGET_CLASS (gtk_list_factory_widget_parent_class)->root (widget); + + if (priv->factory) + gtk_list_factory_widget_setup_factory (self); +} + +static void +gtk_list_factory_widget_unroot (GtkWidget *widget) +{ + GtkListFactoryWidget *self = GTK_LIST_FACTORY_WIDGET (widget); + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + + GTK_WIDGET_CLASS (gtk_list_factory_widget_parent_class)->unroot (widget); + + if (priv->object) + gtk_list_factory_widget_teardown_factory (self); +} + +static void +gtk_list_factory_widget_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkListFactoryWidget *self = GTK_LIST_FACTORY_WIDGET (object); + + switch (property_id) + { + case PROP_ACTIVATABLE: + gtk_list_factory_widget_set_activatable (self, g_value_get_boolean (value)); + break; + + case PROP_FACTORY: + gtk_list_factory_widget_set_factory (self, g_value_get_object (value)); + break; + + case PROP_SELECTABLE: + gtk_list_factory_widget_set_selectable (self, g_value_get_boolean (value)); + break; + + case PROP_SINGLE_CLICK_ACTIVATE: + gtk_list_factory_widget_set_single_click_activate (self, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gtk_list_factory_widget_dispose (GObject *object) +{ + GtkListFactoryWidget *self = GTK_LIST_FACTORY_WIDGET (object); + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + + g_assert (priv->object == NULL); + + g_clear_object (&priv->factory); + + G_OBJECT_CLASS (gtk_list_factory_widget_parent_class)->dispose (object); +} + +static void +gtk_list_factory_widget_select_action (GtkWidget *widget, + const char *action_name, + GVariant *parameter) +{ + GtkListFactoryWidget *self = GTK_LIST_FACTORY_WIDGET (widget); + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + gboolean modify, extend; + + if (!priv->selectable) + return; + + g_variant_get (parameter, "(bb)", &modify, &extend); + + gtk_widget_activate_action (GTK_WIDGET (self), + "list.select-item", + "(ubb)", + gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self)), modify, extend); +} + +static void +gtk_list_factory_widget_scroll_to_action (GtkWidget *widget, + const char *action_name, + GVariant *parameter) +{ + gtk_widget_activate_action (widget, + "list.scroll-to-item", + "u", + gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (widget))); +} + +static void +gtk_list_factory_widget_class_init (GtkListFactoryWidgetClass *klass) +{ + GtkListItemBaseClass *base_class = GTK_LIST_ITEM_BASE_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + klass->activate_signal = gtk_list_factory_widget_activate_signal; + klass->create_object = gtk_list_factory_widget_default_create_object; + klass->setup_object = gtk_list_factory_widget_default_setup_object; + klass->update_object = gtk_list_factory_widget_default_update_object; + klass->teardown_object = gtk_list_factory_widget_default_teardown_object; + + base_class->update = gtk_list_factory_widget_update; + + widget_class->root = gtk_list_factory_widget_root; + widget_class->unroot = gtk_list_factory_widget_unroot; + + gobject_class->set_property = gtk_list_factory_widget_set_property; + gobject_class->dispose = gtk_list_factory_widget_dispose; + + properties[PROP_ACTIVATABLE] = + g_param_spec_boolean ("activatable", NULL, NULL, + FALSE, + G_PARAM_WRITABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + properties[PROP_FACTORY] = + g_param_spec_object ("factory", NULL, NULL, + GTK_TYPE_LIST_ITEM_FACTORY, + G_PARAM_WRITABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + properties[PROP_SELECTABLE] = + g_param_spec_boolean ("selectable", NULL, NULL, + FALSE, + G_PARAM_WRITABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + properties[PROP_SINGLE_CLICK_ACTIVATE] = + g_param_spec_boolean ("single-click-activate", NULL, NULL, + FALSE, + G_PARAM_WRITABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (gobject_class, N_PROPS, properties); + + 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 (GtkListFactoryWidgetClass, activate_signal), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + gtk_widget_class_set_activate_signal (widget_class, signals[ACTIVATE_SIGNAL]); + + /** + * GtkListItem|listitem.select: + * @modify: %TRUE to toggle the existing selection, %FALSE to select + * @extend: %TRUE to extend the selection + * + * Changes selection if the item is selectable. + * If the item is not selectable, nothing happens. + * + * This function will emit the list.select-item action and the resulting + * behavior, in particular the interpretation of @modify and @extend + * depends on the view containing this listitem. See for example + * GtkListView|list.select-item or GtkGridView|list.select-item. + */ + gtk_widget_class_install_action (widget_class, + "listitem.select", + "(bb)", + gtk_list_factory_widget_select_action); + + /** + * GtkListItem|listitem.scroll-to: + * + * Moves the visible area of the list to this item with the minimum amount + * of scrolling required. If the item is already visible, nothing happens. + */ + gtk_widget_class_install_action (widget_class, + "listitem.scroll-to", + NULL, + gtk_list_factory_widget_scroll_to_action); + + 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); + + /* note that some of these may get overwritten by child widgets, + * such as GtkTreeExpander */ + gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, 0, + "listitem.select", "(bb)", TRUE, FALSE); + gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_CONTROL_MASK, + "listitem.select", "(bb)", TRUE, FALSE); + gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_SHIFT_MASK, + "listitem.select", "(bb)", TRUE, FALSE); + gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "listitem.select", "(bb)", TRUE, FALSE); + gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, 0, + "listitem.select", "(bb)", TRUE, FALSE); + gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_CONTROL_MASK, + "listitem.select", "(bb)", TRUE, FALSE); + gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_SHIFT_MASK, + "listitem.select", "(bb)", TRUE, FALSE); + gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "listitem.select", "(bb)", TRUE, FALSE); + + /* This gets overwritten by gtk_list_factory_widget_new() but better safe than sorry */ + gtk_widget_class_set_css_name (widget_class, I_("row")); +} + +static void +gtk_list_factory_widget_click_gesture_pressed (GtkGestureClick *gesture, + int n_press, + double x, + double y, + GtkListFactoryWidget *self) +{ + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + GtkWidget *widget = GTK_WIDGET (self); + + if (!priv->selectable && !priv->activatable) + { + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); + return; + } + + if (priv->activatable) + { + if (n_press == 2 && !priv->single_click_activate) + { + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); + gtk_widget_activate_action (GTK_WIDGET (self), + "list.activate-item", + "u", + gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self))); + } + } + + if (gtk_widget_get_focus_on_click (widget)) + gtk_widget_grab_focus (widget); +} + +static void +gtk_list_factory_widget_click_gesture_released (GtkGestureClick *gesture, + int n_press, + double x, + double y, + GtkListFactoryWidget *self) +{ + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + + if (priv->activatable) + { + if (n_press == 1 && priv->single_click_activate) + { + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); + gtk_widget_activate_action (GTK_WIDGET (self), + "list.activate-item", + "u", + gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self))); + return; + } + } + + if (priv->selectable) + { + GdkModifierType state; + GdkEvent *event; + gboolean extend, modify; + + 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 (GTK_WIDGET (self), + "list.select-item", + "(ubb)", + gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self)), modify, extend); + } +} + +static void +gtk_list_factory_widget_hover_cb (GtkEventControllerMotion *controller, + double x, + double y, + GtkListFactoryWidget *self) +{ + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + + if (!priv->single_click_activate) + return; + + if (priv->selectable) + { + gtk_widget_activate_action (GTK_WIDGET (self), + "list.select-item", + "(ubb)", + gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self)), FALSE, FALSE); + } +} + +static void +gtk_list_factory_widget_init (GtkListFactoryWidget *self) +{ + GtkEventController *controller; + GtkGesture *gesture; + + gtk_widget_set_focusable (GTK_WIDGET (self), TRUE); + + gesture = gtk_gesture_click_new (); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), + GTK_PHASE_BUBBLE); + gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), + FALSE); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), + GDK_BUTTON_PRIMARY); + g_signal_connect (gesture, "pressed", + G_CALLBACK (gtk_list_factory_widget_click_gesture_pressed), self); + g_signal_connect (gesture, "released", + G_CALLBACK (gtk_list_factory_widget_click_gesture_released), self); + gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture)); + + controller = gtk_event_controller_motion_new (); + g_signal_connect (controller, "enter", G_CALLBACK (gtk_list_factory_widget_hover_cb), self); + gtk_widget_add_controller (GTK_WIDGET (self), controller); +} + +gpointer +gtk_list_factory_widget_get_object (GtkListFactoryWidget *self) +{ + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + + return priv->object; +} + +void +gtk_list_factory_widget_set_factory (GtkListFactoryWidget *self, + GtkListItemFactory *factory) +{ + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + + if (priv->factory == factory) + return; + + if (priv->factory) + { + if (priv->object) + gtk_list_factory_widget_teardown_factory (self); + g_clear_object (&priv->factory); + } + + if (factory) + { + priv->factory = g_object_ref (factory); + + if (gtk_widget_get_root (GTK_WIDGET (self))) + gtk_list_factory_widget_setup_factory (self); + } + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]); +} + +void +gtk_list_factory_widget_set_single_click_activate (GtkListFactoryWidget *self, + gboolean single_click_activate) +{ + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + + if (priv->single_click_activate == single_click_activate) + return; + + priv->single_click_activate = single_click_activate; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SINGLE_CLICK_ACTIVATE]); +} + +gboolean +gtk_list_factory_widget_get_single_click_activate (GtkListFactoryWidget *self) +{ + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + + return priv->single_click_activate; +} + +void +gtk_list_factory_widget_set_activatable (GtkListFactoryWidget *self, + gboolean activatable) +{ + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + + if (priv->activatable == activatable) + return; + + priv->activatable = activatable; + + if (activatable) + gtk_widget_add_css_class (GTK_WIDGET (self), "activatable"); + else + gtk_widget_remove_css_class (GTK_WIDGET (self), "activatable"); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SINGLE_CLICK_ACTIVATE]); +} + +gboolean +gtk_list_factory_widget_get_activatable (GtkListFactoryWidget *self) +{ + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + + return priv->activatable; +} + +void +gtk_list_factory_widget_set_selectable (GtkListFactoryWidget *self, + gboolean selectable) +{ + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + + if (priv->selectable == selectable) + return; + + priv->selectable = selectable; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SINGLE_CLICK_ACTIVATE]); +} + +gboolean +gtk_list_factory_widget_get_selectable (GtkListFactoryWidget *self) +{ + GtkListFactoryWidgetPrivate *priv = gtk_list_factory_widget_get_instance_private (self); + + return priv->selectable; +} diff --git a/gtk/gtklistfactorywidgetprivate.h b/gtk/gtklistfactorywidgetprivate.h new file mode 100644 index 0000000000..ee01a64c51 --- /dev/null +++ b/gtk/gtklistfactorywidgetprivate.h @@ -0,0 +1,86 @@ +/* + * Copyright © 2023 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Benjamin Otte + */ + +#ifndef __GTK_LIST_FACTORY_WIDGET_PRIVATE_H__ +#define __GTK_LIST_FACTORY_WIDGET_PRIVATE_H__ + +#include "gtklistitembaseprivate.h" + +#include "gtklistitemfactory.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_LIST_FACTORY_WIDGET (gtk_list_factory_widget_get_type ()) +#define GTK_LIST_FACTORY_WIDGET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_FACTORY_WIDGET, GtkListFactoryWidget)) +#define GTK_LIST_FACTORY_WIDGET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_FACTORY_WIDGET, GtkListFactoryWidgetClass)) +#define GTK_IS_LIST_FACTORY_WIDGET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_FACTORY_WIDGET)) +#define GTK_IS_LIST_FACTORY_WIDGET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_FACTORY_WIDGET)) +#define GTK_LIST_FACTORY_WIDGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_FACTORY_WIDGET, GtkListFactoryWidgetClass)) + +typedef struct _GtkListFactoryWidget GtkListFactoryWidget; +typedef struct _GtkListFactoryWidgetClass GtkListFactoryWidgetClass; + +struct _GtkListFactoryWidget +{ + GtkListItemBase parent_instance; +}; + +struct _GtkListFactoryWidgetClass +{ + GtkListItemBaseClass parent_class; + + void (* activate_signal) (GtkListFactoryWidget *self); + + gpointer (* create_object) (GtkListFactoryWidget *self); + void (* setup_object) (GtkListFactoryWidget *self, + gpointer object); + void (* update_object) (GtkListFactoryWidget *self, + gpointer object, + guint position, + gpointer item, + gboolean selected); + void (* teardown_object) (GtkListFactoryWidget *self, + gpointer object); +}; + +GType gtk_list_factory_widget_get_type (void) G_GNUC_CONST; + +gpointer gtk_list_factory_widget_get_object (GtkListFactoryWidget *self); + +void gtk_list_factory_widget_set_factory (GtkListFactoryWidget *self, + GtkListItemFactory *factory); +GtkListItemFactory * gtk_list_factory_widget_get_factory (GtkListFactoryWidget *self); + +void gtk_list_factory_widget_set_single_click_activate + (GtkListFactoryWidget *self, + gboolean single_click_activate); +gboolean gtk_list_factory_widget_get_single_click_activate + (GtkListFactoryWidget *self); + +void gtk_list_factory_widget_set_activatable (GtkListFactoryWidget *self, + gboolean activatable); +gboolean gtk_list_factory_widget_get_activatable (GtkListFactoryWidget *self); + +void gtk_list_factory_widget_set_selectable (GtkListFactoryWidget *self, + gboolean activatable); +gboolean gtk_list_factory_widget_get_selectable (GtkListFactoryWidget *self); + +G_END_DECLS + +#endif /* __GTK_LIST_FACTORY_WIDGET_PRIVATE_H__ */ diff --git a/gtk/gtklistitem.c b/gtk/gtklistitem.c index 837784bd47..3bea81c5df 100644 --- a/gtk/gtklistitem.c +++ b/gtk/gtklistitem.c @@ -417,6 +417,9 @@ gtk_list_item_set_selectable (GtkListItem *self, self->selectable = selectable; + if (self->owner) + gtk_list_factory_widget_set_selectable (GTK_LIST_FACTORY_WIDGET (self->owner), selectable); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTABLE]); } @@ -464,7 +467,7 @@ gtk_list_item_set_activatable (GtkListItem *self, self->activatable = activatable; if (self->owner) - gtk_list_item_widget_set_activatable (self->owner, activatable); + gtk_list_factory_widget_set_activatable (GTK_LIST_FACTORY_WIDGET (self->owner), activatable); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIVATABLE]); } diff --git a/gtk/gtklistitemwidget.c b/gtk/gtklistitemwidget.c index 5b770bf030..31489b4f81 100644 --- a/gtk/gtklistitemwidget.c +++ b/gtk/gtklistitemwidget.c @@ -32,47 +32,7 @@ #include "gtkwidget.h" #include "gtkwidgetprivate.h" -typedef struct _GtkListItemWidgetPrivate GtkListItemWidgetPrivate; -struct _GtkListItemWidgetPrivate -{ - GtkListItemFactory *factory; - GtkListItem *list_item; - - gboolean single_click_activate; -}; - -enum { - PROP_0, - PROP_FACTORY, - PROP_SINGLE_CLICK_ACTIVATE, - - N_PROPS -}; - -enum -{ - ACTIVATE_SIGNAL, - LAST_SIGNAL -}; - -G_DEFINE_TYPE_WITH_PRIVATE (GtkListItemWidget, gtk_list_item_widget, GTK_TYPE_LIST_ITEM_BASE) - -static GParamSpec *properties[N_PROPS] = { NULL, }; -static guint signals[LAST_SIGNAL] = { 0 }; - -static void -gtk_list_item_widget_activate_signal (GtkListItemWidget *self) -{ - GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self); - - if (priv->list_item && !priv->list_item->activatable) - return; - - gtk_widget_activate_action (GTK_WIDGET (self), - "list.activate-item", - "u", - gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self))); -} +G_DEFINE_TYPE (GtkListItemWidget, gtk_list_item_widget, GTK_TYPE_LIST_FACTORY_WIDGET) static gboolean gtk_list_item_widget_focus (GtkWidget *widget, @@ -117,7 +77,6 @@ static gboolean gtk_list_item_widget_grab_focus (GtkWidget *widget) { GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget); - GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self); GtkWidget *child; for (child = gtk_widget_get_first_child (widget); @@ -128,28 +87,34 @@ gtk_list_item_widget_grab_focus (GtkWidget *widget) return TRUE; } - if (priv->list_item == NULL || - !priv->list_item->selectable) + if (!gtk_list_factory_widget_get_selectable (GTK_LIST_FACTORY_WIDGET (self))) return FALSE; return GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->grab_focus (widget); } +static gpointer +gtk_list_item_widget_create_object (GtkListFactoryWidget *fw) +{ + return gtk_list_item_new (); +} + static void -gtk_list_item_widget_setup_func (gpointer object, - gpointer data) +gtk_list_item_widget_setup_object (GtkListFactoryWidget *fw, + gpointer object) { - GtkListItemWidget *self = data; - GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self); + GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (fw); GtkListItem *list_item = object; - priv->list_item = list_item; + GTK_LIST_FACTORY_WIDGET_CLASS (gtk_list_item_widget_parent_class)->setup_object (fw, object); + list_item->owner = self; if (list_item->child) gtk_list_item_widget_add_child (self, list_item->child); - gtk_list_item_widget_set_activatable (self, list_item->activatable); + gtk_list_factory_widget_set_activatable (fw, list_item->activatable); + gtk_list_factory_widget_set_selectable (fw, list_item->selectable); gtk_list_item_do_notify (list_item, gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self)) != NULL, @@ -158,432 +123,82 @@ gtk_list_item_widget_setup_func (gpointer object, } static void -gtk_list_item_widget_setup_factory (GtkListItemWidget *self) -{ - GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self); - GtkListItem *list_item; - - list_item = gtk_list_item_new (); - - gtk_list_item_factory_setup (priv->factory, - G_OBJECT (list_item), - gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self)) != NULL, - gtk_list_item_widget_setup_func, - self); - - g_assert (priv->list_item == list_item); -} - -static void -gtk_list_item_widget_teardown_func (gpointer object, - gpointer data) +gtk_list_item_widget_teardown_object (GtkListFactoryWidget *fw, + gpointer object) { - GtkListItemWidget *self = data; - GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self); + GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (fw); GtkListItem *list_item = object; - g_assert (priv->list_item == list_item); + GTK_LIST_FACTORY_WIDGET_CLASS (gtk_list_item_widget_parent_class)->teardown_object (fw, object); - priv->list_item = NULL; list_item->owner = NULL; if (list_item->child) gtk_list_item_widget_remove_child (self, list_item->child); - gtk_list_item_widget_set_activatable (self, FALSE); + gtk_list_factory_widget_set_activatable (fw, FALSE); + gtk_list_factory_widget_set_selectable (fw, FALSE); gtk_list_item_do_notify (list_item, gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self)) != NULL, gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self)) != GTK_INVALID_LIST_POSITION, gtk_list_item_base_get_selected (GTK_LIST_ITEM_BASE (self))); + /* FIXME: This is technically not correct, the child is user code, isn't it? */ gtk_list_item_set_child (list_item, NULL); } static void -gtk_list_item_widget_teardown_factory (GtkListItemWidget *self) -{ - GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self); - GtkListItem *list_item = priv->list_item; - - gtk_list_item_factory_teardown (priv->factory, - G_OBJECT (list_item), - gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self)) != NULL, - gtk_list_item_widget_teardown_func, - self); - - g_assert (priv->list_item == NULL); - g_object_unref (list_item); -} - -typedef struct { - GtkListItemWidget *widget; - guint position; - gpointer item; - gboolean selected; -} GtkListItemWidgetUpdate; - -static void -gtk_list_item_widget_update_func (gpointer object, - gpointer data) +gtk_list_item_widget_update_object (GtkListFactoryWidget *fw, + gpointer object, + guint position, + gpointer item, + gboolean selected) { - GtkListItemWidgetUpdate *update = data; + GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (fw); + GtkListItemBase *base = GTK_LIST_ITEM_BASE (self); GtkListItem *list_item = object; /* Track notify manually instead of freeze/thaw_notify for performance reasons. */ gboolean notify_item = FALSE, notify_position = FALSE, notify_selected = FALSE; - GtkListItemWidget *self = update->widget; - GtkListItemBase *base = GTK_LIST_ITEM_BASE (self); /* FIXME: It's kinda evil to notify external objects from here... */ - notify_item = gtk_list_item_base_get_item (base) != update->item; - notify_position = gtk_list_item_base_get_position (base) != update->position; - notify_selected = gtk_list_item_base_get_selected (base) != update->selected; - - GTK_LIST_ITEM_BASE_CLASS (gtk_list_item_widget_parent_class)->update (base, - update->position, - update->item, - update->selected); - if (list_item) - gtk_list_item_do_notify (list_item, notify_item, notify_position, notify_selected); -} - -static void -gtk_list_item_widget_update (GtkListItemBase *base, - guint position, - gpointer item, - gboolean selected) -{ - GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (base); - GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self); - GtkListItemWidgetUpdate update = { self, position, item, selected }; - - if (priv->list_item) - { - gtk_list_item_factory_update (priv->factory, - G_OBJECT (priv->list_item), - gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self)) != NULL, - item != NULL, - gtk_list_item_widget_update_func, - &update); - } - else - { - gtk_list_item_widget_update_func (NULL, &update); - } -} - -static void -gtk_list_item_widget_root (GtkWidget *widget) -{ - GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget); - GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self); - - GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->root (widget); - - if (priv->factory) - gtk_list_item_widget_setup_factory (self); -} - -static void -gtk_list_item_widget_unroot (GtkWidget *widget) -{ - GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget); - GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self); - - GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->unroot (widget); - - if (priv->list_item) - gtk_list_item_widget_teardown_factory (self); -} - -static void -gtk_list_item_widget_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (object); - - switch (property_id) - { - case PROP_FACTORY: - gtk_list_item_widget_set_factory (self, g_value_get_object (value)); - break; - - case PROP_SINGLE_CLICK_ACTIVATE: - gtk_list_item_widget_set_single_click_activate (self, g_value_get_boolean (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gtk_list_item_widget_dispose (GObject *object) -{ - GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (object); - GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self); - - g_assert (priv->list_item == NULL); - - g_clear_object (&priv->factory); - - G_OBJECT_CLASS (gtk_list_item_widget_parent_class)->dispose (object); -} - -static void -gtk_list_item_widget_select_action (GtkWidget *widget, - const char *action_name, - GVariant *parameter) -{ - GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget); - GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self); - gboolean modify, extend; - - if (priv->list_item && !priv->list_item->selectable) - return; + notify_item = gtk_list_item_base_get_item (base) != item; + notify_position = gtk_list_item_base_get_position (base) != position; + notify_selected = gtk_list_item_base_get_selected (base) != selected; - g_variant_get (parameter, "(bb)", &modify, &extend); + GTK_LIST_FACTORY_WIDGET_CLASS (gtk_list_item_widget_parent_class)->update_object (fw, + object, + position, + item, + selected); - gtk_widget_activate_action (GTK_WIDGET (self), - "list.select-item", - "(ubb)", - gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self)), modify, extend); -} - -static void -gtk_list_item_widget_scroll_to_action (GtkWidget *widget, - const char *action_name, - GVariant *parameter) -{ - gtk_widget_activate_action (widget, - "list.scroll-to-item", - "u", - gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (widget))); + if (list_item) + gtk_list_item_do_notify (list_item, notify_item, notify_position, notify_selected); } static void gtk_list_item_widget_class_init (GtkListItemWidgetClass *klass) { - GtkListItemBaseClass *base_class = GTK_LIST_ITEM_BASE_CLASS (klass); + GtkListFactoryWidgetClass *factory_class = GTK_LIST_FACTORY_WIDGET_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - klass->activate_signal = gtk_list_item_widget_activate_signal; - - base_class->update = gtk_list_item_widget_update; + factory_class->create_object = gtk_list_item_widget_create_object; + factory_class->setup_object = gtk_list_item_widget_setup_object; + factory_class->update_object = gtk_list_item_widget_update_object; + factory_class->teardown_object = gtk_list_item_widget_teardown_object; widget_class->focus = gtk_list_item_widget_focus; widget_class->grab_focus = gtk_list_item_widget_grab_focus; - widget_class->root = gtk_list_item_widget_root; - widget_class->unroot = gtk_list_item_widget_unroot; - - gobject_class->set_property = gtk_list_item_widget_set_property; - gobject_class->dispose = gtk_list_item_widget_dispose; - - properties[PROP_FACTORY] = - g_param_spec_object ("factory", NULL, NULL, - GTK_TYPE_LIST_ITEM_FACTORY, - G_PARAM_WRITABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); - - properties[PROP_SINGLE_CLICK_ACTIVATE] = - g_param_spec_boolean ("single-click-activate", NULL, NULL, - FALSE, - G_PARAM_WRITABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (gobject_class, N_PROPS, properties); - - 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 (GtkListItemWidgetClass, activate_signal), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - gtk_widget_class_set_activate_signal (widget_class, signals[ACTIVATE_SIGNAL]); - - /** - * GtkListItem|listitem.select: - * @modify: %TRUE to toggle the existing selection, %FALSE to select - * @extend: %TRUE to extend the selection - * - * Changes selection if the item is selectable. - * If the item is not selectable, nothing happens. - * - * This function will emit the list.select-item action and the resulting - * behavior, in particular the interpretation of @modify and @extend - * depends on the view containing this listitem. See for example - * GtkListView|list.select-item or GtkGridView|list.select-item. - */ - gtk_widget_class_install_action (widget_class, - "listitem.select", - "(bb)", - gtk_list_item_widget_select_action); - - /** - * GtkListItem|listitem.scroll-to: - * - * Moves the visible area of the list to this item with the minimum amount - * of scrolling required. If the item is already visible, nothing happens. - */ - gtk_widget_class_install_action (widget_class, - "listitem.scroll-to", - NULL, - gtk_list_item_widget_scroll_to_action); - - 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); - - /* note that some of these may get overwritten by child widgets, - * such as GtkTreeExpander */ - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, 0, - "listitem.select", "(bb)", TRUE, FALSE); - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_CONTROL_MASK, - "listitem.select", "(bb)", TRUE, FALSE); - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_SHIFT_MASK, - "listitem.select", "(bb)", TRUE, FALSE); - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "listitem.select", "(bb)", TRUE, FALSE); - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, 0, - "listitem.select", "(bb)", TRUE, FALSE); - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_CONTROL_MASK, - "listitem.select", "(bb)", TRUE, FALSE); - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_SHIFT_MASK, - "listitem.select", "(bb)", TRUE, FALSE); - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "listitem.select", "(bb)", TRUE, FALSE); /* This gets overwritten by gtk_list_item_widget_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); } -static void -gtk_list_item_widget_click_gesture_pressed (GtkGestureClick *gesture, - int n_press, - double x, - double y, - GtkListItemWidget *self) -{ - GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self); - GtkWidget *widget = GTK_WIDGET (self); - - if (priv->list_item && !priv->list_item->selectable && !priv->list_item->activatable) - { - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); - return; - } - - if (!priv->list_item || priv->list_item->activatable) - { - if (n_press == 2 && !priv->single_click_activate) - { - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); - gtk_widget_activate_action (GTK_WIDGET (self), - "list.activate-item", - "u", - gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self))); - } - } - - if (gtk_widget_get_focus_on_click (widget)) - gtk_widget_grab_focus (widget); -} - -static void -gtk_list_item_widget_click_gesture_released (GtkGestureClick *gesture, - int n_press, - double x, - double y, - GtkListItemWidget *self) -{ - GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self); - - if (!priv->list_item || priv->list_item->activatable) - { - if (n_press == 1 && priv->single_click_activate) - { - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); - gtk_widget_activate_action (GTK_WIDGET (self), - "list.activate-item", - "u", - gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self))); - return; - } - } - - if (!priv->list_item || priv->list_item->selectable) - { - GdkModifierType state; - GdkEvent *event; - gboolean extend, modify; - - 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_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); - gtk_widget_activate_action (GTK_WIDGET (self), - "list.select-item", - "(ubb)", - gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self)), modify, extend); - } -} - -static void -gtk_list_item_widget_hover_cb (GtkEventControllerMotion *controller, - double x, - double y, - GtkListItemWidget *self) -{ - GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self); - - if (!priv->single_click_activate) - return; - - if (!priv->list_item || priv->list_item->selectable) - { - gtk_widget_activate_action (GTK_WIDGET (self), - "list.select-item", - "(ubb)", - gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self)), FALSE, FALSE); - } -} - static void gtk_list_item_widget_init (GtkListItemWidget *self) { - GtkEventController *controller; - GtkGesture *gesture; - gtk_widget_set_focusable (GTK_WIDGET (self), TRUE); - - gesture = gtk_gesture_click_new (); - gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), - GTK_PHASE_BUBBLE); - gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), - FALSE); - gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), - GDK_BUTTON_PRIMARY); - g_signal_connect (gesture, "pressed", - G_CALLBACK (gtk_list_item_widget_click_gesture_pressed), self); - g_signal_connect (gesture, "released", - G_CALLBACK (gtk_list_item_widget_click_gesture_released), self); - gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture)); - - controller = gtk_event_controller_motion_new (); - g_signal_connect (controller, "enter", G_CALLBACK (gtk_list_item_widget_hover_cb), self); - gtk_widget_add_controller (GTK_WIDGET (self), controller); } GtkWidget * @@ -600,57 +215,6 @@ gtk_list_item_widget_new (GtkListItemFactory *factory, NULL); } -void -gtk_list_item_widget_set_factory (GtkListItemWidget *self, - GtkListItemFactory *factory) -{ - GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self); - - if (priv->factory == factory) - return; - - if (priv->factory) - { - if (priv->list_item) - gtk_list_item_widget_teardown_factory (self); - g_clear_object (&priv->factory); - } - - if (factory) - { - priv->factory = g_object_ref (factory); - - if (gtk_widget_get_root (GTK_WIDGET (self))) - gtk_list_item_widget_setup_factory (self); - } - - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]); -} - -void -gtk_list_item_widget_set_single_click_activate (GtkListItemWidget *self, - gboolean single_click_activate) -{ - GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self); - - if (priv->single_click_activate == single_click_activate) - return; - - priv->single_click_activate = single_click_activate; - - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SINGLE_CLICK_ACTIVATE]); -} - -void -gtk_list_item_widget_set_activatable (GtkListItemWidget *self, - gboolean activatable) -{ - if (activatable) - gtk_widget_add_css_class (GTK_WIDGET (self), "activatable"); - else - gtk_widget_remove_css_class (GTK_WIDGET (self), "activatable"); -} - void gtk_list_item_widget_add_child (GtkListItemWidget *self, GtkWidget *child) @@ -694,11 +258,3 @@ gtk_list_item_widget_remove_child (GtkListItemWidget *self, gtk_widget_unparent (child); } -GtkListItem * -gtk_list_item_widget_get_list_item (GtkListItemWidget *self) -{ - GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self); - - return priv->list_item; -} - diff --git a/gtk/gtklistitemwidgetprivate.h b/gtk/gtklistitemwidgetprivate.h index 5345419a19..154eb2cbd2 100644 --- a/gtk/gtklistitemwidgetprivate.h +++ b/gtk/gtklistitemwidgetprivate.h @@ -20,9 +20,7 @@ #ifndef __GTK_LIST_ITEM_WIDGET_PRIVATE_H__ #define __GTK_LIST_ITEM_WIDGET_PRIVATE_H__ -#include "gtklistitembaseprivate.h" - -#include "gtklistitemfactory.h" +#include "gtklistfactorywidgetprivate.h" G_BEGIN_DECLS @@ -38,14 +36,12 @@ typedef struct _GtkListItemWidgetClass GtkListItemWidgetClass; struct _GtkListItemWidget { - GtkListItemBase parent_instance; + GtkListFactoryWidget parent_instance; }; struct _GtkListItemWidgetClass { - GtkListItemBaseClass parent_class; - - void (* activate_signal) (GtkListItemWidget *self); + GtkListFactoryWidgetClass parent_class; }; GType gtk_list_item_widget_get_type (void) G_GNUC_CONST; @@ -54,15 +50,6 @@ GtkWidget * gtk_list_item_widget_new (GtkListItemFact const char *css_name, GtkAccessibleRole role); -GtkListItem * gtk_list_item_widget_get_list_item (GtkListItemWidget *self); - -void gtk_list_item_widget_set_factory (GtkListItemWidget *self, - GtkListItemFactory *factory); -void gtk_list_item_widget_set_single_click_activate - (GtkListItemWidget *self, - gboolean single_click_activate); -void gtk_list_item_widget_set_activatable (GtkListItemWidget *self, - gboolean activatable); void gtk_list_item_widget_add_child (GtkListItemWidget *self, GtkWidget *child); void gtk_list_item_widget_reorder_child (GtkListItemWidget *self, diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c index eff45468f9..3e0f277b60 100644 --- a/gtk/gtklistview.c +++ b/gtk/gtklistview.c @@ -226,7 +226,7 @@ gtk_list_view_create_list_widget (GtkListBase *base) "row", GTK_ACCESSIBLE_ROLE_LIST_ITEM); - gtk_list_item_widget_set_single_click_activate (GTK_LIST_ITEM_WIDGET (result), self->single_click_activate); + gtk_list_factory_widget_set_single_click_activate (GTK_LIST_FACTORY_WIDGET (result), self->single_click_activate); return GTK_LIST_ITEM_BASE (result); } @@ -954,7 +954,7 @@ gtk_list_view_set_factory (GtkListView *self, tile = gtk_rb_tree_node_get_next (tile)) { if (tile->widget) - gtk_list_item_widget_set_factory (GTK_LIST_ITEM_WIDGET (tile->widget), factory); + gtk_list_factory_widget_set_factory (GTK_LIST_FACTORY_WIDGET (tile->widget), factory); } g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]); @@ -1030,7 +1030,7 @@ gtk_list_view_set_single_click_activate (GtkListView *self, tile = gtk_rb_tree_node_get_next (tile)) { if (tile->widget) - gtk_list_item_widget_set_single_click_activate (GTK_LIST_ITEM_WIDGET (tile->widget), single_click_activate); + gtk_list_factory_widget_set_single_click_activate (GTK_LIST_FACTORY_WIDGET (tile->widget), single_click_activate); } g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SINGLE_CLICK_ACTIVATE]); diff --git a/gtk/meson.build b/gtk/meson.build index bcb92fe443..c22900ff60 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -269,6 +269,7 @@ gtk_public_sources = files([ 'gtklistbase.c', 'gtklinkbutton.c', 'gtklistbox.c', + 'gtklistfactorywidget.c', 'gtklistitem.c', 'gtklistitembase.c', 'gtklistitemfactory.c', -- 2.30.2