[gnome-builder/wip/gtk4-port] plugins/buildui: show errors and warnings in status popover



commit 02eab61a7b96444c219f4195475077d9a2db7f60
Author: Christian Hergert <chergert redhat com>
Date:   Fri May 20 14:38:26 2022 -0700

    plugins/buildui: show errors and warnings in status popover

 src/plugins/buildui/buildui.gresource.xml          |   2 +
 .../buildui/gbp-buildui-status-indicator.ui        |  14 +-
 .../buildui/gbp-buildui-status-popover-row.ui      |  45 ++++
 src/plugins/buildui/gbp-buildui-status-popover.c   | 241 +++++++++++++++++++++
 src/plugins/buildui/gbp-buildui-status-popover.h   |  35 +++
 src/plugins/buildui/gbp-buildui-status-popover.ui  | 114 ++++++++++
 src/plugins/buildui/gbp-buildui-workspace-addin.c  |   9 +-
 src/plugins/buildui/meson.build                    |   1 +
 8 files changed, 453 insertions(+), 8 deletions(-)
---
diff --git a/src/plugins/buildui/buildui.gresource.xml b/src/plugins/buildui/buildui.gresource.xml
index 66bb58b38..747861b34 100644
--- a/src/plugins/buildui/buildui.gresource.xml
+++ b/src/plugins/buildui/buildui.gresource.xml
@@ -7,6 +7,8 @@
     <file preprocess="xml-stripblanks">gbp-buildui-pane.ui</file>
     <file preprocess="xml-stripblanks">gbp-buildui-stage-row.ui</file>
     <file preprocess="xml-stripblanks">gbp-buildui-status-indicator.ui</file>
+    <file preprocess="xml-stripblanks">gbp-buildui-status-popover.ui</file>
+    <file preprocess="xml-stripblanks">gbp-buildui-status-popover-row.ui</file>
     <file preprocess="xml-stripblanks">gbp-buildui-targets-dialog.ui</file>
     <file preprocess="xml-stripblanks">gtk/menus.ui</file>
   </gresource>
diff --git a/src/plugins/buildui/gbp-buildui-status-indicator.ui 
b/src/plugins/buildui/gbp-buildui-status-indicator.ui
index 730d8398f..58d942aa0 100644
--- a/src/plugins/buildui/gbp-buildui-status-indicator.ui
+++ b/src/plugins/buildui/gbp-buildui-status-indicator.ui
@@ -9,13 +9,16 @@
             <property name="spacing">3</property>
             <child>
               <object class="GtkImage">
-                <property name="icon-name">dialog-warning-symbolic</property>
+                <property name="icon-name">dialog-error-symbolic</property>
                 <property name="pixel-size">12</property>
               </object>
             </child>
             <child>
-              <object class="GtkLabel" id="warning_label">
+              <object class="GtkLabel" id="error_label">
                 <property name="label">0</property>
+                <attributes>
+                  <attribute name="font-features" value="tnum"/>
+                </attributes>
               </object>
             </child>
           </object>
@@ -25,13 +28,16 @@
             <property name="spacing">3</property>
             <child>
               <object class="GtkImage">
-                <property name="icon-name">dialog-error-symbolic</property>
+                <property name="icon-name">dialog-warning-symbolic</property>
                 <property name="pixel-size">12</property>
               </object>
             </child>
             <child>
-              <object class="GtkLabel" id="error_label">
+              <object class="GtkLabel" id="warning_label">
                 <property name="label">0</property>
+                <attributes>
+                  <attribute name="font-features" value="tnum"/>
+                </attributes>
               </object>
             </child>
           </object>
diff --git a/src/plugins/buildui/gbp-buildui-status-popover-row.ui 
b/src/plugins/buildui/gbp-buildui-status-popover-row.ui
new file mode 100644
index 000000000..ca4af3f55
--- /dev/null
+++ b/src/plugins/buildui/gbp-buildui-status-popover-row.ui
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GtkListItem">
+    <property name="child">
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <property name="spacing">6</property>
+        <property name="margin-top">3</property>
+        <property name="margin-bottom">3</property>
+        <property name="margin-start">6</property>
+        <property name="margin-end">6</property>
+        <child>
+          <object class="GtkLabel">
+            <property name="halign">start</property>
+            <property name="hexpand">true</property>
+            <property name="ellipsize">start</property>
+            <style>
+              <class name="dim-label"/>
+            </style>
+            <binding name="label">
+              <lookup name="title" type="IdeLocation">
+                <lookup name="location" type="IdeDiagnostic">
+                  <lookup name="item">GtkListItem</lookup>
+                </lookup>
+              </lookup>
+            </binding>
+          </object>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <property name="halign">start</property>
+            <property name="hexpand">true</property>
+            <property name="wrap">true</property>
+            <binding name="label">
+              <lookup name="text" type="IdeDiagnostic">
+                <lookup name="item">GtkListItem</lookup>
+              </lookup>
+            </binding>
+          </object>
+        </child>
+      </object>
+    </property>
+  </template>
+</interface>
+
diff --git a/src/plugins/buildui/gbp-buildui-status-popover.c 
b/src/plugins/buildui/gbp-buildui-status-popover.c
new file mode 100644
index 000000000..401bdf428
--- /dev/null
+++ b/src/plugins/buildui/gbp-buildui-status-popover.c
@@ -0,0 +1,241 @@
+/* gbp-buildui-status-popover.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-buildui-status-popover"
+
+#include "config.h"
+
+#include <libide-editor.h>
+#include <libide-foundry.h>
+#include <libide-gui.h>
+
+#include "gbp-buildui-status-popover.h"
+
+struct _GbpBuilduiStatusPopover
+{
+  GtkPopover      parent_instance;
+
+  /* Owned references */
+  IdeSignalGroup  *pipeline_signals;
+  GHashTable      *deduplicator;
+  GtkCustomFilter *error_filter;
+  GtkCustomFilter *warning_filter;
+
+  /* Template references */
+  GListStore     *diagnostics;
+};
+
+G_DEFINE_FINAL_TYPE (GbpBuilduiStatusPopover, gbp_buildui_status_popover, GTK_TYPE_POPOVER)
+
+static void
+gbp_buildui_status_popover_clear (GbpBuilduiStatusPopover *self)
+{
+  g_assert (GBP_IS_BUILDUI_STATUS_POPOVER (self));
+
+  if (self->diagnostics != NULL)
+    g_list_store_remove_all (self->diagnostics);
+
+  if (self->deduplicator != NULL)
+    g_hash_table_remove_all (self->deduplicator);
+}
+
+static void
+gbp_buildui_status_popover_add_diagnsotic (GbpBuilduiStatusPopover *self,
+                                           IdeDiagnostic           *diagnostic,
+                                           IdePipeline             *pipeline)
+{
+  g_assert (GBP_IS_BUILDUI_STATUS_POPOVER (self));
+  g_assert (IDE_IS_DIAGNOSTIC (diagnostic));
+  g_assert (IDE_IS_PIPELINE (pipeline));
+
+  if (g_hash_table_contains (self->deduplicator, diagnostic))
+    return;
+
+  g_hash_table_insert (self->deduplicator, g_object_ref (diagnostic), NULL);
+  g_list_store_insert_sorted (self->diagnostics,
+                              diagnostic,
+                              (GCompareDataFunc)ide_diagnostic_compare,
+                              NULL);
+}
+
+static void
+gbp_buildui_status_popover_bind_pipeline (GbpBuilduiStatusPopover *self,
+                                          IdePipeline             *pipeline,
+                                          IdeSignalGroup          *signal_group)
+{
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_BUILDUI_STATUS_POPOVER (self));
+  g_assert (IDE_IS_PIPELINE (pipeline));
+  g_assert (IDE_IS_SIGNAL_GROUP (signal_group));
+
+  gbp_buildui_status_popover_clear (self);
+
+  IDE_EXIT;
+}
+
+static void
+gbp_buildui_status_popover_unbind_pipeline (GbpBuilduiStatusPopover *self,
+                                            IdeSignalGroup          *signal_group)
+{
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_BUILDUI_STATUS_POPOVER (self));
+  g_assert (IDE_IS_SIGNAL_GROUP (signal_group));
+
+  IDE_EXIT;
+}
+
+static void
+gbp_buildui_status_popover_activate_cb (GbpBuilduiStatusPopover *self,
+                                        guint                    item_position,
+                                        GtkListView             *list_view)
+{
+  g_autoptr(IdePanelPosition) position = NULL;
+  g_autoptr(IdeDiagnostic) diagnostic = NULL;
+  IdeWorkspace *workspace;
+  IdeLocation *location;
+  GListModel *model;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_BUILDUI_STATUS_POPOVER (self));
+  g_assert (GTK_IS_LIST_VIEW (list_view));
+
+  if (!(model = G_LIST_MODEL (gtk_list_view_get_model (list_view))) ||
+      !(diagnostic = g_list_model_get_item (model, item_position)) ||
+      !IDE_IS_DIAGNOSTIC (diagnostic) ||
+      !(location = ide_diagnostic_get_location (diagnostic)))
+    IDE_EXIT;
+
+  workspace = ide_widget_get_workspace (GTK_WIDGET (self));
+  position = ide_panel_position_new ();
+  ide_editor_focus_location (workspace, position, location);
+
+  gtk_popover_popdown (GTK_POPOVER (self));
+
+  IDE_EXIT;
+}
+
+static gboolean
+is_warning (gpointer data,
+            gpointer user_data)
+{
+  return ide_diagnostic_get_severity (data) == IDE_DIAGNOSTIC_WARNING;
+}
+
+static gboolean
+is_error (gpointer data,
+          gpointer user_data)
+{
+  return ide_diagnostic_get_severity (data) == IDE_DIAGNOSTIC_ERROR;
+}
+
+static void
+gbp_buildui_status_popover_dispose (GObject *object)
+{
+  GbpBuilduiStatusPopover *self = (GbpBuilduiStatusPopover *)object;
+
+  g_clear_object (&self->pipeline_signals);
+
+  G_OBJECT_CLASS (gbp_buildui_status_popover_parent_class)->dispose (object);
+}
+
+static void
+gbp_buildui_status_popover_class_init (GbpBuilduiStatusPopoverClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->dispose = gbp_buildui_status_popover_dispose;
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/plugins/buildui/gbp-buildui-status-popover.ui");
+  gtk_widget_class_bind_template_child (widget_class, GbpBuilduiStatusPopover, diagnostics);
+  gtk_widget_class_bind_template_child (widget_class, GbpBuilduiStatusPopover, error_filter);
+  gtk_widget_class_bind_template_child (widget_class, GbpBuilduiStatusPopover, warning_filter);
+  gtk_widget_class_bind_template_callback (widget_class, gbp_buildui_status_popover_activate_cb);
+
+  g_type_ensure (IDE_TYPE_DIAGNOSTIC);
+  g_type_ensure (IDE_TYPE_LOCATION);
+}
+
+static void
+gbp_buildui_status_popover_init (GbpBuilduiStatusPopover *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  gtk_custom_filter_set_filter_func (self->warning_filter, is_warning, NULL, NULL);
+  gtk_custom_filter_set_filter_func (self->error_filter, is_error, NULL, NULL);
+
+  self->deduplicator = g_hash_table_new_full ((GHashFunc)ide_diagnostic_hash,
+                                              (GEqualFunc)ide_diagnostic_equal,
+                                              g_object_unref,
+                                              NULL);
+  self->pipeline_signals = ide_signal_group_new (IDE_TYPE_PIPELINE);
+  g_signal_connect_object (self->pipeline_signals,
+                           "bind",
+                           G_CALLBACK (gbp_buildui_status_popover_bind_pipeline),
+                           self,
+                           G_CONNECT_SWAPPED);
+  g_signal_connect_object (self->pipeline_signals,
+                           "unbind",
+                           G_CALLBACK (gbp_buildui_status_popover_unbind_pipeline),
+                           self,
+                           G_CONNECT_SWAPPED);
+  ide_signal_group_connect_object (self->pipeline_signals,
+                                   "diagnostic",
+                                   G_CALLBACK (gbp_buildui_status_popover_add_diagnsotic),
+                                   self,
+                                   G_CONNECT_SWAPPED);
+}
+
+static void
+gbp_buildui_status_popover_connect (GbpBuilduiStatusPopover *self,
+                                    IdeContext              *context)
+{
+  IdeBuildManager *build_manager;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_BUILDUI_STATUS_POPOVER (self));
+  g_assert (IDE_IS_CONTEXT (context));
+
+  build_manager = ide_build_manager_from_context (context);
+  g_assert (IDE_IS_BUILD_MANAGER (build_manager));
+
+  g_object_bind_property (build_manager, "pipeline",
+                          self->pipeline_signals, "target",
+                          G_BINDING_SYNC_CREATE);
+
+  IDE_EXIT;
+}
+
+GbpBuilduiStatusPopover *
+gbp_buildui_status_popover_new (IdeContext *context)
+{
+  GbpBuilduiStatusPopover *self;
+
+  g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
+
+  self = g_object_new (GBP_TYPE_BUILDUI_STATUS_POPOVER, NULL);
+  gbp_buildui_status_popover_connect (self, context);
+
+  return self;
+}
diff --git a/src/plugins/buildui/gbp-buildui-status-popover.h 
b/src/plugins/buildui/gbp-buildui-status-popover.h
new file mode 100644
index 000000000..c12543f28
--- /dev/null
+++ b/src/plugins/buildui/gbp-buildui-status-popover.h
@@ -0,0 +1,35 @@
+/* gbp-buildui-status-popover.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 <gtk/gtk.h>
+
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_BUILDUI_STATUS_POPOVER (gbp_buildui_status_popover_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpBuilduiStatusPopover, gbp_buildui_status_popover, GBP, BUILDUI_STATUS_POPOVER, 
GtkPopover)
+
+GbpBuilduiStatusPopover *gbp_buildui_status_popover_new (IdeContext *context);
+
+G_END_DECLS
diff --git a/src/plugins/buildui/gbp-buildui-status-popover.ui 
b/src/plugins/buildui/gbp-buildui-status-popover.ui
new file mode 100644
index 000000000..a3d02bb53
--- /dev/null
+++ b/src/plugins/buildui/gbp-buildui-status-popover.ui
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GbpBuilduiStatusPopover" parent="GtkPopover">
+    <property name="width-request">400</property>
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkStackSwitcher">
+            <property name="stack">stack</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkFrame">
+            <child>
+              <object class="GtkStack" id="stack">
+                <property name="hhomogeneous">false</property>
+                <property name="vhomogeneous">false</property>
+                <property name="transition-duration">200</property>
+                <property name="transition-type">crossfade</property>
+                <child>
+                  <object class="GtkStackPage">
+                    <property name="title" translatable="yes">_Errors</property>
+                    <property name="use-underline">true</property>
+                    <property name="name">errors</property>
+                    <property name="child">
+                      <object class="GtkScrolledWindow">
+                        <property name="has-frame">true</property>
+                        <property name="propagate-natural-height">true</property>
+                        <property name="propagate-natural-width">true</property>
+                        <property name="min-content-height">300</property>
+                        <property name="max-content-height">600</property>
+                        <property name="min-content-width">400</property>
+                        <property name="max-content-width">400</property>
+                        <child>
+                          <object class="GtkListView">
+                            <signal name="activate" handler="gbp_buildui_status_popover_activate_cb" 
swapped="true" object="GbpBuilduiStatusPopover"/>
+                            <property name="orientation">vertical</property>
+                            <property name="single-click-activate">True</property>
+                            <property name="factory">
+                              <object class="GtkBuilderListItemFactory">
+                                <property 
name="resource">/plugins/buildui/gbp-buildui-status-popover-row.ui</property>
+                              </object>
+                            </property>
+                            <property name="model">
+                              <object class="GtkNoSelection">
+                                <property name="model">
+                                  <object class="GtkFilterListModel">
+                                    <property name="model">diagnostics</property>
+                                    <property name="filter">error_filter</property>
+                                  </object>
+                                </property>
+                              </object>
+                            </property>
+                          </object>
+                        </child>
+                      </object>
+                    </property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkStackPage">
+                    <property name="title" translatable="yes">_Warnings</property>
+                    <property name="use-underline">true</property>
+                    <property name="name">warnings</property>
+                    <property name="child">
+                      <object class="GtkScrolledWindow">
+                        <property name="has-frame">true</property>
+                        <property name="propagate-natural-height">true</property>
+                        <property name="propagate-natural-width">true</property>
+                        <property name="min-content-height">300</property>
+                        <property name="max-content-height">600</property>
+                        <property name="min-content-width">400</property>
+                        <property name="max-content-width">400</property>
+                        <child>
+                          <object class="GtkListView">
+                            <signal name="activate" handler="gbp_buildui_status_popover_activate_cb" 
swapped="true" object="GbpBuilduiStatusPopover"/>
+                            <property name="orientation">vertical</property>
+                            <property name="single-click-activate">True</property>
+                            <property name="factory">
+                              <object class="GtkBuilderListItemFactory">
+                                <property 
name="resource">/plugins/buildui/gbp-buildui-status-popover-row.ui</property>
+                              </object>
+                            </property>
+                            <property name="model">
+                              <object class="GtkNoSelection">
+                                <property name="model">
+                                  <object class="GtkFilterListModel">
+                                    <property name="model">diagnostics</property>
+                                    <property name="filter">warning_filter</property>
+                                  </object>
+                                </property>
+                              </object>
+                            </property>
+                          </object>
+                        </child>
+                      </object>
+                    </property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GListStore" id="diagnostics">
+    <property name="item-type">IdeDiagnostic</property>
+  </object>
+  <object class="GtkCustomFilter" id="warning_filter"/>
+  <object class="GtkCustomFilter" id="error_filter"/>
+</interface>
diff --git a/src/plugins/buildui/gbp-buildui-workspace-addin.c 
b/src/plugins/buildui/gbp-buildui-workspace-addin.c
index 2339b197b..654540e8a 100644
--- a/src/plugins/buildui/gbp-buildui-workspace-addin.c
+++ b/src/plugins/buildui/gbp-buildui-workspace-addin.c
@@ -31,6 +31,7 @@
 #include "gbp-buildui-omni-bar-section.h"
 #include "gbp-buildui-pane.h"
 #include "gbp-buildui-status-indicator.h"
+#include "gbp-buildui-status-popover.h"
 #include "gbp-buildui-targets-dialog.h"
 #include "gbp-buildui-workspace-addin.h"
 
@@ -48,7 +49,6 @@ struct _GbpBuilduiWorkspaceAddin
   GtkLabel                  *error_label;
   GtkImage                  *warning_image;
   GtkLabel                  *warning_label;
-  GbpBuilduiStatusIndicator *status_indicator;
   GtkMenuButton             *status_button;
 
   /* Owned references */
@@ -267,9 +267,11 @@ gbp_buildui_workspace_addin_load (IdeWorkspaceAddin *addin,
   build_manager = ide_build_manager_from_context (context);
 
   statusbar = ide_workspace_get_statusbar (workspace);
-  self->status_indicator = gbp_buildui_status_indicator_new (context);
   self->status_button = g_object_new (GTK_TYPE_MENU_BUTTON,
-                                      "child", self->status_indicator,
+                                      "child", gbp_buildui_status_indicator_new (context),
+                                      "popover", gbp_buildui_status_popover_new (context),
+                                      "direction", GTK_ARROW_UP,
+                                      "focus-on-click", FALSE,
                                       NULL);
   panel_statusbar_add_prefix (statusbar, 1000, GTK_WIDGET (self->status_button));
 
@@ -394,7 +396,6 @@ gbp_buildui_workspace_addin_unload (IdeWorkspaceAddin *addin,
   statusbar = ide_workspace_get_statusbar (workspace);
   panel_statusbar_remove (statusbar, GTK_WIDGET (self->status_button));
   self->status_button = NULL;
-  self->status_indicator = NULL;
 
   for (guint i = 0; i < G_N_ELEMENTS (actions); i++)
     g_action_map_remove_action (G_ACTION_MAP (workspace), actions[i].name);
diff --git a/src/plugins/buildui/meson.build b/src/plugins/buildui/meson.build
index cf90b69e8..0cb5fed4b 100644
--- a/src/plugins/buildui/meson.build
+++ b/src/plugins/buildui/meson.build
@@ -10,6 +10,7 @@ plugins_sources += files([
   'gbp-buildui-runtime-row.c',
   'gbp-buildui-stage-row.c',
   'gbp-buildui-status-indicator.c',
+  'gbp-buildui-status-popover.c',
   'gbp-buildui-targets-dialog.c',
   'gbp-buildui-tree-addin.c',
   'gbp-buildui-workspace-addin.c',


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