[gnome-builder/wip/chergert/layout] layout: incremental work on layout features



commit fb14a74679941f893af539488da4307aa06114fc
Author: Christian Hergert <chergert redhat com>
Date:   Thu Jun 29 23:12:19 2017 -0700

    layout: incremental work on layout features
    
    This implements GListModel for the grid which can be useful to integrate
    with with listboxes.

 libide/layout/ide-layout-grid-column.c  |    9 ++
 libide/layout/ide-layout-grid.c         |  192 ++++++++++++++++++++++++++++++-
 libide/layout/ide-layout-private.h      |    4 +
 libide/layout/ide-layout-stack-header.c |    4 +-
 libide/layout/ide-layout-stack.c        |    8 +-
 5 files changed, 212 insertions(+), 5 deletions(-)
---
diff --git a/libide/layout/ide-layout-grid-column.c b/libide/layout/ide-layout-grid-column.c
index 42332d0..7633e3d 100644
--- a/libide/layout/ide-layout-grid-column.c
+++ b/libide/layout/ide-layout-grid-column.c
@@ -79,8 +79,13 @@ ide_layout_grid_column_add (GtkContainer *container,
     }
   else if (IDE_IS_LAYOUT_STACK (widget))
     {
+      GtkWidget *grid;
+
       g_queue_push_head (&self->focus_stack, widget);
       GTK_CONTAINER_CLASS (ide_layout_grid_column_parent_class)->add (container, widget);
+
+      if (IDE_IS_LAYOUT_GRID (grid = gtk_widget_get_parent (GTK_WIDGET (self))))
+        _ide_layout_grid_stack_added (IDE_LAYOUT_GRID (grid), IDE_LAYOUT_STACK (widget));
     }
   else
     {
@@ -95,10 +100,14 @@ ide_layout_grid_column_remove (GtkContainer *container,
                                GtkWidget    *widget)
 {
   IdeLayoutGridColumn *self = (IdeLayoutGridColumn *)container;
+  GtkWidget *grid;
 
   g_assert (IDE_IS_LAYOUT_GRID_COLUMN (self));
   g_assert (IDE_IS_LAYOUT_STACK (widget));
 
+  if (IDE_IS_LAYOUT_GRID (grid = gtk_widget_get_parent (GTK_WIDGET (self))))
+    _ide_layout_grid_stack_removed (IDE_LAYOUT_GRID (grid), IDE_LAYOUT_STACK (widget));
+
   g_queue_remove (&self->focus_stack, widget);
 
   GTK_CONTAINER_CLASS (ide_layout_grid_column_parent_class)->remove (container, widget);
diff --git a/libide/layout/ide-layout-grid.c b/libide/layout/ide-layout-grid.c
index 751f0b7..a5f2e27 100644
--- a/libide/layout/ide-layout-grid.c
+++ b/libide/layout/ide-layout-grid.c
@@ -21,11 +21,34 @@
 #include "ide-layout-grid.h"
 #include "ide-layout-private.h"
 
+/**
+ * SECTION:ide-layout-grid
+ * @title: IdeLayoutGrid
+ * @short_description: A grid for #IdeLayoutView
+ *
+ * The #IdeLayoutGrid provides a grid of views that the user may
+ * manipulate.
+ *
+ * Internally, this is implemented with #IdeLayoutGrid at the top
+ * containing one or more of #IdeLayoutGridColumn. Those columns
+ * contain one or more #IdeLayoutStack. The stack can contain many
+ * #IdeLayoutView.
+ *
+ * #IdeLayoutGrid implements the #GListModel interface to simplify
+ * the process of listing (with deduplication) the views that are
+ * contianed within the #IdeLayoutGrid. If you would instead like
+ * to see all possible views in the stack, use the
+ * ide_layout_grid_foreach_view() API.
+ *
+ * Since: 3.26
+ */
+
 typedef struct
 {
   /* Owned references */
   DzlSignalGroup *toplevel_signals;
   GQueue          focus_column;
+  GArray         *stack_info;
 
   /*
    * This unowned reference is simply used to compare to a new focus
@@ -35,6 +58,12 @@ typedef struct
   IdeLayoutView  *_last_focused_view;
 } IdeLayoutGridPrivate;
 
+typedef struct
+{
+  IdeLayoutStack *stack;
+  guint           len;
+} StackInfo;
+
 enum {
   PROP_0,
   PROP_CURRENT_COLUMN,
@@ -48,7 +77,11 @@ enum {
   N_SIGNALS
 };
 
-G_DEFINE_TYPE_WITH_PRIVATE (IdeLayoutGrid, ide_layout_grid, DZL_TYPE_MULTI_PANED)
+static void list_model_iface_init (GListModelInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (IdeLayoutGrid, ide_layout_grid, DZL_TYPE_MULTI_PANED,
+                         G_ADD_PRIVATE (IdeLayoutGrid)
+                         G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
 
 static GParamSpec *properties [N_PROPS];
 static guint signals [N_SIGNALS];
@@ -199,10 +232,20 @@ ide_layout_grid_add (GtkContainer *container,
 
   if (IDE_IS_LAYOUT_GRID_COLUMN (widget))
     {
+      GList *children;
+
+      /* Add our column to the grid */
       g_queue_push_head (&priv->focus_column, widget);
       GTK_CONTAINER_CLASS (ide_layout_grid_parent_class)->add (container, widget);
       ide_layout_grid_set_current_column (self, IDE_LAYOUT_GRID_COLUMN (widget));
       _ide_layout_grid_column_update_actions (IDE_LAYOUT_GRID_COLUMN (widget));
+
+      /* Start monitoring all the stacks in the grid for views */
+      children = gtk_container_get_children (GTK_CONTAINER (widget));
+      for (const GList *iter = children; iter; iter = iter->next)
+        if (IDE_IS_LAYOUT_STACK (iter->data))
+          _ide_layout_grid_stack_added (self, iter->data);
+      g_list_free (children);
     }
   else if (IDE_IS_LAYOUT_STACK (widget))
     {
@@ -283,11 +326,11 @@ ide_layout_grid_finalize (GObject *object)
   IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
 
   g_assert (IDE_IS_LAYOUT_GRID (self));
-
   g_assert (priv->focus_column.head == NULL);
   g_assert (priv->focus_column.tail == NULL);
   g_assert (priv->focus_column.length == 0);
 
+  g_clear_pointer (&priv->stack_info, g_array_unref);
   g_clear_object (&priv->toplevel_signals);
 
   G_OBJECT_CLASS (ide_layout_grid_parent_class)->finalize (object);
@@ -400,6 +443,8 @@ ide_layout_grid_init (IdeLayoutGrid *self)
 {
   IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
 
+  priv->stack_info = g_array_new (FALSE, FALSE, sizeof (StackInfo));
+
   priv->toplevel_signals = dzl_signal_group_new (GTK_TYPE_WINDOW);
 
   dzl_signal_group_connect_object (priv->toplevel_signals,
@@ -409,6 +454,15 @@ ide_layout_grid_init (IdeLayoutGrid *self)
                                    G_CONNECT_SWAPPED | G_CONNECT_AFTER);
 }
 
+/**
+ * ide_layout_grid_new:
+ *
+ * Creates a new #IdeLayoutGrid.
+ *
+ * Returns: (transfer full): A newly created #IdeLayoutGrid
+ *
+ * Since: 3.26
+ */
 GtkWidget *
 ide_layout_grid_new (void)
 {
@@ -694,3 +748,137 @@ ide_layout_grid_foreach_view (IdeLayoutGrid *self,
   for (guint i = 0; i < views->len; i++)
     callback (g_ptr_array_index (views, i), user_data);
 }
+
+static GType
+ide_layout_grid_get_item_type (GListModel *model)
+{
+  return IDE_TYPE_LAYOUT_VIEW;
+}
+
+static guint
+ide_layout_grid_get_n_items (GListModel *model)
+{
+  IdeLayoutGrid *self = (IdeLayoutGrid *)model;
+  IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+  guint n_items = 0;
+
+  g_assert (IDE_IS_LAYOUT_GRID (self));
+
+  for (guint i = 0; i < priv->stack_info->len; i++)
+    n_items += g_array_index (priv->stack_info, StackInfo, i).len;
+
+  return n_items;
+}
+
+static gpointer
+ide_layout_grid_get_item (GListModel *model,
+                          guint       position)
+{
+  IdeLayoutGrid *self = (IdeLayoutGrid *)model;
+  IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+
+  g_assert (IDE_IS_LAYOUT_GRID (self));
+  g_assert (position < ide_layout_grid_get_n_items (model));
+
+  for (guint i = 0; i < priv->stack_info->len; i++)
+    {
+      const StackInfo *info = &g_array_index (priv->stack_info, StackInfo, i);
+
+      if (position >= info->len)
+        {
+          position -= info->len;
+          continue;
+        }
+
+      return g_list_model_get_item (G_LIST_MODEL (info->stack), position);
+    }
+
+  g_warning ("Failed to locate position %u within %s",
+             position, G_OBJECT_TYPE_NAME (self));
+
+  return NULL;
+}
+
+static void
+list_model_iface_init (GListModelInterface *iface)
+{
+  iface->get_item_type = ide_layout_grid_get_item_type;
+  iface->get_n_items = ide_layout_grid_get_n_items;
+  iface->get_item = ide_layout_grid_get_item;
+}
+
+static void
+ide_layout_grid_stack_items_changed (IdeLayoutGrid  *self,
+                                     guint           position,
+                                     guint           removed,
+                                     guint           added,
+                                     IdeLayoutStack *stack)
+{
+  IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+  guint real_position = 0;
+
+  g_assert (IDE_IS_LAYOUT_GRID (self));
+  g_assert (IDE_IS_LAYOUT_STACK (stack));
+
+  for (guint i = 0; i < priv->stack_info->len; i++)
+    {
+      StackInfo *info = &g_array_index (priv->stack_info, StackInfo, i);
+
+      if (info->stack == stack)
+        {
+          info->len -= removed;
+          info->len += added;
+
+          g_list_model_items_changed (G_LIST_MODEL (self),
+                                      real_position + position,
+                                      removed,
+                                      added);
+
+          return;
+        }
+
+      real_position += info->len;
+    }
+
+  g_warning ("Failed to locate %s within %s",
+             G_OBJECT_TYPE_NAME (stack), G_OBJECT_TYPE_NAME (self));
+}
+
+void
+_ide_layout_grid_stack_added (IdeLayoutGrid  *self,
+                              IdeLayoutStack *stack)
+{
+  IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+  StackInfo info = { 0 };
+  guint n_items;
+
+  g_return_if_fail (IDE_IS_LAYOUT_GRID (self));
+  g_return_if_fail (IDE_IS_LAYOUT_STACK (stack));
+  g_return_if_fail (G_IS_LIST_MODEL (stack));
+
+  info.stack = stack;
+  info.len = 0;
+
+  g_array_append_val (priv->stack_info, info);
+
+  g_signal_connect_object (stack,
+                           "items-changed",
+                           G_CALLBACK (ide_layout_grid_stack_items_changed),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  n_items = g_list_model_get_n_items (G_LIST_MODEL (stack));
+  ide_layout_grid_stack_items_changed (self, 0, 0, n_items, stack);
+}
+
+void
+_ide_layout_grid_stack_removed (IdeLayoutGrid  *self,
+                                IdeLayoutStack *stack)
+{
+  g_return_if_fail (IDE_IS_LAYOUT_GRID (self));
+  g_return_if_fail (IDE_IS_LAYOUT_STACK (stack));
+
+  g_signal_handlers_disconnect_by_func (stack,
+                                        G_CALLBACK (ide_layout_grid_stack_items_changed),
+                                        self);
+}
diff --git a/libide/layout/ide-layout-private.h b/libide/layout/ide-layout-private.h
index ae56aad..72348ed 100644
--- a/libide/layout/ide-layout-private.h
+++ b/libide/layout/ide-layout-private.h
@@ -43,6 +43,10 @@ IdeLayoutStack *_ide_layout_grid_get_nth_stack            (IdeLayoutGrid
 IdeLayoutStack *_ide_layout_grid_get_nth_stack_for_column (IdeLayoutGrid        *self,
                                                            IdeLayoutGridColumn  *column,
                                                            gint                  nth);
+void            _ide_layout_grid_stack_added              (IdeLayoutGrid        *self,
+                                                           IdeLayoutStack       *stack);
+void            _ide_layout_grid_stack_removed            (IdeLayoutGrid        *self,
+                                                           IdeLayoutStack       *stack);
 void            _ide_layout_stack_request_close           (IdeLayoutStack       *stack,
                                                            IdeLayoutView        *view);
 void            _ide_layout_stack_header_update           (IdeLayoutStackHeader *self,
diff --git a/libide/layout/ide-layout-stack-header.c b/libide/layout/ide-layout-stack-header.c
index 1c9597f..0c4cd19 100644
--- a/libide/layout/ide-layout-stack-header.c
+++ b/libide/layout/ide-layout-stack-header.c
@@ -190,7 +190,6 @@ create_document_row (gpointer item,
                       "visible", TRUE,
                       NULL);
   box = g_object_new (GTK_TYPE_BOX,
-                      "spacing", 6,
                       "visible", TRUE,
                       NULL);
   image = g_object_new (GTK_TYPE_IMAGE,
@@ -268,6 +267,9 @@ ide_layout_stack_header_destroy (GtkWidget *widget)
 
   g_assert (IDE_IS_LAYOUT_STACK_HEADER (self));
 
+  if (self->title_list_box != NULL)
+    gtk_list_box_bind_model (self->title_list_box, NULL, NULL, NULL, NULL);
+
   g_clear_object (&self->menu);
 
   GTK_WIDGET_CLASS (ide_layout_stack_header_parent_class)->destroy (widget);
diff --git a/libide/layout/ide-layout-stack.c b/libide/layout/ide-layout-stack.c
index 71c91c3..e0f76cc 100644
--- a/libide/layout/ide-layout-stack.c
+++ b/libide/layout/ide-layout-stack.c
@@ -414,6 +414,12 @@ ide_layout_stack_destroy (GtkWidget *widget)
 
   g_assert (IDE_IS_LAYOUT_STACK (self));
 
+  if (priv->views != NULL)
+    {
+      g_list_model_items_changed (G_LIST_MODEL (self), 0, priv->views->len, 0);
+      g_clear_pointer (&priv->views, g_ptr_array_unref);
+    }
+
   g_clear_object (&priv->addins);
 
   if (priv->bindings != NULL)
@@ -428,8 +434,6 @@ ide_layout_stack_destroy (GtkWidget *widget)
       g_clear_object (&priv->signals);
     }
 
-  g_clear_pointer (&priv->views, g_ptr_array_unref);
-
   GTK_WIDGET_CLASS (ide_layout_stack_parent_class)->destroy (widget);
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]