[gnome-builder/wip/lazy-tree] tree: make GbTree build nodes lazily
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/lazy-tree] tree: make GbTree build nodes lazily
- Date: Sun, 14 Jun 2015 21:38:15 +0000 (UTC)
commit 3f01397b69c84b1cf55addc51b8b80132cd662f6
Author: Christian Hergert <christian hergert me>
Date: Sun Jun 14 14:38:06 2015 -0700
tree: make GbTree build nodes lazily
This changes GbTree to only build tree nodes lazily. This means a couple
things for consumers. For example, performing a foreach on the tree
will no longer yield results unless the item has already been added.
Such features such as "Reveal in Tree" will need to be updated to deal
with this.
However, what this lets us do is change things like the Project Tree to
no longer use IdeProjectFiles, but instead lookup files from the file-
system directly, and lazily. Doing so should improve support for larger
project trees greatly.
Still need to deal with the fallout from this, but this covers the
plumbing required.
src/tree/gb-tree-builder.c | 52 ++++-------
src/tree/gb-tree-node.c | 69 +++++++++-----
src/tree/gb-tree-private.h | 7 ++
src/tree/gb-tree.c | 220 +++++++++++++++++++++++++++++---------------
4 files changed, 212 insertions(+), 136 deletions(-)
---
diff --git a/src/tree/gb-tree-builder.c b/src/tree/gb-tree-builder.c
index 050d731..03b3cb1 100644
--- a/src/tree/gb-tree-builder.c
+++ b/src/tree/gb-tree-builder.c
@@ -126,16 +126,9 @@ _gb_tree_builder_removed (GbTreeBuilder *builder,
g_signal_emit (builder, gSignals [REMOVED], 0, tree);
}
-/**
- * gb_tree_builder_set_tree:
- * @builder: (in): A #GbTreeBuilder.
- * @tree: (in): A #GbTree.
- *
- * Sets the tree the builder is associated with.
- */
-static void
-gb_tree_builder_set_tree (GbTreeBuilder *builder,
- GbTree *tree)
+void
+_gb_tree_builder_set_tree (GbTreeBuilder *builder,
+ GbTree *tree)
{
GbTreeBuilderPrivate *priv = gb_tree_builder_get_instance_private (builder);
@@ -143,10 +136,21 @@ gb_tree_builder_set_tree (GbTreeBuilder *builder,
g_return_if_fail (priv->tree == NULL);
g_return_if_fail (GB_IS_TREE (tree));
- if (tree)
+ if (priv->tree != tree)
{
- priv->tree = tree;
- g_object_add_weak_pointer (G_OBJECT (priv->tree), (gpointer *)&priv->tree);
+ if (priv->tree != NULL)
+ {
+ g_object_remove_weak_pointer (G_OBJECT (priv->tree), (gpointer *)&priv->tree);
+ priv->tree = NULL;
+ }
+
+ if (tree != NULL)
+ {
+ priv->tree = tree;
+ g_object_add_weak_pointer (G_OBJECT (priv->tree), (gpointer *)&priv->tree);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (builder), gParamSpecs [PROP_TREE]);
}
}
@@ -204,39 +208,19 @@ gb_tree_builder_get_property (GObject *object,
}
static void
-gb_tree_builder_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- GbTreeBuilder *builder = GB_TREE_BUILDER (object);
-
- switch (prop_id)
- {
- case PROP_TREE:
- gb_tree_builder_set_tree (builder, g_value_get_object (value));
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
-}
-
-static void
gb_tree_builder_class_init (GbTreeBuilderClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gb_tree_builder_finalize;
object_class->get_property = gb_tree_builder_get_property;
- object_class->set_property = gb_tree_builder_set_property;
gParamSpecs[PROP_TREE] =
g_param_spec_object("tree",
_("Tree"),
_("The GbTree the builder belongs to."),
GB_TYPE_TREE,
- G_PARAM_READWRITE);
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
diff --git a/src/tree/gb-tree-node.c b/src/tree/gb-tree-node.c
index afc5982..b7a9f44 100644
--- a/src/tree/gb-tree-node.c
+++ b/src/tree/gb-tree-node.c
@@ -34,6 +34,7 @@ struct _GbTreeNode
GbTree *tree;
GQuark icon_name;
guint use_markup : 1;
+ guint needs_build : 1;
};
typedef struct
@@ -280,27 +281,27 @@ gb_tree_node_set_item (GbTreeNode *node,
g_object_notify_by_pspec (G_OBJECT (node), gParamSpecs [PROP_ITEM]);
}
-/**
- * gb_tree_node_set_parent:
- * @node: (in): A #GbTreeNode.
- * @parent: (in): A #GbTreeNode.
- *
- * Sets the parent for this node. This is a weak pointer to prevent
- * cyclic references.
- */
-static void
-gb_tree_node_set_parent (GbTreeNode *node,
- GbTreeNode *parent)
+void
+_gb_tree_node_set_parent (GbTreeNode *node,
+ GbTreeNode *parent)
{
g_return_if_fail (GB_IS_TREE_NODE (node));
g_return_if_fail (node->parent == NULL);
g_return_if_fail (!parent || GB_IS_TREE_NODE (parent));
- if (parent)
+ if (parent != node->parent)
{
- node->parent = parent;
- g_object_add_weak_pointer (G_OBJECT (node->parent),
- (gpointer *)&node->parent);
+ if (node->parent != NULL)
+ {
+ g_object_remove_weak_pointer (G_OBJECT (node->parent), (gpointer *)&node->parent);
+ node->parent = NULL;
+ }
+
+ if (parent != NULL)
+ {
+ node->parent = parent;
+ g_object_add_weak_pointer (G_OBJECT (node->parent), (gpointer *)&node->parent);
+ }
}
}
@@ -446,16 +447,18 @@ gb_tree_node_rebuild (GbTreeNode *self)
gboolean
gb_tree_node_get_expanded (GbTreeNode *self)
{
- GbTree *tree;
GtkTreePath *path;
- gboolean ret;
+ gboolean ret = TRUE;
g_return_val_if_fail (GB_IS_TREE_NODE (self), FALSE);
- tree = gb_tree_node_get_tree (self);
- path = gb_tree_node_get_path (self);
- ret = gtk_tree_view_row_expanded (GTK_TREE_VIEW (tree), path);
- gtk_tree_path_free (path);
+ if ((self->tree != NULL) && (self->parent != NULL) && (self->parent->parent != NULL))
+ {
+ path = gb_tree_node_get_path (self);
+ g_assert (path != NULL);
+ ret = gtk_tree_view_row_expanded (GTK_TREE_VIEW (self->tree), path);
+ gtk_tree_path_free (path);
+ }
return ret;
}
@@ -560,10 +563,6 @@ gb_tree_node_set_property (GObject *object,
gb_tree_node_set_item (node, g_value_get_object (value));
break;
- case PROP_PARENT:
- gb_tree_node_set_parent (node, g_value_get_object (value));
- break;
-
case PROP_TEXT:
gb_tree_node_set_text (node, g_value_get_string (value));
break;
@@ -626,7 +625,7 @@ gb_tree_node_class_init (GbTreeNodeClass *klass)
_("Parent"),
_("The parent node."),
GB_TYPE_TREE_NODE,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
/**
* GbTreeNode:tree:
@@ -676,6 +675,7 @@ gb_tree_node_class_init (GbTreeNodeClass *klass)
static void
gb_tree_node_init (GbTreeNode *node)
{
+ node->needs_build = TRUE;
}
static gboolean
@@ -789,3 +789,20 @@ gb_tree_node_show_popover (GbTreeNode *self,
gb_tree_node_show_popover_timeout_cb (popreq);
}
}
+
+gboolean
+_gb_tree_node_get_needs_build (GbTreeNode *self)
+{
+ g_assert (GB_IS_TREE_NODE (self));
+
+ return self->needs_build;
+}
+
+void
+_gb_tree_node_set_needs_build (GbTreeNode *self,
+ gboolean needs_build)
+{
+ g_assert (GB_IS_TREE_NODE (self));
+
+ self->needs_build = !!needs_build;
+}
diff --git a/src/tree/gb-tree-private.h b/src/tree/gb-tree-private.h
index df57db0..ff9114c 100644
--- a/src/tree/gb-tree-private.h
+++ b/src/tree/gb-tree-private.h
@@ -30,7 +30,14 @@ GtkTreePath *_gb_tree_get_path (GbTree *tree,
void _gb_tree_node_set_tree (GbTreeNode *node,
GbTree *tree);
+void _gb_tree_node_set_parent (GbTreeNode *node,
+ GbTreeNode *parent);
+gboolean _gb_tree_node_get_needs_build (GbTreeNode *node);
+void _gb_tree_node_set_needs_build (GbTreeNode *node,
+ gboolean needs_build);
+void _gb_tree_builder_set_tree (GbTreeBuilder *builder,
+ GbTree *tree);
void _gb_tree_builder_added (GbTreeBuilder *builder,
GbTree *tree);
void _gb_tree_builder_removed (GbTreeBuilder *builder,
diff --git a/src/tree/gb-tree.c b/src/tree/gb-tree.c
index a011526..d000378 100644
--- a/src/tree/gb-tree.c
+++ b/src/tree/gb-tree.c
@@ -36,7 +36,6 @@ typedef struct
GtkCellRenderer *cell_text;
GtkTreeStore *store;
guint show_icons : 1;
- guint building;
} GbTreePrivate;
typedef struct
@@ -71,6 +70,27 @@ static GParamSpec *gParamSpecs [LAST_PROP];
static guint gSignals [LAST_SIGNAL];
static void
+gb_tree_build_node (GbTree *self,
+ GbTreeNode *node)
+{
+ GbTreePrivate *priv = gb_tree_get_instance_private (self);
+ gsize i;
+
+ g_assert (GB_IS_TREE (self));
+ g_assert (GB_IS_TREE_NODE (node));
+
+ _gb_tree_node_set_needs_build (node, FALSE);
+
+ for (i = 0; i < priv->builders->len; i++)
+ {
+ GbTreeBuilder *builder;
+
+ builder = g_ptr_array_index (priv->builders, i);
+ _gb_tree_builder_build_node (builder, node);
+ }
+}
+
+static void
gb_tree_unselect (GbTree *self)
{
GtkTreeSelection *selection;
@@ -397,7 +417,8 @@ gb_tree_add_builder_foreach_cb (GtkTreeModel *model,
g_return_val_if_fail (iter != NULL, FALSE);
gtk_tree_model_get (model, iter, 0, &node, -1);
- _gb_tree_builder_build_node (builder, node);
+ if (!_gb_tree_node_get_needs_build (node))
+ _gb_tree_builder_build_node (builder, node);
g_clear_object (&node);
IDE_RETURN (FALSE);
@@ -500,45 +521,39 @@ gb_tree_add (GbTree *self,
gboolean prepend)
{
GbTreePrivate *priv = gb_tree_get_instance_private (self);
- GbTreeBuilder *builder;
GtkTreePath *path;
+ GtkTreeIter *parentptr;
GtkTreeIter iter;
- GtkTreeIter that;
- gint i;
+ GtkTreeIter parent;
g_return_if_fail (GB_IS_TREE (self));
g_return_if_fail (GB_IS_TREE_NODE (node));
g_return_if_fail (GB_IS_TREE_NODE (child));
- g_object_set (child, "parent", node, NULL);
+ _gb_tree_node_set_tree (child, self);
+ _gb_tree_node_set_parent (child, node);
- if ((path = gb_tree_node_get_path (node)))
+ if (node == priv->root)
{
- gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->store), &iter, path);
- if (prepend)
- gtk_tree_store_prepend (priv->store, &that, &iter);
- else
- gtk_tree_store_append (priv->store, &that, &iter);
- gtk_tree_store_set (priv->store, &that, 0, child, -1);
- gtk_tree_path_free (path);
+ parentptr = NULL;
}
else
{
- if (prepend)
- gtk_tree_store_prepend (priv->store, &iter, NULL);
- else
- gtk_tree_store_append (priv->store, &iter, NULL);
- gtk_tree_store_set (priv->store, &iter, 0, child, -1);
+ path = gb_tree_node_get_path (node);
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->store), &parent, path);
+ parentptr = &parent;
+ g_clear_pointer (&path, gtk_tree_path_free);
}
- if (!priv->building)
- {
- for (i = 0; i < priv->builders->len; i++)
- {
- builder = g_ptr_array_index (priv->builders, i);
- _gb_tree_builder_build_node (builder, child);
- }
- }
+ if (prepend)
+ gtk_tree_store_prepend (priv->store, &iter, parentptr);
+ else
+ gtk_tree_store_append (priv->store, &iter, parentptr);
+
+ gtk_tree_store_set (priv->store, &iter, 0, child, -1);
+
+ if (gb_tree_node_get_expanded (node))
+ gb_tree_build_node (self, child);
}
static void
@@ -581,6 +596,66 @@ gb_tree_row_activated (GtkTreeView *tree_view,
}
}
+static void
+gb_tree_row_expanded (GtkTreeView *tree_view,
+ GtkTreeIter *iter,
+ GtkTreePath *path)
+{
+ GbTree *self = (GbTree *)tree_view;
+ GbTreePrivate *priv = gb_tree_get_instance_private (self);
+ GtkTreeModel *model;
+ GbTreeNode *node;
+
+ g_assert (GB_IS_TREE (self));
+ g_assert (iter != NULL);
+ g_assert (path != NULL);
+
+ model = GTK_TREE_MODEL (priv->store);
+
+ gtk_tree_model_get (model, iter, 0, &node, -1);
+
+ if (_gb_tree_node_get_needs_build (node))
+ gb_tree_build_node (self, node);
+
+ /*
+ * The following code looks like inefficient use of GtkTreeModel as we
+ * are not using iter_children() and iter_next(). However, this is required
+ * since the tree will likely have changes since our last iteration cycle.
+ * This is due to the builders adding children for the individual nodes.
+ * Therefore, we simply require that path is still valid (since builders
+ * cannot change anything but current/child nodes).
+ */
+
+ if (gtk_tree_model_iter_has_child (model, iter))
+ {
+ GtkTreeIter child_iter;
+ guint n_children;
+ guint i;
+
+ n_children = gtk_tree_model_iter_n_children (model, iter);
+
+ for (i = 0; i < n_children; i++)
+ {
+ gtk_tree_model_get_iter (model, iter, path);
+
+ if (gtk_tree_model_iter_nth_child (model, &child_iter, iter, i))
+ {
+ GbTreeNode *child;
+
+ gtk_tree_model_get (model, &child_iter, 0, &child, -1);
+
+ if (_gb_tree_node_get_needs_build (child))
+ gb_tree_build_node (self, child);
+
+ g_clear_object (&child);
+ }
+ }
+ }
+
+ g_clear_object (&node);
+}
+
+
static gboolean
gb_tree_button_press_event (GtkWidget *widget,
GdkEventButton *button)
@@ -636,10 +711,16 @@ static gboolean
gb_tree_find_item_foreach_cb (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
- gpointer data)
+ gpointer user_data)
{
GbTreeNode *node = NULL;
- NodeLookup *lookup = data;
+ NodeLookup *lookup = user_data;
+ gboolean ret = FALSE;
+
+ g_assert (GTK_IS_TREE_MODEL (model));
+ g_assert (path != NULL);
+ g_assert (iter != NULL);
+ g_assert (lookup != NULL);
gtk_tree_model_get (model, iter, 0, &node, -1);
@@ -652,11 +733,13 @@ gb_tree_find_item_foreach_cb (GtkTreeModel *model,
if (lookup->equal_func (lookup->key, item))
{
lookup->result = node;
- return TRUE;
+ ret = TRUE;
}
}
- return FALSE;
+ g_clear_object (&node);
+
+ return ret;
}
static void
@@ -835,6 +918,7 @@ gb_tree_class_init (GbTreeClass *klass)
widget_class->button_press_event = gb_tree_button_press_event;
tree_view_class->row_activated = gb_tree_row_activated;
+ tree_view_class->row_expanded = gb_tree_row_expanded;
klass->action = gb_tree_real_action;
@@ -1101,18 +1185,14 @@ gb_tree_add_builder (GbTree *self,
g_return_if_fail (GB_IS_TREE (self));
g_return_if_fail (GB_IS_TREE_BUILDER (builder));
- g_object_set (builder, "tree", self, NULL);
g_ptr_array_add (priv->builders, g_object_ref_sink (builder));
- if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store), &iter))
- {
- priv->building++;
- gb_tree_foreach (self, &iter, gb_tree_add_builder_foreach_cb, builder);
- priv->building--;
- }
-
+ _gb_tree_builder_set_tree (builder, self);
_gb_tree_builder_added (builder, self);
+ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store), &iter))
+ gb_tree_foreach (self, &iter, gb_tree_add_builder_foreach_cb, builder);
+
IDE_EXIT;
}
@@ -1180,26 +1260,30 @@ void
gb_tree_set_root (GbTree *self,
GbTreeNode *root)
{
- GbTreeBuilder *builder;
- gint i;
GbTreePrivate *priv = gb_tree_get_instance_private (self);
IDE_ENTRY;
g_return_if_fail (GB_IS_TREE (self));
- gtk_tree_store_clear (priv->store);
- g_clear_object (&priv->root);
-
- if (root)
+ if (priv->root != root)
{
- priv->root = g_object_ref_sink (root);
- _gb_tree_node_set_tree (root, self);
- for (i = 0; i < priv->builders->len; i++)
+ if (priv->root != NULL)
{
- builder = g_ptr_array_index (priv->builders, i);
- _gb_tree_builder_build_node (builder, root);
+ _gb_tree_node_set_parent (priv->root, NULL);
+ _gb_tree_node_set_tree (priv->root, NULL);
+ g_clear_object (&priv->root);
}
+
+ if (root != NULL)
+ {
+ priv->root = g_object_ref (root);
+ _gb_tree_node_set_parent (priv->root, NULL);
+ _gb_tree_node_set_tree (priv->root, self);
+ gb_tree_build_node (self, priv->root);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_ROOT]);
}
IDE_EXIT;
@@ -1208,20 +1292,21 @@ gb_tree_set_root (GbTree *self,
void
gb_tree_rebuild (GbTree *self)
{
- GbTreeNode *root;
- GtkTreeSelection *selection;
GbTreePrivate *priv = gb_tree_get_instance_private (self);
+ GtkTreeSelection *selection;
g_return_if_fail (GB_IS_TREE (self));
- /* avoid dealign with selection changes while rebuilding */
+ /*
+ * We don't want notification of selection changes while rebuilding.
+ */
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
gtk_tree_selection_unselect_all (selection);
- if ((root = priv->root ? g_object_ref (priv->root) : NULL))
+ if (priv->root != NULL)
{
- gb_tree_set_root (self, root);
- g_object_unref (root);
+ gtk_tree_store_clear (priv->store);
+ gb_tree_build_node (self, priv->root);
}
}
@@ -1331,7 +1416,6 @@ _gb_tree_rebuild_node (GbTree *self,
GtkTreePath *path;
GtkTreeIter iter;
GtkTreeIter child;
- guint i;
g_return_if_fail (GB_IS_TREE (self));
g_return_if_fail (GB_IS_TREE_NODE (node));
@@ -1343,27 +1427,11 @@ _gb_tree_rebuild_node (GbTree *self,
if (gtk_tree_model_iter_children (model, &child, &iter))
{
while (gtk_tree_store_remove (priv->store, &child))
- { /* Do Nothing */ }
+ {
+ }
}
- priv->building++;
- for (i = 0; i < priv->builders->len; i++)
- {
- GbTreeBuilder *builder;
-
- /*
- * FIXME:
- *
- * Refactor this to do all builders when walking each node.
- */
-
- builder = g_ptr_array_index (priv->builders, i);
- gb_tree_foreach (self,
- &iter,
- gb_tree_add_builder_foreach_cb,
- builder);
- priv->building--;
- }
+ gb_tree_build_node (self, node);
gtk_tree_path_free (path);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]