columnviewcellwidget: Untangle from GtkListItemWidget
authorBenjamin Otte <otte@redhat.com>
Thu, 30 Mar 2023 09:40:05 +0000 (11:40 +0200)
committerBenjamin Otte <otte@redhat.com>
Sat, 1 Apr 2023 18:49:40 +0000 (20:49 +0200)
This is step 1 in switching cells to their own ListItem subclass.

gtk/gtkcolumnviewcellwidget.c
gtk/gtkcolumnviewcellwidgetprivate.h
gtk/gtklistitem.c
gtk/gtklistitemprivate.h

index 82bd843bb45db429ff26bcf2215e9b4c2ff988c0..fcb7b8b6b7b7019257bbedd779d533144c1785a8 100644 (file)
@@ -25,8 +25,8 @@
 #include "gtkcolumnviewrowwidgetprivate.h"
 #include "gtkcssboxesprivate.h"
 #include "gtkcssnodeprivate.h"
+#include "gtklistfactorywidgetprivate.h"
 #include "gtklistitemprivate.h"
-#include "gtklistitemwidgetprivate.h"
 #include "gtkprivate.h"
 #include "gtkwidgetprivate.h"
 
@@ -47,7 +47,72 @@ struct _GtkColumnViewCellWidgetClass
   GtkListItemWidgetClass parent_class;
 };
 
-G_DEFINE_TYPE (GtkColumnViewCellWidget, gtk_column_view_cell_widget, GTK_TYPE_LIST_ITEM_WIDGET)
+G_DEFINE_TYPE (GtkColumnViewCellWidget, gtk_column_view_cell_widget, GTK_TYPE_LIST_FACTORY_WIDGET)
+
+static gboolean
+gtk_column_view_cell_widget_focus (GtkWidget        *widget,
+                                   GtkDirectionType  direction)
+{
+  GtkWidget *child = gtk_widget_get_first_child (widget);
+
+  if (gtk_widget_get_focus_child (widget))
+    {
+      /* 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;
+        }
+    }
+}
+
+static gboolean
+gtk_column_view_cell_widget_grab_focus (GtkWidget *widget)
+{
+  GtkWidget *child;
+
+  if (GTK_WIDGET_CLASS (gtk_column_view_cell_widget_parent_class)->grab_focus (widget))
+    return TRUE;
+
+  child = gtk_widget_get_first_child (widget);
+  if (child && gtk_widget_grab_focus (child))
+    return TRUE;
+
+  return FALSE;
+}
 
 static gpointer
 gtk_column_view_cell_widget_create_object (GtkListFactoryWidget *fw)
@@ -63,13 +128,81 @@ gtk_column_view_cell_widget_create_object (GtkListFactoryWidget *fw)
   return list_item;
 }
 
+static void
+gtk_column_view_cell_widget_setup_object (GtkListFactoryWidget *fw,
+                                          gpointer              object)
+{
+  GtkColumnViewCellWidget *self = GTK_COLUMN_VIEW_CELL_WIDGET (fw);
+  GtkListItem *list_item = object;
+
+  GTK_LIST_FACTORY_WIDGET_CLASS (gtk_column_view_cell_widget_parent_class)->setup_object (fw, object);
+
+  list_item->cell = self;
+
+  gtk_list_factory_widget_set_activatable (fw, list_item->activatable);
+  gtk_list_factory_widget_set_selectable (fw, list_item->selectable);
+  gtk_column_view_cell_widget_set_child (GTK_COLUMN_VIEW_CELL_WIDGET (self), list_item->child);
+
+  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_item_base_get_position (GTK_LIST_ITEM_BASE (self)) != GTK_INVALID_LIST_POSITION,
+                           gtk_list_item_base_get_selected (GTK_LIST_ITEM_BASE (self)));
+}
+
 static void
 gtk_column_view_cell_widget_teardown_object (GtkListFactoryWidget *fw,
                                              gpointer              object)
 {
+  GtkColumnViewCellWidget *self = GTK_COLUMN_VIEW_CELL_WIDGET (fw);
+  GtkListItem *list_item = object;
+
   GTK_LIST_FACTORY_WIDGET_CLASS (gtk_column_view_cell_widget_parent_class)->teardown_object (fw, object);
 
-  gtk_widget_set_focusable (GTK_WIDGET (fw), FALSE);
+  list_item->cell = NULL;
+
+  gtk_column_view_cell_widget_set_child (GTK_COLUMN_VIEW_CELL_WIDGET (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), 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_column_view_cell_widget_update_object (GtkListFactoryWidget *fw,
+                                           gpointer              object,
+                                           guint                 position,
+                                           gpointer              item,
+                                           gboolean              selected)
+{
+  GtkColumnViewCellWidget *self = GTK_COLUMN_VIEW_CELL_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;
+
+  /* FIXME: It's kinda evil to notify external objects from here... */
+  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;
+
+  GTK_LIST_FACTORY_WIDGET_CLASS (gtk_column_view_cell_widget_parent_class)->update_object (fw,
+                                                                                           object,
+                                                                                           position,
+                                                                                           item,
+                                                                                           selected);
+
+  if (list_item)
+    gtk_list_item_do_notify (list_item, notify_item, notify_position, notify_selected);
 }
 
 static int
@@ -193,8 +326,12 @@ gtk_column_view_cell_widget_class_init (GtkColumnViewCellWidgetClass *klass)
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
   factory_class->create_object = gtk_column_view_cell_widget_create_object;
+  factory_class->setup_object = gtk_column_view_cell_widget_setup_object;
+  factory_class->update_object = gtk_column_view_cell_widget_update_object;
   factory_class->teardown_object = gtk_column_view_cell_widget_teardown_object;
 
+  widget_class->focus = gtk_column_view_cell_widget_focus;
+  widget_class->grab_focus = gtk_column_view_cell_widget_grab_focus;
   widget_class->measure = gtk_column_view_cell_widget_measure;
   widget_class->size_allocate = gtk_column_view_cell_widget_size_allocate;
   widget_class->get_request_mode = gtk_column_view_cell_widget_get_request_mode;
@@ -272,3 +409,18 @@ gtk_column_view_cell_widget_get_column (GtkColumnViewCellWidget *self)
 {
   return self->column;
 }
+
+void
+gtk_column_view_cell_widget_set_child (GtkColumnViewCellWidget *self,
+                                       GtkWidget               *child)
+{
+  GtkWidget *cur_child = gtk_widget_get_first_child (GTK_WIDGET (self));
+
+  if (cur_child == child)
+    return;
+
+  g_clear_pointer (&cur_child, gtk_widget_unparent);
+
+  if (child)
+    gtk_widget_set_parent (child, GTK_WIDGET (self));
+}
index b50e69e477d94f91aeb2021a7451b8d267a175d8..57632adf9b14938b6db99fd0b209476a317695bc 100644 (file)
@@ -38,6 +38,9 @@ GType                           gtk_column_view_cell_widget_get_type           (
 GtkWidget *                     gtk_column_view_cell_widget_new                (GtkColumnViewColumn             *column,
                                                                                 gboolean                         inert);
 
+void                            gtk_column_view_cell_widget_set_child          (GtkColumnViewCellWidget         *self,
+                                                                                GtkWidget                       *child);
+
 void                            gtk_column_view_cell_widget_remove             (GtkColumnViewCellWidget         *self);
 
 GtkColumnViewCellWidget *       gtk_column_view_cell_widget_get_next           (GtkColumnViewCellWidget         *self);
index 59f450f97b3f2857b3d418359b04ad4ed57d78d0..8890641a4a050bd39afd6fa5c174db79b9749078 100644 (file)
@@ -70,6 +70,7 @@ gtk_list_item_dispose (GObject *object)
   GtkListItem *self = GTK_LIST_ITEM (object);
 
   g_assert (self->owner == NULL); /* would hold a reference */
+  g_assert (self->cell == NULL); /* would hold a reference */
   g_clear_object (&self->child);
 
   G_OBJECT_CLASS (gtk_list_item_parent_class)->dispose (object);
@@ -100,11 +101,15 @@ gtk_list_item_get_property (GObject    *object,
     case PROP_ITEM:
       if (self->owner)
         g_value_set_object (value, gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self->owner)));
+      else if (self->cell)
+        g_value_set_object (value, gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self->cell)));
       break;
 
     case PROP_POSITION:
       if (self->owner)
         g_value_set_uint (value, gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self->owner)));
+      else if (self->cell)
+        g_value_set_uint (value, gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self->cell)));
       else
         g_value_set_uint (value, GTK_INVALID_LIST_POSITION);
       break;
@@ -116,6 +121,8 @@ gtk_list_item_get_property (GObject    *object,
     case PROP_SELECTED:
       if (self->owner)
         g_value_set_boolean (value, gtk_list_item_base_get_selected (GTK_LIST_ITEM_BASE (self->owner)));
+      if (self->cell)
+        g_value_set_boolean (value, gtk_list_item_base_get_selected (GTK_LIST_ITEM_BASE (self->cell)));
       else
         g_value_set_boolean (value, FALSE);
       break;
@@ -287,10 +294,12 @@ gtk_list_item_get_item (GtkListItem *self)
 {
   g_return_val_if_fail (GTK_IS_LIST_ITEM (self), NULL);
 
-  if (self->owner == NULL)
+  if (self->owner)
+    return gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self->owner));
+  else if (self->cell)
+    return gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self->cell));
+  else
     return NULL;
-
-  return gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self->owner));
 }
 
 /**
@@ -347,6 +356,8 @@ gtk_list_item_set_child (GtkListItem *self,
 
   if (self->owner)
     gtk_list_item_widget_set_child (self->owner, child);
+  else if (self->cell)
+    gtk_column_view_cell_widget_set_child (self->cell, child);
 
   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHILD]);
 }
@@ -388,10 +399,12 @@ gtk_list_item_get_selected (GtkListItem *self)
 {
   g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE);
 
-  if (self->owner == NULL)
+  if (self->owner)
+    return gtk_list_item_base_get_selected (GTK_LIST_ITEM_BASE (self->owner));
+  if (self->cell)
+    return gtk_list_item_base_get_selected (GTK_LIST_ITEM_BASE (self->cell));
+  else
     return FALSE;
-
-  return gtk_list_item_base_get_selected (GTK_LIST_ITEM_BASE (self->owner));
 }
 
 /**
@@ -444,6 +457,8 @@ gtk_list_item_set_selectable (GtkListItem *self,
 
   if (self->owner)
     gtk_list_factory_widget_set_selectable (GTK_LIST_FACTORY_WIDGET (self->owner), selectable);
+  if (self->cell)
+    gtk_list_factory_widget_set_selectable (GTK_LIST_FACTORY_WIDGET (self->cell), selectable);
 
   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTABLE]);
 }
@@ -493,6 +508,8 @@ gtk_list_item_set_activatable (GtkListItem *self,
 
   if (self->owner)
     gtk_list_factory_widget_set_activatable (GTK_LIST_FACTORY_WIDGET (self->owner), activatable);
+  if (self->cell)
+    gtk_list_factory_widget_set_activatable (GTK_LIST_FACTORY_WIDGET (self->cell), activatable);
 
   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIVATABLE]);
 }
@@ -548,6 +565,8 @@ gtk_list_item_set_focusable (GtkListItem *self,
 
   if (self->owner)
     gtk_widget_set_focusable (GTK_WIDGET (self->owner), focusable);
+  if (self->cell)
+    gtk_widget_set_focusable (GTK_WIDGET (self->cell), focusable);
 
   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FOCUSABLE]);
 }
index 99adbeee4c1477013c2c3608ab1c8934213b90b7..68584198215056abb053cf54d22132ac220beaf2 100644 (file)
@@ -22,6 +22,7 @@
 #include "gtklistitem.h"
 
 #include "gtklistitemwidgetprivate.h"
+#include "gtkcolumnviewcellwidgetprivate.h"
 #include "gtkversion.h"
 
 G_BEGIN_DECLS
@@ -31,6 +32,7 @@ struct _GtkListItem
   GObject parent_instance;
 
   GtkListItemWidget *owner; /* has a reference */
+  GtkColumnViewCellWidget *cell; /* has a reference */
 
   GtkWidget *child;