[gnome-builder/wip/gtk4-port: 702/1774] plugins/symbol-tree: make the symbol tree work
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/gtk4-port: 702/1774] plugins/symbol-tree: make the symbol tree work
- Date: Mon, 11 Jul 2022 22:31:21 +0000 (UTC)
commit 3166b7b38298a6eb78d0033241d81ce0f820ba56
Author: Christian Hergert <chergert redhat com>
Date: Thu Apr 21 18:00:32 2022 -0700
plugins/symbol-tree: make the symbol tree work
There is certainly more we can tweak on this as we go, but this gets the
basics in place.
src/plugins/symbol-tree/gbp-symbol-list-model.c | 184 ++++++++++++++++++++
src/plugins/symbol-tree/gbp-symbol-list-model.h | 35 ++++
src/plugins/symbol-tree/gbp-symbol-popover-row.ui | 4 +
src/plugins/symbol-tree/gbp-symbol-popover.c | 194 ++++++++++++++++++++--
src/plugins/symbol-tree/gbp-symbol-popover.ui | 5 +-
src/plugins/symbol-tree/meson.build | 1 +
6 files changed, 408 insertions(+), 15 deletions(-)
---
diff --git a/src/plugins/symbol-tree/gbp-symbol-list-model.c b/src/plugins/symbol-tree/gbp-symbol-list-model.c
new file mode 100644
index 000000000..bde035a76
--- /dev/null
+++ b/src/plugins/symbol-tree/gbp-symbol-list-model.c
@@ -0,0 +1,184 @@
+/* gbp-symbol-list-model.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-symbol-list-model"
+
+#include "config.h"
+
+#include "gbp-symbol-list-model.h"
+
+struct _GbpSymbolListModel
+{
+ GObject parent_instance;
+ IdeSymbolTree *tree;
+ IdeSymbolNode *parent;
+};
+
+enum {
+ PROP_0,
+ PROP_TREE,
+ PROP_PARENT,
+ N_PROPS
+};
+
+static GType
+gbp_symbol_list_model_get_item_type (GListModel *model)
+{
+ return IDE_TYPE_SYMBOL_NODE;
+}
+
+static guint
+gbp_symbol_list_model_get_n_items (GListModel *model)
+{
+ GbpSymbolListModel *self = GBP_SYMBOL_LIST_MODEL (model);
+
+ return ide_symbol_tree_get_n_children (self->tree, self->parent);
+}
+
+static gpointer
+gbp_symbol_list_model_get_item (GListModel *model,
+ guint position)
+{
+ GbpSymbolListModel *self = GBP_SYMBOL_LIST_MODEL (model);
+
+ if (position >= ide_symbol_tree_get_n_children (self->tree, self->parent))
+ return NULL;
+
+ return ide_symbol_tree_get_nth_child (self->tree, self->parent, position);
+}
+
+static void
+list_model_iface_init (GListModelInterface *iface)
+{
+ iface->get_item = gbp_symbol_list_model_get_item;
+ iface->get_n_items = gbp_symbol_list_model_get_n_items;
+ iface->get_item_type = gbp_symbol_list_model_get_item_type;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GbpSymbolListModel, gbp_symbol_list_model, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+gbp_symbol_list_model_dispose (GObject *object)
+{
+ GbpSymbolListModel *self = (GbpSymbolListModel *)object;
+
+ g_clear_object (&self->tree);
+ g_clear_object (&self->parent);
+
+ G_OBJECT_CLASS (gbp_symbol_list_model_parent_class)->dispose (object);
+}
+
+static void
+gbp_symbol_list_model_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GbpSymbolListModel *self = GBP_SYMBOL_LIST_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_TREE:
+ g_value_set_object (value, self->tree);
+ break;
+
+ case PROP_PARENT:
+ g_value_set_object (value, self->parent);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_symbol_list_model_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GbpSymbolListModel *self = GBP_SYMBOL_LIST_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_TREE:
+ self->tree = g_value_dup_object (value);
+ break;
+
+ case PROP_PARENT:
+ self->parent = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_symbol_list_model_class_init (GbpSymbolListModelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gbp_symbol_list_model_dispose;
+ object_class->get_property = gbp_symbol_list_model_get_property;
+ object_class->set_property = gbp_symbol_list_model_set_property;
+
+ properties [PROP_TREE] =
+ g_param_spec_object ("tree",
+ "Tree",
+ "The tree of nodes",
+ IDE_TYPE_SYMBOL_TREE,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_PARENT] =
+ g_param_spec_object ("parent",
+ "Parent",
+ "The parent node",
+ IDE_TYPE_SYMBOL_NODE,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gbp_symbol_list_model_init (GbpSymbolListModel *self)
+{
+}
+
+IdeSymbolTree *
+gbp_symbol_list_model_get_tree (GbpSymbolListModel *self)
+{
+ g_return_val_if_fail (GBP_IS_SYMBOL_LIST_MODEL (self), NULL);
+
+ return self->tree;
+}
+
+GbpSymbolListModel *
+gbp_symbol_list_model_new (IdeSymbolTree *tree,
+ IdeSymbolNode *parent)
+{
+ return g_object_new (GBP_TYPE_SYMBOL_LIST_MODEL,
+ "tree", tree,
+ "parent", parent,
+ NULL);
+}
diff --git a/src/plugins/symbol-tree/gbp-symbol-list-model.h b/src/plugins/symbol-tree/gbp-symbol-list-model.h
new file mode 100644
index 000000000..55eb8b986
--- /dev/null
+++ b/src/plugins/symbol-tree/gbp-symbol-list-model.h
@@ -0,0 +1,35 @@
+/* gbp-symbol-list-model.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-code.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_SYMBOL_LIST_MODEL (gbp_symbol_list_model_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpSymbolListModel, gbp_symbol_list_model, GBP, SYMBOL_LIST_MODEL, GObject)
+
+GbpSymbolListModel *gbp_symbol_list_model_new (IdeSymbolTree *tree,
+ IdeSymbolNode *parent);
+IdeSymbolTree *gbp_symbol_list_model_get_tree (GbpSymbolListModel *self);
+
+G_END_DECLS
diff --git a/src/plugins/symbol-tree/gbp-symbol-popover-row.ui
b/src/plugins/symbol-tree/gbp-symbol-popover-row.ui
index 71628f6c7..a4ec1685a 100644
--- a/src/plugins/symbol-tree/gbp-symbol-popover-row.ui
+++ b/src/plugins/symbol-tree/gbp-symbol-popover-row.ui
@@ -9,6 +9,10 @@
<property name="child">
<object class="GtkBox">
<property name="spacing">6</property>
+ <property name="margin-top">3</property>
+ <property name="margin-bottom">3</property>
+ <property name="margin-start">3</property>
+ <property name="margin-end">3</property>
<child>
<object class="GtkImage">
<binding name="icon-name">
diff --git a/src/plugins/symbol-tree/gbp-symbol-popover.c b/src/plugins/symbol-tree/gbp-symbol-popover.c
index 0c48716db..37d4ae753 100644
--- a/src/plugins/symbol-tree/gbp-symbol-popover.c
+++ b/src/plugins/symbol-tree/gbp-symbol-popover.c
@@ -22,16 +22,23 @@
#include "config.h"
+#include <libide-editor.h>
+#include <libide-gui.h>
+
+#include "gbp-symbol-list-model.h"
#include "gbp-symbol-popover.h"
struct _GbpSymbolPopover
{
- GtkPopover parent_instance;
+ GtkPopover parent_instance;
+
+ IdeSymbolTree *symbol_tree;
+ GtkFilter *filter;
- IdeSymbolTree *symbol_tree;
+ GtkSearchEntry *search_entry;
+ GtkListView *list_view;
- GtkSearchEntry *search_entry;
- GtkListView *list_view;
+ char **search_needle;
};
enum {
@@ -44,11 +51,44 @@ G_DEFINE_FINAL_TYPE (GbpSymbolPopover, gbp_symbol_popover, GTK_TYPE_POPOVER)
static GParamSpec *properties [N_PROPS];
+static void
+gbp_symbol_popover_get_location_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeSymbolNode *node = (IdeSymbolNode *)object;
+ g_autoptr(GbpSymbolPopover) self = user_data;
+ g_autoptr(IdeLocation) location = NULL;
+ g_autoptr(GError) error = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SYMBOL_NODE (node));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (GBP_IS_SYMBOL_POPOVER (self));
+
+ if ((location = ide_symbol_node_get_location_finish (node, result, &error)))
+ {
+ IdeWorkspace *workspace = ide_widget_get_workspace (GTK_WIDGET (self));
+ g_autoptr(IdePanelPosition) position = ide_panel_position_new ();
+
+ ide_editor_focus_location (workspace, position, location);
+
+ gtk_popover_popdown (GTK_POPOVER (self));
+ }
+
+ IDE_EXIT;
+}
+
static void
gbp_symbol_popover_activate_cb (GbpSymbolPopover *self,
guint position,
GtkListView *list_view)
{
+ GtkTreeListRow *row;
+ IdeSymbolNode *node;
+ GListModel *model;
+
IDE_ENTRY;
g_assert (GBP_IS_SYMBOL_POPOVER (self));
@@ -56,23 +96,37 @@ gbp_symbol_popover_activate_cb (GbpSymbolPopover *self,
g_debug ("Activating symbol row at position %u", position);
+ model = G_LIST_MODEL (gtk_list_view_get_model (list_view));
+ row = g_list_model_get_item (model, position);
+ node = gtk_tree_list_row_get_item (row);
+
+ g_assert (IDE_IS_SYMBOL_NODE (node));
+
+ ide_symbol_node_get_location_async (node,
+ NULL,
+ gbp_symbol_popover_get_location_cb,
+ g_object_ref (self));
+
IDE_EXIT;
}
static void
-gbp_symbol_popover_reload (GbpSymbolPopover *self)
+gbp_symbol_popover_search_changed_cb (GbpSymbolPopover *self,
+ GtkSearchEntry *search_entry)
{
- IDE_ENTRY;
+ const char *text;
g_assert (GBP_IS_SYMBOL_POPOVER (self));
+ g_assert (GTK_IS_SEARCH_ENTRY (search_entry));
- if (self->symbol_tree == NULL)
- {
- gtk_list_view_set_model (self->list_view, NULL);
- IDE_EXIT;
- }
+ text = gtk_editable_get_text (GTK_EDITABLE (search_entry));
+ g_clear_pointer (&self->search_needle, g_strfreev);
- IDE_EXIT;
+ if (text && *text)
+ self->search_needle = g_str_tokenize_and_fold (text, NULL, NULL);
+
+ if (self->filter != NULL)
+ gtk_filter_changed (self->filter, GTK_FILTER_CHANGE_DIFFERENT);
}
static void
@@ -80,6 +134,7 @@ gbp_symbol_popover_dispose (GObject *object)
{
GbpSymbolPopover *self = (GbpSymbolPopover *)object;
+ g_clear_object (&self->filter);
g_clear_object (&self->symbol_tree);
G_OBJECT_CLASS (gbp_symbol_popover_parent_class)->dispose (object);
@@ -143,8 +198,10 @@ gbp_symbol_popover_class_init (GbpSymbolPopoverClass *klass)
g_object_class_install_properties (object_class, N_PROPS, properties);
gtk_widget_class_set_template_from_resource (widget_class, "/plugins/symbol-tree/gbp-symbol-popover.ui");
+ gtk_widget_class_bind_template_child (widget_class, GbpSymbolPopover, list_view);
gtk_widget_class_bind_template_child (widget_class, GbpSymbolPopover, search_entry);
gtk_widget_class_bind_template_callback (widget_class, gbp_symbol_popover_activate_cb);
+ gtk_widget_class_bind_template_callback (widget_class, gbp_symbol_popover_search_changed_cb);
}
static void
@@ -169,6 +226,89 @@ gbp_symbol_popover_get_symbol_tree (GbpSymbolPopover *self)
return self->symbol_tree;
}
+static GListModel *
+get_child_model (gpointer item,
+ gpointer user_data)
+{
+ IdeSymbolNode *node = item;
+ IdeSymbolTree *tree = user_data;
+
+ g_assert (IDE_IS_SYMBOL_NODE (node));
+ g_assert (IDE_IS_SYMBOL_TREE (tree));
+
+ return G_LIST_MODEL (gbp_symbol_list_model_new (tree, node));
+}
+
+static gboolean
+filter_node (IdeSymbolNode *node,
+ const char * const *search_needle)
+{
+ /* Show only if the name matches every needle */
+ for (guint i = 0; search_needle[i]; i++)
+ {
+ const char *name = ide_symbol_node_get_name (node);
+
+ if (!name)
+ return FALSE;
+
+ if (g_str_match_string (search_needle[i], name, TRUE))
+ continue;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+filter_by_name (gpointer item,
+ gpointer user_data)
+{
+ GbpSymbolPopover *self = user_data;
+ GtkTreeListRow *row = item;
+ GtkTreeListRow *parent;
+ IdeSymbolNode *node;
+ GListModel *children;
+ guint i, n;
+
+ g_assert (GTK_IS_TREE_LIST_ROW (row));
+ g_assert (GBP_IS_SYMBOL_POPOVER (self));
+
+ /* Show all items if search is empty */
+ if (!self->search_needle || !self->search_needle[0] || !*self->search_needle[0])
+ return TRUE;
+
+ /* Show a row if itself of any parent matches */
+ for (parent = row; parent; parent = gtk_tree_list_row_get_parent (parent))
+ {
+ node = gtk_tree_list_row_get_item (parent);
+ g_assert (IDE_IS_SYMBOL_NODE (node));
+
+ if (filter_node (node, (const char * const *)self->search_needle))
+ return TRUE;
+ }
+
+ /* Show a row if any child matches */
+ if ((children = gtk_tree_list_row_get_children (row)))
+ {
+ n = g_list_model_get_n_items (children);
+ for (i = 0; i < n; i++)
+ {
+ gboolean ret;
+
+ node = g_list_model_get_item (children, i);
+ g_assert (IDE_IS_SYMBOL_NODE (node));
+
+ ret = filter_node (node, (const char * const *)self->search_needle);
+ g_object_unref (node);
+ if (ret)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
void
gbp_symbol_popover_set_symbol_tree (GbpSymbolPopover *self,
IdeSymbolTree *symbol_tree)
@@ -178,7 +318,35 @@ gbp_symbol_popover_set_symbol_tree (GbpSymbolPopover *self,
if (g_set_object (&self->symbol_tree, symbol_tree))
{
- gbp_symbol_popover_reload (self);
+ gtk_list_view_set_model (self->list_view, NULL);
+
+ if (symbol_tree != NULL)
+ {
+ GbpSymbolListModel *model;
+ GtkTreeListModel *tree_model;
+ GtkFilterListModel *filter_model;
+ GtkSingleSelection *selection;
+ GtkFilter *filter;
+
+ model = gbp_symbol_list_model_new (symbol_tree, NULL);
+ tree_model = gtk_tree_list_model_new (G_LIST_MODEL (model),
+ FALSE,
+ TRUE,
+ get_child_model,
+ g_object_ref (symbol_tree),
+ g_object_unref);
+
+ filter_model = gtk_filter_list_model_new (G_LIST_MODEL (tree_model), NULL);
+ filter = GTK_FILTER (gtk_custom_filter_new (filter_by_name, self, NULL));
+ gtk_filter_list_model_set_filter (filter_model, filter);
+ g_set_object (&self->filter, filter);
+ g_object_unref (filter);
+
+ selection = gtk_single_selection_new (G_LIST_MODEL (filter_model));
+ gtk_list_view_set_model (self->list_view, GTK_SELECTION_MODEL (selection));
+ g_object_unref (selection);
+ }
+
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SYMBOL_TREE]);
}
}
diff --git a/src/plugins/symbol-tree/gbp-symbol-popover.ui b/src/plugins/symbol-tree/gbp-symbol-popover.ui
index 5a0c8d532..be0bfb44b 100644
--- a/src/plugins/symbol-tree/gbp-symbol-popover.ui
+++ b/src/plugins/symbol-tree/gbp-symbol-popover.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GbpSymbolPopover" parent="GtkPopover">
- <property name="width-request">300</property>
+ <property name="width-request">400</property>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
@@ -14,7 +14,7 @@
<property name="propagate-natural-width">true</property>
<property name="min-content-height">100</property>
<property name="max-content-height">600</property>
- <property name="min-content-width">300</property>
+ <property name="min-content-width">400</property>
<property name="max-content-width">400</property>
<child>
<object class="GtkListView" id="list_view">
@@ -35,6 +35,7 @@
<child>
<object class="GtkSearchEntry" id="search_entry">
<property name="placeholder-text" translatable="yes">Search Symbols…</property>
+ <signal name="search-changed" handler="gbp_symbol_popover_search_changed_cb" swapped="true"
object="GbpSymbolPopover"/>
</object>
</child>
</object>
diff --git a/src/plugins/symbol-tree/meson.build b/src/plugins/symbol-tree/meson.build
index 54fed0533..94f64dc50 100644
--- a/src/plugins/symbol-tree/meson.build
+++ b/src/plugins/symbol-tree/meson.build
@@ -1,5 +1,6 @@
plugins_sources += files([
'gbp-symbol-hover-provider.c',
+ 'gbp-symbol-list-model.c',
'gbp-symbol-popover.c',
'gbp-symbol-util.c',
'gbp-symbol-workspace-addin.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]