columnview: Add GtkColumnViewRow
authorBenjamin Otte <otte@redhat.com>
Mon, 27 Mar 2023 02:36:22 +0000 (04:36 +0200)
committerBenjamin Otte <otte@redhat.com>
Sat, 1 Apr 2023 18:49:40 +0000 (20:49 +0200)
There is no way to set it yet, this is just to prove that it works.

It also changes the focus behavior of rows. They are now always
focusable - unless turned off by the factory once that is possible.

gtk/gtk.h
gtk/gtkcolumnviewrow.c [new file with mode: 0644]
gtk/gtkcolumnviewrow.h [new file with mode: 0644]
gtk/gtkcolumnviewrowprivate.h [new file with mode: 0644]
gtk/gtkcolumnviewrowwidget.c
gtk/meson.build

index 14550f65f991d849fe1e0f6e9e1052853f16895f..0f2d44da7e59d55737b4f97b0918f174249d6305 100644 (file)
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -87,6 +87,7 @@
 #include <gtk/gtkcolorutils.h>
 #include <gtk/gtkcolumnview.h>
 #include <gtk/gtkcolumnviewcolumn.h>
+#include <gtk/gtkcolumnviewrow.h>
 #include <gtk/gtkcolumnviewsorter.h>
 #include <gtk/deprecated/gtkcombobox.h>
 #include <gtk/deprecated/gtkcomboboxtext.h>
diff --git a/gtk/gtkcolumnviewrow.c b/gtk/gtkcolumnviewrow.c
new file mode 100644 (file)
index 0000000..1570d62
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "config.h"
+
+#include "gtkcolumnviewrowprivate.h"
+
+
+/**
+ * GtkColumnViewRow:
+ *
+ * `GtkColumnViewRow` is used by [class@Gtk.ColumnView] to allow configuring
+ * how rows are displayed.
+ *
+ * It is not used to set the widgets displayed in the individual cells. For that
+ * see [method@GtkColumnViewColumn.set_factory] and [class@GtkColumnViewCell].
+ *
+ * Since: 4.12
+ */
+
+struct _GtkColumnViewRowClass
+{
+  GObjectClass parent_class;
+};
+
+enum
+{
+  PROP_0,
+  PROP_ACTIVATABLE,
+  PROP_FOCUSABLE,
+  PROP_ITEM,
+  PROP_POSITION,
+  PROP_SELECTABLE,
+  PROP_SELECTED,
+
+  N_PROPS
+};
+
+G_DEFINE_TYPE (GtkColumnViewRow, gtk_column_view_row, G_TYPE_OBJECT)
+
+static GParamSpec *properties[N_PROPS] = { NULL, };
+
+static void
+gtk_column_view_row_get_property (GObject    *object,
+                                  guint       property_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  GtkColumnViewRow *self = GTK_COLUMN_VIEW_ROW (object);
+
+  switch (property_id)
+    {
+    case PROP_ACTIVATABLE:
+      g_value_set_boolean (value, self->activatable);
+      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)));
+      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
+        g_value_set_uint (value, GTK_INVALID_LIST_POSITION);
+      break;
+
+    case PROP_SELECTABLE:
+      g_value_set_boolean (value, self->selectable);
+      break;
+
+    case PROP_SELECTED:
+      if (self->owner)
+        g_value_set_boolean (value, gtk_list_item_base_get_selected (GTK_LIST_ITEM_BASE (self->owner)));
+      else
+        g_value_set_boolean (value, FALSE);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_column_view_row_set_property (GObject      *object,
+                                  guint         property_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  GtkColumnViewRow *self = GTK_COLUMN_VIEW_ROW (object);
+
+  switch (property_id)
+    {
+    case PROP_ACTIVATABLE:
+      gtk_column_view_row_set_activatable (self, g_value_get_boolean (value));
+      break;
+
+    case PROP_FOCUSABLE:
+      gtk_column_view_row_set_focusable (self, g_value_get_boolean (value));
+      break;
+
+    case PROP_SELECTABLE:
+      gtk_column_view_row_set_selectable (self, g_value_get_boolean (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_column_view_row_class_init (GtkColumnViewRowClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->get_property = gtk_column_view_row_get_property;
+  gobject_class->set_property = gtk_column_view_row_set_property;
+
+  /**
+   * GtkColumnViewRow:activatable: (attributes org.gtk.Property.get=gtk_column_view_row_get_activatable org.gtk.Property.set=gtk_column_view_row_set_activatable)
+   *
+   * If the row can be activated by the user.
+   * 
+   * Since: 4.12
+   */
+  properties[PROP_ACTIVATABLE] =
+    g_param_spec_boolean ("activatable", NULL, NULL,
+                          TRUE,
+                          G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+  /**
+   * GtkColumnViewRow:focusable: (attributes org.gtk.Property.get=gtk_column_view_row_get_focusable org.gtk.Property.set=gtk_column_view_row_set_focusable)
+   *
+   * If the row 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);
+
+  /**
+   * GtkColumnViewRow:item: (attributes org.gtk.Property.get=gtk_column_view_row_get_item)
+   *
+   * The item for this row.
+   *
+   * Since: 4.12
+   */
+  properties[PROP_ITEM] =
+    g_param_spec_object ("item", NULL, NULL,
+                         G_TYPE_OBJECT,
+                         G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+  /**
+   * GtkColumnViewRow:position: (attributes org.gtk.Property.get=gtk_column_view_row_get_position)
+   *
+   * Position of the row.
+   *
+   * Since: 4.12
+   */
+  properties[PROP_POSITION] =
+    g_param_spec_uint ("position", NULL, NULL,
+                       0, G_MAXUINT, GTK_INVALID_LIST_POSITION,
+                       G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+  /**
+   * GtkColumnViewRow:selectable: (attributes org.gtk.Property.get=gtk_column_view_row_get_selectable org.gtk.Property.set=gtk_column_view_row_set_selectable)
+   *
+   * If the row can be selected by the user.
+   *
+   * Since: 4.12
+   */
+  properties[PROP_SELECTABLE] =
+    g_param_spec_boolean ("selectable", NULL, NULL,
+                          TRUE,
+                          G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+  /**
+   * GtkColumnViewRow:selected: (attributes org.gtk.Property.get=gtk_column_view_row_get_selected)
+   *
+   * If the item in the row is currently selected.
+   *
+   * Since: 4.12
+   */
+  properties[PROP_SELECTED] =
+    g_param_spec_boolean ("selected", NULL, NULL,
+                          FALSE,
+                          G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (gobject_class, N_PROPS, properties);
+}
+
+static void
+gtk_column_view_row_init (GtkColumnViewRow *self)
+{
+  self->selectable = TRUE;
+  self->activatable = TRUE;
+  self->focusable = TRUE;
+}
+
+GtkColumnViewRow *
+gtk_column_view_row_new (void)
+{
+  return g_object_new (GTK_TYPE_COLUMN_VIEW_ROW, NULL);
+}
+
+void
+gtk_column_view_row_do_notify (GtkColumnViewRow *column_view_row,
+                               gboolean          notify_item,
+                               gboolean          notify_position,
+                               gboolean          notify_selected)
+{
+  GObject *object = G_OBJECT (column_view_row);
+
+  if (notify_item)
+    g_object_notify_by_pspec (object, properties[PROP_ITEM]);
+  if (notify_position)
+    g_object_notify_by_pspec (object, properties[PROP_POSITION]);
+  if (notify_selected)
+    g_object_notify_by_pspec (object, properties[PROP_SELECTED]);
+}
+
+/**
+ * gtk_column_view_row_get_item: (attributes org.gtk.Method.get_property=item)
+ * @self: a `GtkColumnViewRow`
+ *
+ * Gets the model item that associated with @self.
+ *
+ * If @self is unbound, this function returns %NULL.
+ *
+ * Returns: (nullable) (transfer none) (type GObject): The item displayed
+ *
+ * Since: 4.12
+ **/
+gpointer
+gtk_column_view_row_get_item (GtkColumnViewRow *self)
+{
+  g_return_val_if_fail (GTK_IS_COLUMN_VIEW_ROW (self), NULL);
+
+  if (self->owner == NULL)
+    return NULL;
+
+  return gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self->owner));
+}
+
+/**
+ * gtk_column_view_row_get_position: (attributes org.gtk.Method.get_property=position)
+ * @self: a `GtkColumnViewRow`
+ *
+ * Gets the position in the model that @self currently displays.
+ *
+ * If @self is unbound, %GTK_INVALID_LIST_POSITION is returned.
+ *
+ * Returns: The position of this row 
+ *
+ * Since: 4.12
+ */
+guint
+gtk_column_view_row_get_position (GtkColumnViewRow *self)
+{
+  g_return_val_if_fail (GTK_IS_COLUMN_VIEW_ROW (self), GTK_INVALID_LIST_POSITION);
+
+  if (self->owner == NULL)
+    return GTK_INVALID_LIST_POSITION;
+
+  return gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self->owner));
+}
+
+/**
+ * gtk_column_view_row_get_selected: (attributes org.gtk.Method.get_property=selected)
+ * @self: a `GtkColumnViewRow`
+ *
+ * Checks if the item is selected that this row corresponds to.
+ *
+ * The selected state is maintained by the list widget and its model
+ * and cannot be set otherwise.
+ *
+ * Returns: %TRUE if the item is selected.
+ *
+ * Since: 4.12
+ */
+gboolean
+gtk_column_view_row_get_selected (GtkColumnViewRow *self)
+{
+  g_return_val_if_fail (GTK_IS_COLUMN_VIEW_ROW (self), FALSE);
+
+  if (self->owner == NULL)
+    return FALSE;
+
+  return gtk_list_item_base_get_selected (GTK_LIST_ITEM_BASE (self->owner));
+}
+
+/**
+ * gtk_column_view_row_get_selectable: (attributes org.gtk.Method.get_property=selectable)
+ * @self: a `GtkColumnViewRow`
+ *
+ * Checks if the row has been set to be selectable via
+ * gtk_column_view_row_set_selectable().
+ *
+ * Do not confuse this function with [method@Gtk.ColumnViewRow.get_selected].
+ *
+ * Returns: %TRUE if the row is selectable
+ *
+ * Since: 4.12
+ */
+gboolean
+gtk_column_view_row_get_selectable (GtkColumnViewRow *self)
+{
+  g_return_val_if_fail (GTK_IS_COLUMN_VIEW_ROW (self), FALSE);
+
+  return self->selectable;
+}
+
+/**
+ * gtk_column_view_row_set_selectable: (attributes org.gtk.Method.set_property=selectable)
+ * @self: a `GtkColumnViewRow`
+ * @selectable: if the row should be selectable
+ *
+ * Sets @self to be selectable.
+ *
+ * If a row is selectable, clicking on the row or using the keyboard
+ * will try to select or unselect the row. Whether this succeeds is up to
+ * the model to determine, as it is managing the selected state.
+ *
+ * Note that this means that making a row non-selectable has no
+ * influence on the selected state at all. A non-selectable row
+ * may still be selected.
+ *
+ * By default, rows are selectable.
+ *
+ * Since: 4.12
+ */
+void
+gtk_column_view_row_set_selectable (GtkColumnViewRow *self,
+                                    gboolean          selectable)
+{
+  g_return_if_fail (GTK_IS_COLUMN_VIEW_ROW (self));
+
+  if (self->selectable == selectable)
+    return;
+
+  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]);
+}
+
+/**
+ * gtk_column_view_row_get_activatable: (attributes org.gtk.Method.get_property=activatable)
+ * @self: a `GtkColumnViewRow`
+ *
+ * Checks if the row has been set to be activatable via
+ * gtk_column_view_row_set_activatable().
+ *
+ * Returns: %TRUE if the row is activatable
+ *
+ * Since: 4.12
+ */
+gboolean
+gtk_column_view_row_get_activatable (GtkColumnViewRow *self)
+{
+  g_return_val_if_fail (GTK_IS_COLUMN_VIEW_ROW (self), FALSE);
+
+  return self->activatable;
+}
+
+/**
+ * gtk_column_view_row_set_activatable: (attributes org.gtk.Method.set_property=activatable)
+ * @self: a `GtkColumnViewRow`
+ * @activatable: if the row should be activatable
+ *
+ * Sets @self to be activatable.
+ *
+ * If a row is activatable, double-clicking on the row, using
+ * the Return key or calling gtk_widget_activate() will activate
+ * the row. Activating instructs the containing columnview to 
+ * emit the [signal@Gtk.ColumnView::activate] signal.
+ *
+ * By default, row are activatable.
+ *
+ * Since: 4.12
+ */
+void
+gtk_column_view_row_set_activatable (GtkColumnViewRow *self,
+                                     gboolean          activatable)
+{
+  g_return_if_fail (GTK_IS_COLUMN_VIEW_ROW (self));
+
+  if (self->activatable == activatable)
+    return;
+
+  self->activatable = activatable;
+
+  if (self->owner)
+    gtk_list_factory_widget_set_activatable (GTK_LIST_FACTORY_WIDGET (self->owner), activatable);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIVATABLE]);
+}
+
+/**
+ * gtk_column_view_row_get_focusable: (attributes org.gtk.Method.get_property=focusable)
+ * @self: a `GtkColumnViewRow`
+ *
+ * Checks if a row item has been set to be focusable via
+ * gtk_column_view_row_set_focusable().
+ *
+ * Returns: %TRUE if the row is focusable
+ *
+ * Since: 4.12
+ */
+gboolean
+gtk_column_view_row_get_focusable (GtkColumnViewRow *self)
+{
+  g_return_val_if_fail (GTK_IS_COLUMN_VIEW_ROW (self), FALSE);
+
+  return self->focusable;
+}
+
+/**
+ * gtk_column_view_row_set_focusable: (attributes org.gtk.Method.set_property=focusable)
+ * @self: a `GtkColumnViewRow`
+ * @focusable: if the row should be focusable
+ *
+ * Sets @self to be focusable.
+ *
+ * If a row is focusable, it can be focused using the keyboard.
+ * This works similar to [method@Gtk.Widget.set_focusable].
+ *
+ * Note that if row are not focusable, the contents of cells can still be focused if
+ * they are focusable.
+ *
+ * By default, rows are focusable.
+ *
+ * Since: 4.12
+ */
+void
+gtk_column_view_row_set_focusable (GtkColumnViewRow *self,
+                                   gboolean          focusable)
+{
+  g_return_if_fail (GTK_IS_COLUMN_VIEW_ROW (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]);
+}
diff --git a/gtk/gtkcolumnviewrow.h b/gtk/gtkcolumnviewrow.h
new file mode 100644 (file)
index 0000000..99fb7c9
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#pragma once
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtktypes.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_COLUMN_VIEW_ROW (gtk_column_view_row_get_type ())
+GDK_AVAILABLE_IN_4_12
+GDK_DECLARE_INTERNAL_TYPE(GtkColumnViewRow, gtk_column_view_row, GTK, COLUMN_VIEW_ROW, GObject);
+
+GDK_AVAILABLE_IN_4_12
+gpointer        gtk_column_view_row_get_item                    (GtkColumnViewRow       *self);
+GDK_AVAILABLE_IN_4_12
+guint           gtk_column_view_row_get_position                (GtkColumnViewRow       *self) G_GNUC_PURE;
+GDK_AVAILABLE_IN_4_12
+gboolean        gtk_column_view_row_get_selected                (GtkColumnViewRow       *self) G_GNUC_PURE;
+GDK_AVAILABLE_IN_4_12
+gboolean        gtk_column_view_row_get_selectable              (GtkColumnViewRow       *self) G_GNUC_PURE;
+GDK_AVAILABLE_IN_4_12
+void            gtk_column_view_row_set_selectable              (GtkColumnViewRow       *self,
+                                                                 gboolean                selectable);
+GDK_AVAILABLE_IN_4_12
+gboolean        gtk_column_view_row_get_activatable             (GtkColumnViewRow       *self) G_GNUC_PURE;
+GDK_AVAILABLE_IN_4_12
+void            gtk_column_view_row_set_activatable             (GtkColumnViewRow       *self,
+                                                                 gboolean                activatable);
+GDK_AVAILABLE_IN_4_12
+gboolean        gtk_column_view_row_get_focusable               (GtkColumnViewRow       *self) G_GNUC_PURE;
+GDK_AVAILABLE_IN_4_12
+void            gtk_column_view_row_set_focusable               (GtkColumnViewRow       *self,
+                                                                 gboolean                focusable);
+
+G_END_DECLS
+
diff --git a/gtk/gtkcolumnviewrowprivate.h b/gtk/gtkcolumnviewrowprivate.h
new file mode 100644 (file)
index 0000000..4370231
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#pragma once
+
+#include "gtkcolumnviewrow.h"
+
+#include "gtkcolumnviewrowwidgetprivate.h"
+
+G_BEGIN_DECLS
+
+struct _GtkColumnViewRow
+{
+  GObject parent_instance;
+
+  GtkColumnViewRowWidget *owner; /* has a reference */
+
+  guint activatable : 1;
+  guint selectable : 1;
+  guint focusable : 1;
+};
+
+GtkColumnViewRow *      gtk_column_view_row_new                         (void);
+
+void                    gtk_column_view_row_do_notify                   (GtkColumnViewRow       *self,
+                                                                         gboolean                notify_item,
+                                                                         gboolean                notify_position,
+                                                                         gboolean                notify_selected);
+
+
+G_END_DECLS
+
index 234759f03647166f5881d191efdaab1492ab9ba8..7cc4e724f9870c2ec31b9fcf35d12b1eed403ea9 100644 (file)
@@ -25,6 +25,7 @@
 #include "gtkcolumnviewprivate.h"
 #include "gtkcolumnviewcellprivate.h"
 #include "gtkcolumnviewcolumnprivate.h"
+#include "gtkcolumnviewrowprivate.h"
 #include "gtkcolumnviewtitleprivate.h"
 #include "gtklistitemfactoryprivate.h"
 #include "gtklistbaseprivate.h"
@@ -88,8 +89,6 @@ gtk_column_view_row_widget_update (GtkListItemBase *base,
                                    gboolean         selected)
 {
   GtkColumnViewRowWidget *self = GTK_COLUMN_VIEW_ROW_WIDGET (base);
-  GtkListFactoryWidget *fw = GTK_LIST_FACTORY_WIDGET (base);
-  gboolean selectable, activatable;
   GtkWidget *child;
 
   if (gtk_column_view_row_widget_is_header (self))
@@ -97,31 +96,99 @@ gtk_column_view_row_widget_update (GtkListItemBase *base,
 
   GTK_LIST_ITEM_BASE_CLASS (gtk_column_view_row_widget_parent_class)->update (base, position, item, selected);
 
-  /* This really does not belong here, but doing better
-   * requires considerable plumbing that we don't have now,
-   * and something like this is needed to fix the filechooser
-   * in select_folder mode.
-   */
-  selectable = TRUE;
-  activatable = TRUE;
-
   for (child = gtk_widget_get_first_child (GTK_WIDGET (self));
        child;
        child = gtk_widget_get_next_sibling (child))
     {
       gtk_list_item_base_update (GTK_LIST_ITEM_BASE (child), position, item, selected);
-
-      selectable &= gtk_list_factory_widget_get_selectable (GTK_LIST_FACTORY_WIDGET (child));
-      activatable &= gtk_list_factory_widget_get_activatable (GTK_LIST_FACTORY_WIDGET (child));
     }
+}
+
+static gpointer
+gtk_column_view_row_widget_create_object (GtkListFactoryWidget *fw)
+{
+  return gtk_column_view_row_new ();
+}
+
+static void
+gtk_column_view_row_widget_setup_object (GtkListFactoryWidget *fw,
+                                         gpointer              object)
+{
+  GtkColumnViewRowWidget *self = GTK_COLUMN_VIEW_ROW_WIDGET (fw);
+  GtkColumnViewRow *row = object;
 
-  gtk_list_factory_widget_set_selectable (fw, selectable);
-  gtk_list_factory_widget_set_activatable (fw, activatable);
+  g_assert (!gtk_column_view_row_widget_is_header (self));
+
+  GTK_LIST_FACTORY_WIDGET_CLASS (gtk_column_view_row_widget_parent_class)->setup_object (fw, object);
+
+  row->owner = self;
+
+  gtk_list_factory_widget_set_activatable (fw, row->activatable);
+  gtk_list_factory_widget_set_selectable (fw, row->selectable);
+  gtk_widget_set_focusable (GTK_WIDGET (self), row->focusable);
+
+  gtk_column_view_row_do_notify (row,
+                                 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_row_widget_teardown_object (GtkListFactoryWidget *fw,
+                                            gpointer              object)
+{
+  GtkColumnViewRowWidget *self = GTK_COLUMN_VIEW_ROW_WIDGET (fw);
+  GtkColumnViewRow *row = object;
+
+  g_assert (!gtk_column_view_row_widget_is_header (self));
+
+  GTK_LIST_FACTORY_WIDGET_CLASS (gtk_column_view_row_widget_parent_class)->teardown_object (fw, object);
+
+  row->owner = 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_column_view_row_do_notify (row,
+                                 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_row_widget_update_object (GtkListFactoryWidget *fw,
+                                          gpointer              object,
+                                          guint                 position,
+                                          gpointer              item,
+                                          gboolean              selected)
+{
+  GtkColumnViewRowWidget *self = GTK_COLUMN_VIEW_ROW_WIDGET (fw);
+  GtkListItemBase *base = GTK_LIST_ITEM_BASE (self);
+  GtkColumnViewRow *row = object;
+  /* Track notify manually instead of freeze/thaw_notify for performance reasons. */
+  gboolean notify_item = FALSE, notify_position = FALSE, notify_selected = FALSE;
+
+  g_assert (!gtk_column_view_row_widget_is_header (self));
+
+  /* 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_row_widget_parent_class)->update_object (fw,
+                                                                                          object,
+                                                                                          position,
+                                                                                          item,
+                                                                                          selected);
+
+  if (row)
+    gtk_column_view_row_do_notify (row, notify_item, notify_position, notify_selected);
 }
 
 static GtkWidget *
 gtk_column_view_next_focus_widget (GtkWidget        *widget,
-                                   GtkWidget        *child,
+                                   GtkWidget        *current,
                                    GtkDirectionType  direction)
 {
   gboolean forward;
@@ -149,17 +216,27 @@ gtk_column_view_next_focus_widget (GtkWidget        *widget,
 
   if (forward)
     {
-      if (child)
-        return gtk_widget_get_next_sibling (child);
-      else
+      if (current == NULL)
+        return widget;
+      else if (current == widget)
         return gtk_widget_get_first_child (widget);
+      else
+        return gtk_widget_get_next_sibling (current);
     }
   else
     {
-      if (child)
-        return gtk_widget_get_prev_sibling (child);
-      else
+      if (current == NULL)
         return gtk_widget_get_last_child (widget);
+      else if (current == widget)
+        return NULL;
+      else
+        {
+          current = gtk_widget_get_prev_sibling (current);
+          if (current)
+            return current;
+          else
+            return widget;
+        }
     }
 }
 
@@ -168,76 +245,53 @@ gtk_column_view_row_widget_focus (GtkWidget        *widget,
                                   GtkDirectionType  direction)
 {
   GtkColumnViewRowWidget *self = GTK_COLUMN_VIEW_ROW_WIDGET (widget);
-  GtkWidget *child, *focus_child;
+  GtkWidget *child, *current;
   GtkColumnView *view;
 
-  /* 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;
+  current = gtk_widget_get_focus_child (widget);
 
   view = gtk_column_view_row_widget_get_column_view (self);
   if (gtk_column_view_get_tab_behavior (view) == GTK_LIST_TAB_CELL &&
       (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_TAB_BACKWARD))
     {
-      if (focus_child || gtk_widget_is_focus (widget))
+      if (current || gtk_widget_is_focus (widget))
         return FALSE;
     }
 
-  if (focus_child == NULL)
+  if (current == NULL)
     {
       GtkColumnViewColumn *focus_column = gtk_column_view_get_focus_column (view);
       if (focus_column)
         {
-          focus_child = gtk_column_view_row_widget_find_child (self, focus_column);
-          if (focus_child && gtk_widget_child_focus (focus_child, direction))
+          current = gtk_column_view_row_widget_find_child (self, focus_column);
+          if (current && gtk_widget_child_focus (current, direction))
             return TRUE;
         }
     }
 
-  for (child = gtk_column_view_next_focus_widget (widget, focus_child, direction);
+  if (gtk_widget_is_focus (widget))
+    current = widget;
+
+  for (child = gtk_column_view_next_focus_widget (widget, current, direction);
        child;
        child = gtk_column_view_next_focus_widget (widget, child, direction))
     {
-      if (gtk_widget_child_focus (child, direction))
-        return TRUE;
-    }
-
-  switch (direction)
-    {
-      case GTK_DIR_TAB_FORWARD:
-      case GTK_DIR_TAB_BACKWARD:
-        gtk_column_view_set_focus_column (view, NULL);
-        break;
-
-      case GTK_DIR_LEFT:
-      case GTK_DIR_RIGHT:
-        return TRUE;
-
-      default:
-        g_assert_not_reached ();
-      case GTK_DIR_UP:
-      case GTK_DIR_DOWN:
-        break;
+      if (child == widget)
+        {
+          if (gtk_widget_grab_focus_self (widget))
+            {
+              gtk_column_view_set_focus_column (view, NULL);
+              return TRUE;
+            }
+        }
+      else if (child)
+        {
+          if (gtk_widget_child_focus (child, direction))
+            return TRUE;
+        }
     }
 
-  if (focus_child)
-    return FALSE;
-
-  if (gtk_widget_is_focus (widget))
-    return FALSE;
-
-  return gtk_widget_grab_focus (widget);
+  return FALSE;
 }
 
 static gboolean
@@ -259,6 +313,12 @@ gtk_column_view_row_widget_grab_focus (GtkWidget *widget)
   else
     focus_child = NULL;
 
+  if (gtk_widget_grab_focus_self (widget))
+    {
+      gtk_column_view_set_focus_column (view, NULL);
+      return TRUE;
+    }
+
   for (child = focus_child ? gtk_widget_get_next_sibling (focus_child) : gtk_widget_get_first_child (widget);
        child != focus_child;
        child = child ? gtk_widget_get_next_sibling (child) : gtk_widget_get_first_child (widget))
@@ -272,10 +332,7 @@ gtk_column_view_row_widget_grab_focus (GtkWidget *widget)
         return TRUE;
     }
 
-  if (!gtk_list_factory_widget_get_selectable (GTK_LIST_FACTORY_WIDGET (widget)))
-    return FALSE;
-
-  return GTK_WIDGET_CLASS (gtk_column_view_row_widget_parent_class)->grab_focus (widget);
+  return FALSE;
 }
 
 static void
@@ -479,9 +536,15 @@ add_arrow_bindings (GtkWidgetClass   *widget_class,
 static void
 gtk_column_view_row_widget_class_init (GtkColumnViewRowWidgetClass *klass)
 {
+  GtkListFactoryWidgetClass *factory_class = GTK_LIST_FACTORY_WIDGET_CLASS (klass);
   GtkListItemBaseClass *base_class = GTK_LIST_ITEM_BASE_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
+  factory_class->create_object = gtk_column_view_row_widget_create_object;
+  factory_class->setup_object = gtk_column_view_row_widget_setup_object;
+  factory_class->update_object = gtk_column_view_row_widget_update_object;
+  factory_class->teardown_object = gtk_column_view_row_widget_teardown_object;
+
   base_class->update = gtk_column_view_row_widget_update;
 
   widget_class->focus = gtk_column_view_row_widget_focus;
index bfe69528a348d038217c6feb80b5bd80b483883a..522b345ae6a696377d562c25b080342de9d96e39 100644 (file)
@@ -188,6 +188,7 @@ gtk_public_sources = files([
   'gtkcolorutils.c',
   'gtkcolumnview.c',
   'gtkcolumnviewcolumn.c',
+  'gtkcolumnviewrow.c',
   'gtkcolumnviewsorter.c',
   'gtkcomposetable.c',
   'gtkconstraintguide.c',
@@ -441,6 +442,7 @@ gtk_public_headers = files([
   'gtkcolorutils.h',
   'gtkcolumnview.h',
   'gtkcolumnviewcolumn.h',
+  'gtkcolumnviewrow.h',
   'gtkcolumnviewsorter.h',
   'gtkconstraintguide.h',
   'gtkconstraintlayout.h',