[gnome-builder] build-panel: switch to GtkTreeView for diagnostics
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] build-panel: switch to GtkTreeView for diagnostics
- Date: Sun, 16 Oct 2016 06:19:12 +0000 (UTC)
commit dd6728ab830905f7bfd14ba837998691dbab2f41
Author: Christian Hergert <chergert redhat com>
Date: Sat Oct 15 23:15:44 2016 -0700
build-panel: switch to GtkTreeView for diagnostics
When building larger projects, we could potentially have a huge number of
warnings (as we've seen with Geary). When we get to the order of 2000
diagnostics, performance really goes through the floor with the the
GtkListBox design.
This switches to something that looks similar, but not identical wo what
we had previously. However, it uses a GtkTreeView and GtkListStore for
the items. To avoid using a GtkTreeModelSort, we binary search to find
the target position for the row.
Sadly, we can't get the row wrapping the same as it was for the
GtkListBoxRows. So instead we set the ellipsize on the row and use
tooltips to show the full warning information.
I'm still interested in seeing other ways to present this information,
but this gets us in a much better position of not locking up when the
situation occurs.
plugins/build-tools/gbp-build-panel.c | 261 ++++++++++++++++----------
plugins/build-tools/gbp-build-panel.ui | 29 +++-
plugins/build-tools/theme/Adwaita-shared.css | 1 +
3 files changed, 194 insertions(+), 97 deletions(-)
---
diff --git a/plugins/build-tools/gbp-build-panel.c b/plugins/build-tools/gbp-build-panel.c
index 1a79802..607ac9b 100644
--- a/plugins/build-tools/gbp-build-panel.c
+++ b/plugins/build-tools/gbp-build-panel.c
@@ -27,21 +27,25 @@
struct _GbpBuildPanel
{
- PnlDockWidget parent_instance;
-
- IdeBuildResult *result;
- EggSignalGroup *signals;
- EggBindingGroup *bindings;
-
- GtkListBox *diagnostics;
- GtkLabel *errors_label;
- GtkLabel *running_time_label;
- GtkRevealer *status_revealer;
- GtkLabel *status_label;
- GtkLabel *warnings_label;
-
- guint error_count;
- guint warning_count;
+ PnlDockWidget parent_instance;
+
+ IdeBuildResult *result;
+ EggSignalGroup *signals;
+ EggBindingGroup *bindings;
+ GHashTable *diags_hash;
+
+ GtkListStore *diagnostics_store;
+ GtkCellRendererText *diagnostics_text;
+ GtkTreeViewColumn *diagnostics_column;
+ GtkTreeView *diagnostics_tree_view;
+ GtkLabel *errors_label;
+ GtkLabel *running_time_label;
+ GtkRevealer *status_revealer;
+ GtkLabel *status_label;
+ GtkLabel *warnings_label;
+
+ guint error_count;
+ guint warning_count;
};
G_DEFINE_TYPE (GbpBuildPanel, gbp_build_panel, PNL_TYPE_DOCK_WIDGET)
@@ -60,8 +64,9 @@ gbp_build_panel_diagnostic (GbpBuildPanel *self,
IdeBuildResult *result)
{
IdeDiagnosticSeverity severity;
- GtkWidget *row;
- gchar *str;
+ guint hash;
+
+ IDE_ENTRY;
g_assert (GBP_IS_BUILD_PANEL (self));
g_assert (diagnostic != NULL);
@@ -71,26 +76,68 @@ gbp_build_panel_diagnostic (GbpBuildPanel *self,
if (severity == IDE_DIAGNOSTIC_WARNING)
{
+ g_autofree gchar *str = NULL;
+
self->warning_count++;
+
str = g_strdup_printf (ngettext ("%d warning", "%d warnings", self->warning_count),
self->warning_count);
gtk_label_set_label (self->warnings_label, str);
- g_free (str);
}
- else if (severity == IDE_DIAGNOSTIC_ERROR)
+ else if (severity == IDE_DIAGNOSTIC_ERROR || severity == IDE_DIAGNOSTIC_FATAL)
{
+ g_autofree gchar *str = NULL;
+
self->error_count++;
+
str = g_strdup_printf (ngettext ("%d error", "%d errors", self->error_count), self->error_count);
gtk_label_set_label (self->errors_label, str);
- g_free (str);
}
- row = g_object_new (GBP_TYPE_BUILD_PANEL_ROW,
- "diagnostic", diagnostic,
- "visible", TRUE,
- NULL);
- gtk_container_add (GTK_CONTAINER (self->diagnostics), row);
- gtk_list_box_invalidate_sort (self->diagnostics);
- gtk_list_box_invalidate_headers (self->diagnostics);
+ hash = ide_diagnostic_hash (diagnostic);
+
+ if (g_hash_table_insert (self->diags_hash, GUINT_TO_POINTER (hash), NULL))
+ {
+ GtkTreeModel *model = GTK_TREE_MODEL (self->diagnostics_store);
+ GtkTreeIter iter;
+ gint left = 0;
+ gint right = gtk_tree_model_iter_n_children (model, NULL) - 1;
+ gint middle = 0;
+ gint cmpval = 1;
+
+ /* Binary search to locate the target position. */
+ while (left <= right)
+ {
+ g_autoptr(IdeDiagnostic) item = NULL;
+
+ middle = (left + right) / 2;
+
+ gtk_tree_model_iter_nth_child (model, &iter, NULL, middle);
+ gtk_tree_model_get (model, &iter, 0, &item, -1);
+
+ cmpval = ide_diagnostic_compare (item, diagnostic);
+
+ if (cmpval < 0)
+ left = middle + 1;
+ else if (cmpval > 0)
+ right = middle - 1;
+ else
+ break;
+ }
+
+ /* If we binary searched and middle was compared previous
+ * to our new diagnostic, advance one position.
+ */
+ if (cmpval < 0)
+ middle++;
+
+ gtk_list_store_insert (self->diagnostics_store, &iter, middle);
+ gtk_list_store_set (self->diagnostics_store, &iter,
+ 0, diagnostic,
+ 1, ide_diagnostic_get_text (diagnostic),
+ -1);
+ }
+
+ IDE_EXIT;
}
static void
@@ -153,6 +200,8 @@ gbp_build_panel_disconnect (GbpBuildPanel *self)
egg_signal_group_set_target (self->signals, NULL);
egg_binding_group_set_source (self->bindings, NULL);
g_clear_object (&self->result);
+ g_hash_table_remove_all (self->diags_hash);
+ gtk_list_store_clear (self->diagnostics_store);
}
void
@@ -169,10 +218,6 @@ gbp_build_panel_set_result (GbpBuildPanel *self,
if (result)
gbp_build_panel_connect (self, result);
-
- gtk_container_foreach (GTK_CONTAINER (self->diagnostics),
- (GtkCallback)gtk_widget_destroy,
- NULL);
}
}
@@ -199,93 +244,110 @@ gbp_build_panel_notify_running_time (GbpBuildPanel *self,
}
static void
-gbp_build_panel_diagnostic_activated (GbpBuildPanel *self,
- GtkListBoxRow *row,
- GtkListBox *list_box)
+gbp_build_panel_diagnostic_activated (GbpBuildPanel *self,
+ GtkTreePath *path,
+ GtkTreeViewColumn *colun,
+ GtkTreeView *tree_view)
{
g_autoptr(IdeUri) uri = NULL;
- IdeDiagnostic *diagnostic;
IdeSourceLocation *loc;
+ IdeDiagnostic *diagnostic = NULL;
IdeWorkbench *workbench;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ IDE_ENTRY;
g_assert (GBP_IS_BUILD_PANEL (self));
- g_assert (GTK_IS_LIST_BOX_ROW (row));
- g_assert (GTK_IS_LIST_BOX (list_box));
+ g_assert (path != NULL);
+ g_assert (GTK_IS_TREE_VIEW_COLUMN (colun));
+ g_assert (GTK_IS_TREE_VIEW (tree_view));
- diagnostic = gbp_build_panel_row_get_diagnostic (GBP_BUILD_PANEL_ROW (row));
+ model = gtk_tree_view_get_model (tree_view);
+ if (!gtk_tree_model_get_iter (model, &iter, path))
+ IDE_EXIT;
+
+ gtk_tree_model_get (model, &iter, 0, &diagnostic, -1);
if (diagnostic == NULL)
- return;
+ IDE_EXIT;
loc = ide_diagnostic_get_location (diagnostic);
if (loc == NULL)
- return;
+ IDE_EXIT;
uri = ide_source_location_get_uri (loc);
if (uri == NULL)
- return;
+ IDE_EXIT;
workbench = ide_widget_get_workbench (GTK_WIDGET (self));
- ide_workbench_open_uri_async (workbench, uri, "editor", IDE_WORKBENCH_OPEN_FLAGS_NONE, NULL, NULL, NULL);
-}
-static gchar *
-get_severity_title (IdeDiagnosticSeverity severity)
-{
- switch ((int)severity)
- {
- case IDE_DIAGNOSTIC_FATAL:
- case IDE_DIAGNOSTIC_ERROR:
- return _("Errors");
-
- case IDE_DIAGNOSTIC_WARNING:
- return _("Warnings");
+ ide_workbench_open_uri_async (workbench,
+ uri,
+ "editor",
+ IDE_WORKBENCH_OPEN_FLAGS_NONE,
+ NULL,
+ NULL,
+ NULL);
- case IDE_DIAGNOSTIC_NOTE:
- return _("Notes");
-
- default:
- return NULL;
- }
+ IDE_EXIT;
}
static void
-update_header_func (GtkListBoxRow *row,
- GtkListBoxRow *before,
- gpointer user_data)
+gbp_build_panel_text_func (GtkCellLayout *layout,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
{
- IdeDiagnostic *diag;
- IdeDiagnostic *last = NULL;
- IdeDiagnosticSeverity severitya = 0;
- IdeDiagnosticSeverity severityb = 0;
+ g_autoptr(IdeDiagnostic) diagnostic = NULL;
- g_assert (GTK_IS_LIST_BOX_ROW (row));
- g_assert (!before || GTK_IS_LIST_BOX_ROW (before));
+ gtk_tree_model_get (model, iter, 0, &diagnostic, -1);
- diag = gbp_build_panel_row_get_diagnostic (GBP_BUILD_PANEL_ROW (row));
- severitya = ide_diagnostic_get_severity (diag);
-
- if (before != NULL)
+ if (diagnostic != NULL)
{
- last = gbp_build_panel_row_get_diagnostic (GBP_BUILD_PANEL_ROW (before));
- severityb = ide_diagnostic_get_severity (last);
- }
+ GString *str;
+ const gchar *text;
+ g_autofree gchar *name = NULL;
+ IdeSourceLocation *location;
+ GFile *gfile = NULL;
+ guint line = 0;
+ guint column = 0;
- if (last == NULL || severitya != severityb)
- {
- const gchar *str = get_severity_title (severitya);
+ location = ide_diagnostic_get_location (diagnostic);
- if (str != NULL)
+ if (location != NULL)
{
- GtkWidget *widget;
-
- widget = g_object_new (GTK_TYPE_LABEL,
- "label", str,
- "visible", TRUE,
- "xalign", 0.0f,
- NULL);
- gtk_list_box_row_set_header (row, widget);
+ IdeFile *file;
+
+ if (NULL != (file = ide_source_location_get_file (location)))
+ {
+ if (NULL != (gfile = ide_file_get_file (file)))
+ {
+ name = g_file_get_basename (gfile);
+ line = ide_source_location_get_line (location);
+ column = ide_source_location_get_line_offset (location);
+ }
+ }
+
}
+
+ str = g_string_new (NULL);
+
+ if (name != NULL)
+ g_string_append_printf (str, "<b>%s:%u:%u</b>\n",
+ name, line + 1, column + 1);
+
+ text = ide_diagnostic_get_text (diagnostic);
+
+ if (text != NULL)
+ g_string_append (str, text);
+
+ g_object_set (renderer, "markup", str->str, NULL);
+
+ g_string_free (str, TRUE);
}
+ else
+ g_object_set (renderer, "text", NULL, NULL);
}
static void
@@ -298,6 +360,7 @@ gbp_build_panel_destroy (GtkWidget *widget)
g_clear_object (&self->bindings);
g_clear_object (&self->signals);
+ g_clear_pointer (&self->diags_hash, g_hash_table_unref);
GTK_WIDGET_CLASS (gbp_build_panel_parent_class)->destroy (widget);
}
@@ -362,12 +425,17 @@ gbp_build_panel_class_init (GbpBuildPanelClass *klass)
gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/builder/plugins/build-tools-plugin/gbp-build-panel.ui");
gtk_widget_class_set_css_name (widget_class, "buildpanel");
- gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, diagnostics);
+ gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, diagnostics_column);
+ gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, diagnostics_store);
+ gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, diagnostics_text);
+ gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, diagnostics_tree_view);
gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, errors_label);
gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, running_time_label);
gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, status_label);
gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, status_revealer);
gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, warnings_label);
+
+ g_type_ensure (IDE_TYPE_DIAGNOSTIC);
}
static void
@@ -375,6 +443,8 @@ gbp_build_panel_init (GbpBuildPanel *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
+ self->diags_hash = g_hash_table_new (NULL, NULL);
+
g_object_set (self, "title", _("Build"), NULL);
self->signals = egg_signal_group_new (IDE_TYPE_BUILD_RESULT);
@@ -397,17 +467,18 @@ gbp_build_panel_init (GbpBuildPanel *self)
self,
G_CONNECT_SWAPPED);
- gtk_list_box_set_sort_func (self->diagnostics,
- (GtkListBoxSortFunc)gbp_build_panel_row_compare,
- NULL, NULL);
- gtk_list_box_set_header_func (self->diagnostics, update_header_func, NULL, NULL);
-
- g_signal_connect_object (self->diagnostics,
+ g_signal_connect_object (self->diagnostics_tree_view,
"row-activated",
G_CALLBACK (gbp_build_panel_diagnostic_activated),
self,
G_CONNECT_SWAPPED);
+ gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (self->diagnostics_column),
+ GTK_CELL_RENDERER (self->diagnostics_text),
+ gbp_build_panel_text_func,
+ self, NULL);
+
+
self->bindings = egg_binding_group_new ();
egg_binding_group_bind (self->bindings, "mode", self->status_label, "label",
diff --git a/plugins/build-tools/gbp-build-panel.ui b/plugins/build-tools/gbp-build-panel.ui
index 2afac7d..8f3a1da 100644
--- a/plugins/build-tools/gbp-build-panel.ui
+++ b/plugins/build-tools/gbp-build-panel.ui
@@ -139,12 +139,31 @@
</child>
<child>
<object class="GtkScrolledWindow">
+ <property name="hscrollbar-policy">never</property>
<property name="vexpand">true</property>
<property name="visible">true</property>
<child>
- <object class="GtkListBox" id="diagnostics">
- <property name="selection-mode">none</property>
+ <object class="GtkTreeView" id="diagnostics_tree_view">
+ <property name="activate-on-single-click">true</property>
+ <property name="tooltip-column">1</property>
+ <property name="model">diagnostics_store</property>
+ <property name="headers-visible">false</property>
<property name="visible">true</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="diagnostics_column">
+ <property name="expand">true</property>
+ <child>
+ <object class="GtkCellRendererText" id="diagnostics_text">
+ <property name="yalign">0.0</property>
+ <property name="xalign">0.0</property>
+ <property name="ypad">6</property>
+ <property name="width-chars">0</property>
+ <property name="ellipsize">end</property>
+ <property name="wrap-mode">word-char</property>
+ </object>
+ </child>
+ </object>
+ </child>
</object>
</child>
</object>
@@ -152,4 +171,10 @@
</object>
</child>
</template>
+ <object class="GtkListStore" id="diagnostics_store">
+ <columns>
+ <column type="IdeDiagnostic"/>
+ <column type="gchararray"/>
+ </columns>
+ </object>
</interface>
diff --git a/plugins/build-tools/theme/Adwaita-shared.css b/plugins/build-tools/theme/Adwaita-shared.css
index 1cb3337..b4c5fee 100644
--- a/plugins/build-tools/theme/Adwaita-shared.css
+++ b/plugins/build-tools/theme/Adwaita-shared.css
@@ -71,6 +71,7 @@ configurationview list row entry {
/* hrmm, we can use this to get row separators */
+buildpanel treeview,
configurationview treeview {
border-bottom: 1px solid alpha(@borders, 0.4);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]