[nautilus/wip/toolbar-start-end: 2/6] toolbar: Factor out operations progress indicator




commit f53f7c03856fce3f377f0df2d8dce504745bcfe8
Author: António Fernandes <antoniof gnome org>
Date:   Sat Aug 6 22:22:48 2022 +0100

    toolbar: Factor out operations progress indicator
    
    Same rationale as last commit, except this control is independent from
    the window slot, so it doesn't require any property binding.

 src/meson.build                                 |   2 +
 src/nautilus-progress-indicator.c               | 548 ++++++++++++++++++++++++
 src/nautilus-progress-indicator.h               |  20 +
 src/nautilus-toolbar.c                          | 511 +---------------------
 src/nautilus-toolbar.h                          |   2 -
 src/resources/nautilus.gresource.xml            |   1 +
 src/resources/ui/nautilus-progress-indicator.ui |  70 +++
 src/resources/ui/nautilus-toolbar.ui            |  46 +-
 8 files changed, 644 insertions(+), 556 deletions(-)
---
diff --git a/src/meson.build b/src/meson.build
index 1b585d9f9..5d024c30c 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -109,6 +109,8 @@ libnautilus_sources = [
   'nautilus-places-view.h',
   'nautilus-previewer.c',
   'nautilus-previewer.h',
+  'nautilus-progress-indicator.c',
+  'nautilus-progress-indicator.h',
   'nautilus-progress-info-widget.c',
   'nautilus-progress-info-widget.h',
   'nautilus-progress-persistence-handler.c',
diff --git a/src/nautilus-progress-indicator.c b/src/nautilus-progress-indicator.c
new file mode 100644
index 000000000..775983964
--- /dev/null
+++ b/src/nautilus-progress-indicator.c
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2022 The GNOME project contributors
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "nautilus-progress-indicator.h"
+
+#include "nautilus-file-operations.h"
+#include "nautilus-progress-info-manager.h"
+#include "nautilus-progress-info-widget.h"
+#include "nautilus-window.h"
+
+#define OPERATION_MINIMUM_TIME 2 /*s */
+#define NEEDS_ATTENTION_ANIMATION_TIMEOUT 2000 /*ms */
+#define REMOVE_FINISHED_OPERATIONS_TIEMOUT 3 /*s */
+
+struct _NautilusProgressIndicator
+{
+    AdwBin parent_instance;
+
+    guint start_operations_timeout_id;
+    guint remove_finished_operations_timeout_id;
+    guint operations_button_attention_timeout_id;
+
+    GtkWidget *operations_button;
+    GtkWidget *operations_popover;
+    GtkWidget *operations_list;
+    GListStore *progress_infos_model;
+    GtkWidget *operations_revealer;
+    GtkWidget *operations_icon;
+
+    NautilusProgressInfoManager *progress_manager;
+};
+
+G_DEFINE_FINAL_TYPE (NautilusProgressIndicator, nautilus_progress_indicator, ADW_TYPE_BIN);
+
+
+static void update_operations (NautilusProgressIndicator *self);
+
+static gboolean
+should_show_progress_info (NautilusProgressInfo *info)
+{
+    return nautilus_progress_info_get_total_elapsed_time (info) +
+           nautilus_progress_info_get_remaining_time (info) > OPERATION_MINIMUM_TIME;
+}
+
+static GList *
+get_filtered_progress_infos (NautilusProgressIndicator *self)
+{
+    GList *l;
+    GList *filtered_progress_infos;
+    GList *progress_infos;
+
+    progress_infos = nautilus_progress_info_manager_get_all_infos (self->progress_manager);
+    filtered_progress_infos = NULL;
+
+    for (l = progress_infos; l != NULL; l = l->next)
+    {
+        if (should_show_progress_info (l->data))
+        {
+            filtered_progress_infos = g_list_append (filtered_progress_infos, l->data);
+        }
+    }
+
+    return filtered_progress_infos;
+}
+
+static gboolean
+should_hide_operations_button (NautilusProgressIndicator *self)
+{
+    GList *progress_infos;
+    GList *l;
+
+    progress_infos = get_filtered_progress_infos (self);
+
+    for (l = progress_infos; l != NULL; l = l->next)
+    {
+        if (nautilus_progress_info_get_total_elapsed_time (l->data) +
+            nautilus_progress_info_get_remaining_time (l->data) > OPERATION_MINIMUM_TIME &&
+            !nautilus_progress_info_get_is_cancelled (l->data) &&
+            !nautilus_progress_info_get_is_finished (l->data))
+        {
+            return FALSE;
+        }
+    }
+
+    g_list_free (progress_infos);
+
+    return TRUE;
+}
+
+static gboolean
+on_remove_finished_operations_timeout (NautilusProgressIndicator *self)
+{
+    nautilus_progress_info_manager_remove_finished_or_cancelled_infos (self->progress_manager);
+    if (should_hide_operations_button (self))
+    {
+        gtk_revealer_set_reveal_child (GTK_REVEALER (self->operations_revealer),
+                                       FALSE);
+    }
+    else
+    {
+        update_operations (self);
+    }
+
+    self->remove_finished_operations_timeout_id = 0;
+
+    return G_SOURCE_REMOVE;
+}
+
+static void
+unschedule_remove_finished_operations (NautilusProgressIndicator *self)
+{
+    if (self->remove_finished_operations_timeout_id != 0)
+    {
+        g_source_remove (self->remove_finished_operations_timeout_id);
+        self->remove_finished_operations_timeout_id = 0;
+    }
+}
+
+static void
+schedule_remove_finished_operations (NautilusProgressIndicator *self)
+{
+    if (self->remove_finished_operations_timeout_id == 0)
+    {
+        self->remove_finished_operations_timeout_id =
+            g_timeout_add_seconds (REMOVE_FINISHED_OPERATIONS_TIEMOUT,
+                                   (GSourceFunc) on_remove_finished_operations_timeout,
+                                   self);
+    }
+}
+
+static void
+remove_operations_button_attention_style (NautilusProgressIndicator *self)
+{
+    gtk_widget_remove_css_class (self->operations_button,
+                                 "nautilus-operations-button-needs-attention");
+}
+
+static gboolean
+on_remove_operations_button_attention_style_timeout (NautilusProgressIndicator *self)
+{
+    remove_operations_button_attention_style (self);
+    self->operations_button_attention_timeout_id = 0;
+
+    return G_SOURCE_REMOVE;
+}
+
+static void
+unschedule_operations_button_attention_style (NautilusProgressIndicator *self)
+{
+    if (self->operations_button_attention_timeout_id != 0)
+    {
+        g_source_remove (self->operations_button_attention_timeout_id);
+        self->operations_button_attention_timeout_id = 0;
+    }
+}
+
+static void
+add_operations_button_attention_style (NautilusProgressIndicator *self)
+{
+    unschedule_operations_button_attention_style (self);
+    remove_operations_button_attention_style (self);
+
+    gtk_widget_add_css_class (self->operations_button,
+                              "nautilus-operations-button-needs-attention");
+    self->operations_button_attention_timeout_id = g_timeout_add (NEEDS_ATTENTION_ANIMATION_TIMEOUT,
+                                                                  (GSourceFunc) 
on_remove_operations_button_attention_style_timeout,
+                                                                  self);
+}
+
+static void
+on_progress_info_cancelled (NautilusProgressIndicator *self)
+{
+    /* Update the pie chart progress */
+    gtk_widget_queue_draw (self->operations_icon);
+
+    if (!nautilus_progress_manager_has_viewers (self->progress_manager))
+    {
+        schedule_remove_finished_operations (self);
+    }
+}
+
+static void
+on_progress_info_progress_changed (NautilusProgressIndicator *self)
+{
+    /* Update the pie chart progress */
+    gtk_widget_queue_draw (self->operations_icon);
+}
+
+static void
+on_progress_info_finished (NautilusProgressIndicator   *self,
+                           NautilusProgressInfo *info)
+{
+    NautilusWindow *window;
+    gchar *main_label;
+    GFile *folder_to_open;
+
+    window = NAUTILUS_WINDOW (gtk_widget_get_root (GTK_WIDGET (self)));
+
+    /* Update the pie chart progress */
+    gtk_widget_queue_draw (self->operations_icon);
+
+    if (!nautilus_progress_manager_has_viewers (self->progress_manager))
+    {
+        schedule_remove_finished_operations (self);
+    }
+
+    folder_to_open = nautilus_progress_info_get_destination (info);
+    /* If destination is null, don't show a notification. This happens when the
+     * operation is a trash operation, which we already show a diferent kind of
+     * notification */
+    if (!gtk_widget_is_visible (self->operations_popover) &&
+        folder_to_open != NULL)
+    {
+        add_operations_button_attention_style (self);
+        main_label = nautilus_progress_info_get_status (info);
+        nautilus_window_show_operation_notification (window,
+                                                     main_label,
+                                                     folder_to_open);
+        g_free (main_label);
+    }
+
+    g_clear_object (&folder_to_open);
+}
+
+static void
+disconnect_progress_infos (NautilusProgressIndicator *self)
+{
+    GList *progress_infos;
+    GList *l;
+
+    progress_infos = nautilus_progress_info_manager_get_all_infos (self->progress_manager);
+    for (l = progress_infos; l != NULL; l = l->next)
+    {
+        g_signal_handlers_disconnect_by_data (l->data, self);
+    }
+}
+
+static void
+update_operations (NautilusProgressIndicator *self)
+{
+    GList *progress_infos;
+    GList *l;
+    gboolean should_show_progress_button = FALSE;
+
+    disconnect_progress_infos (self);
+    g_list_store_remove_all (self->progress_infos_model);
+
+    progress_infos = get_filtered_progress_infos (self);
+    for (l = progress_infos; l != NULL; l = l->next)
+    {
+        should_show_progress_button = should_show_progress_button ||
+                                      should_show_progress_info (l->data);
+
+        g_signal_connect_swapped (l->data, "finished",
+                                  G_CALLBACK (on_progress_info_finished), self);
+        g_signal_connect_swapped (l->data, "cancelled",
+                                  G_CALLBACK (on_progress_info_cancelled), self);
+        g_signal_connect_swapped (l->data, "progress-changed",
+                                  G_CALLBACK (on_progress_info_progress_changed), self);
+        g_list_store_append (self->progress_infos_model, l->data);
+    }
+
+    g_list_free (progress_infos);
+
+    if (should_show_progress_button &&
+        !gtk_revealer_get_reveal_child (GTK_REVEALER (self->operations_revealer)))
+    {
+        add_operations_button_attention_style (self);
+        gtk_revealer_set_reveal_child (GTK_REVEALER (self->operations_revealer),
+                                       TRUE);
+        gtk_widget_queue_draw (self->operations_icon);
+    }
+
+    /* Since we removed the info widgets, we need to restore the focus */
+    if (gtk_widget_get_visible (self->operations_popover))
+    {
+        gtk_widget_grab_focus (self->operations_popover);
+    }
+}
+
+static gboolean
+on_progress_info_started_timeout (NautilusProgressIndicator *self)
+{
+    GList *progress_infos;
+    GList *filtered_progress_infos;
+
+    update_operations (self);
+
+    /* In case we didn't show the operations button because the operation total
+     * time stimation is not good enough, update again to make sure we don't miss
+     * a long time operation because of that */
+
+    progress_infos = nautilus_progress_info_manager_get_all_infos (self->progress_manager);
+    filtered_progress_infos = get_filtered_progress_infos (self);
+    if (!nautilus_progress_manager_are_all_infos_finished_or_cancelled (self->progress_manager) &&
+        g_list_length (progress_infos) != g_list_length (filtered_progress_infos))
+    {
+        g_list_free (filtered_progress_infos);
+        return G_SOURCE_CONTINUE;
+    }
+    else
+    {
+        g_list_free (filtered_progress_infos);
+        self->start_operations_timeout_id = 0;
+        return G_SOURCE_REMOVE;
+    }
+}
+
+static void
+schedule_operations_start (NautilusProgressIndicator *self)
+{
+    if (self->start_operations_timeout_id == 0)
+    {
+        /* Timeout is a little more than what we require for a stimated operation
+         * total time, to make sure the stimated total time is correct */
+        self->start_operations_timeout_id =
+            g_timeout_add (SECONDS_NEEDED_FOR_APROXIMATE_TRANSFER_RATE * 1000 + 500,
+                           (GSourceFunc) on_progress_info_started_timeout,
+                           self);
+    }
+}
+
+static void
+unschedule_operations_start (NautilusProgressIndicator *self)
+{
+    if (self->start_operations_timeout_id != 0)
+    {
+        g_source_remove (self->start_operations_timeout_id);
+        self->start_operations_timeout_id = 0;
+    }
+}
+
+static void
+on_progress_info_started (NautilusProgressInfo *info,
+                          NautilusProgressIndicator   *self)
+{
+    g_signal_handlers_disconnect_by_data (info, self);
+    schedule_operations_start (self);
+}
+
+static void
+on_new_progress_info (NautilusProgressInfoManager *manager,
+                      NautilusProgressInfo        *info,
+                      NautilusProgressIndicator          *self)
+{
+    g_signal_connect (info, "started",
+                      G_CALLBACK (on_progress_info_started), self);
+}
+
+static void
+on_operations_icon_draw (GtkDrawingArea     *drawing_area,
+                         cairo_t            *cr,
+                         int                 width,
+                         int                 height,
+                         NautilusProgressIndicator *self)
+{
+    GtkWidget *widget = GTK_WIDGET (drawing_area);
+    gfloat elapsed_progress = 0;
+    gint remaining_progress = 0;
+    gint total_progress;
+    gdouble ratio;
+    GList *progress_infos;
+    GList *l;
+    gboolean all_cancelled;
+    GdkRGBA background;
+    GdkRGBA foreground;
+    GtkStyleContext *style_context;
+
+    style_context = gtk_widget_get_style_context (widget);
+    gtk_style_context_get_color (style_context, &foreground);
+    background = foreground;
+    background.alpha *= 0.3;
+
+    all_cancelled = TRUE;
+    progress_infos = get_filtered_progress_infos (self);
+    for (l = progress_infos; l != NULL; l = l->next)
+    {
+        if (!nautilus_progress_info_get_is_cancelled (l->data))
+        {
+            all_cancelled = FALSE;
+            remaining_progress += nautilus_progress_info_get_remaining_time (l->data);
+            elapsed_progress += nautilus_progress_info_get_elapsed_time (l->data);
+        }
+    }
+
+    g_list_free (progress_infos);
+
+    total_progress = remaining_progress + elapsed_progress;
+
+    if (all_cancelled)
+    {
+        ratio = 1.0;
+    }
+    else
+    {
+        if (total_progress > 0)
+        {
+            ratio = MAX (0.05, elapsed_progress / total_progress);
+        }
+        else
+        {
+            ratio = 0.05;
+        }
+    }
+
+
+    width = gtk_widget_get_allocated_width (widget);
+    height = gtk_widget_get_allocated_height (widget);
+
+    gdk_cairo_set_source_rgba (cr, &background);
+    cairo_arc (cr,
+               width / 2.0, height / 2.0,
+               MIN (width, height) / 2.0,
+               0, 2 * G_PI);
+    cairo_fill (cr);
+    cairo_move_to (cr, width / 2.0, height / 2.0);
+    gdk_cairo_set_source_rgba (cr, &foreground);
+    cairo_arc (cr,
+               width / 2.0, height / 2.0,
+               MIN (width, height) / 2.0,
+               -G_PI / 2.0, ratio * 2 * G_PI - G_PI / 2.0);
+
+    cairo_fill (cr);
+}
+
+static void
+on_operations_popover_notify_visible (NautilusProgressIndicator *self,
+                                      GParamSpec         *pspec,
+                                      GObject            *popover)
+{
+    if (gtk_widget_get_visible (GTK_WIDGET (popover)))
+    {
+        unschedule_remove_finished_operations (self);
+        nautilus_progress_manager_add_viewer (self->progress_manager,
+                                              G_OBJECT (self));
+    }
+    else
+    {
+        nautilus_progress_manager_remove_viewer (self->progress_manager,
+                                                 G_OBJECT (self));
+    }
+}
+
+static void
+on_progress_has_viewers_changed (NautilusProgressInfoManager *manager,
+                                 NautilusProgressIndicator          *self)
+{
+    if (nautilus_progress_manager_has_viewers (manager))
+    {
+        unschedule_remove_finished_operations (self);
+        return;
+    }
+
+    if (nautilus_progress_manager_are_all_infos_finished_or_cancelled (manager))
+    {
+        unschedule_remove_finished_operations (self);
+        schedule_remove_finished_operations (self);
+    }
+}
+
+static GtkWidget *
+operations_list_create_widget (GObject  *item,
+                               gpointer  user_data)
+{
+    NautilusProgressInfo *info = NAUTILUS_PROGRESS_INFO (item);
+    GtkWidget *widget;
+
+    widget = nautilus_progress_info_widget_new (info);
+    gtk_widget_show (widget);
+
+    return widget;
+}
+
+static void
+nautilus_progress_indicator_constructed (GObject *object)
+{
+    NautilusProgressIndicator *self = NAUTILUS_PROGRESS_INDICATOR (object);
+
+    self->progress_manager = nautilus_progress_info_manager_dup_singleton ();
+    g_signal_connect (self->progress_manager, "new-progress-info",
+                      G_CALLBACK (on_new_progress_info), self);
+    g_signal_connect (self->progress_manager, "has-viewers-changed",
+                      G_CALLBACK (on_progress_has_viewers_changed), self);
+
+    self->progress_infos_model = g_list_store_new (NAUTILUS_TYPE_PROGRESS_INFO);
+    gtk_list_box_bind_model (GTK_LIST_BOX (self->operations_list),
+                             G_LIST_MODEL (self->progress_infos_model),
+                             (GtkListBoxCreateWidgetFunc) operations_list_create_widget,
+                             NULL,
+                             NULL);
+    update_operations (self);
+
+    g_signal_connect (self->operations_popover, "show",
+                      (GCallback) gtk_widget_grab_focus, NULL);
+    g_signal_connect_swapped (self->operations_popover, "closed",
+                              (GCallback) gtk_widget_grab_focus, self);
+}
+
+static void
+nautilus_progress_indicator_finalize (GObject *obj)
+{
+    NautilusProgressIndicator *self = NAUTILUS_PROGRESS_INDICATOR (obj);
+
+    disconnect_progress_infos (self);
+    unschedule_remove_finished_operations (self);
+    unschedule_operations_start (self);
+    unschedule_operations_button_attention_style (self);
+
+    g_clear_object (&self->progress_infos_model);
+    g_signal_handlers_disconnect_by_data (self->progress_manager, self);
+    g_clear_object (&self->progress_manager);
+
+    G_OBJECT_CLASS (nautilus_progress_indicator_parent_class)->finalize (obj);
+}
+
+static void
+nautilus_progress_indicator_class_init (NautilusProgressIndicatorClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+    object_class->constructed = nautilus_progress_indicator_constructed;
+    object_class->finalize = nautilus_progress_indicator_finalize;
+
+    gtk_widget_class_set_template_from_resource (widget_class,
+                                                 "/org/gnome/nautilus/ui/nautilus-progress-indicator.ui");
+    gtk_widget_class_bind_template_child (widget_class, NautilusProgressIndicator, operations_button);
+    gtk_widget_class_bind_template_child (widget_class, NautilusProgressIndicator, operations_icon);
+    gtk_widget_class_bind_template_child (widget_class, NautilusProgressIndicator, operations_popover);
+    gtk_widget_class_bind_template_child (widget_class, NautilusProgressIndicator, operations_list);
+    gtk_widget_class_bind_template_child (widget_class, NautilusProgressIndicator, operations_revealer);
+
+    gtk_widget_class_bind_template_callback (widget_class, on_operations_popover_notify_visible);
+}
+
+static void
+nautilus_progress_indicator_init (NautilusProgressIndicator *self)
+{
+    gtk_widget_init_template (GTK_WIDGET (self));
+
+    gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (self->operations_icon),
+                                    (GtkDrawingAreaDrawFunc) on_operations_icon_draw,
+                                    self,
+                                    NULL);
+}
diff --git a/src/nautilus-progress-indicator.h b/src/nautilus-progress-indicator.h
new file mode 100644
index 000000000..9b69ac075
--- /dev/null
+++ b/src/nautilus-progress-indicator.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The GNOME project contributors
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <libadwaita-1/adwaita.h>
+
+#include "nautilus-window-slot.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_PROGRESS_INDICATOR (nautilus_progress_indicator_get_type())
+
+G_DECLARE_FINAL_TYPE (NautilusProgressIndicator, nautilus_progress_indicator, NAUTILUS, PROGRESS_INDICATOR, 
AdwBin)
+
+G_END_DECLS
diff --git a/src/nautilus-toolbar.c b/src/nautilus-toolbar.c
index 41ef83ade..cf570342a 100644
--- a/src/nautilus-toolbar.c
+++ b/src/nautilus-toolbar.c
@@ -27,24 +27,16 @@
 
 #include "nautilus-application.h"
 #include "nautilus-bookmark.h"
-#include "nautilus-file-operations.h"
 #include "nautilus-file-undo-manager.h"
 #include "nautilus-global-preferences.h"
 #include "nautilus-history-controls.h"
 #include "nautilus-location-entry.h"
 #include "nautilus-pathbar.h"
-#include "nautilus-progress-info-manager.h"
-#include "nautilus-progress-info-widget.h"
+#include "nautilus-progress-indicator.h"
 #include "nautilus-toolbar-menu-sections.h"
 #include "nautilus-ui-utilities.h"
 #include "nautilus-window.h"
 
-#define OPERATION_MINIMUM_TIME 2 /*s */
-#define NEEDS_ATTENTION_ANIMATION_TIMEOUT 2000 /*ms */
-#define REMOVE_FINISHED_OPERATIONS_TIEMOUT 3 /*s */
-
-/* Just design, context at https://gitlab.gnome.org/GNOME/nautilus/issues/548#note_274131 */
-
 struct _NautilusToolbar
 {
     AdwBin parent_instance;
@@ -61,17 +53,6 @@ struct _NautilusToolbar
     gboolean show_location_entry;
     gboolean location_entry_should_auto_hide;
 
-    guint start_operations_timeout_id;
-    guint remove_finished_operations_timeout_id;
-    guint operations_button_attention_timeout_id;
-
-    GtkWidget *operations_button;
-    GtkWidget *operations_popover;
-    GtkWidget *operations_list;
-    GListStore *progress_infos_model;
-    GtkWidget *operations_revealer;
-    GtkWidget *operations_icon;
-
     GtkWidget *view_split_button;
     GMenuModel *view_menu;
 
@@ -86,8 +67,6 @@ struct _NautilusToolbar
 
     GtkWidget *location_entry_close_button;
 
-    NautilusProgressInfoManager *progress_manager;
-
     /* active slot & bindings */
     NautilusWindowSlot *window_slot;
     GBinding *search_binding;
@@ -110,8 +89,6 @@ G_DEFINE_TYPE (NautilusToolbar, nautilus_toolbar, ADW_TYPE_BIN);
 
 static void nautilus_toolbar_set_window_slot_real (NautilusToolbar    *self,
                                                    NautilusWindowSlot *slot);
-static void update_operations (NautilusToolbar *self);
-
 static void
 toolbar_update_appearance (NautilusToolbar *self)
 {
@@ -136,433 +113,6 @@ toolbar_update_appearance (NautilusToolbar *self)
     }
 }
 
-static gboolean
-should_show_progress_info (NautilusProgressInfo *info)
-{
-    return nautilus_progress_info_get_total_elapsed_time (info) +
-           nautilus_progress_info_get_remaining_time (info) > OPERATION_MINIMUM_TIME;
-}
-
-static GList *
-get_filtered_progress_infos (NautilusToolbar *self)
-{
-    GList *l;
-    GList *filtered_progress_infos;
-    GList *progress_infos;
-
-    progress_infos = nautilus_progress_info_manager_get_all_infos (self->progress_manager);
-    filtered_progress_infos = NULL;
-
-    for (l = progress_infos; l != NULL; l = l->next)
-    {
-        if (should_show_progress_info (l->data))
-        {
-            filtered_progress_infos = g_list_append (filtered_progress_infos, l->data);
-        }
-    }
-
-    return filtered_progress_infos;
-}
-
-static gboolean
-should_hide_operations_button (NautilusToolbar *self)
-{
-    GList *progress_infos;
-    GList *l;
-
-    progress_infos = get_filtered_progress_infos (self);
-
-    for (l = progress_infos; l != NULL; l = l->next)
-    {
-        if (nautilus_progress_info_get_total_elapsed_time (l->data) +
-            nautilus_progress_info_get_remaining_time (l->data) > OPERATION_MINIMUM_TIME &&
-            !nautilus_progress_info_get_is_cancelled (l->data) &&
-            !nautilus_progress_info_get_is_finished (l->data))
-        {
-            return FALSE;
-        }
-    }
-
-    g_list_free (progress_infos);
-
-    return TRUE;
-}
-
-static gboolean
-on_remove_finished_operations_timeout (NautilusToolbar *self)
-{
-    nautilus_progress_info_manager_remove_finished_or_cancelled_infos (self->progress_manager);
-    if (should_hide_operations_button (self))
-    {
-        gtk_revealer_set_reveal_child (GTK_REVEALER (self->operations_revealer),
-                                       FALSE);
-    }
-    else
-    {
-        update_operations (self);
-    }
-
-    self->remove_finished_operations_timeout_id = 0;
-
-    return G_SOURCE_REMOVE;
-}
-
-static void
-unschedule_remove_finished_operations (NautilusToolbar *self)
-{
-    if (self->remove_finished_operations_timeout_id != 0)
-    {
-        g_source_remove (self->remove_finished_operations_timeout_id);
-        self->remove_finished_operations_timeout_id = 0;
-    }
-}
-
-static void
-schedule_remove_finished_operations (NautilusToolbar *self)
-{
-    if (self->remove_finished_operations_timeout_id == 0)
-    {
-        self->remove_finished_operations_timeout_id =
-            g_timeout_add_seconds (REMOVE_FINISHED_OPERATIONS_TIEMOUT,
-                                   (GSourceFunc) on_remove_finished_operations_timeout,
-                                   self);
-    }
-}
-
-static void
-remove_operations_button_attention_style (NautilusToolbar *self)
-{
-    GtkStyleContext *style_context;
-
-    style_context = gtk_widget_get_style_context (self->operations_button);
-    gtk_style_context_remove_class (style_context,
-                                    "nautilus-operations-button-needs-attention");
-}
-
-static gboolean
-on_remove_operations_button_attention_style_timeout (NautilusToolbar *self)
-{
-    remove_operations_button_attention_style (self);
-    self->operations_button_attention_timeout_id = 0;
-
-    return G_SOURCE_REMOVE;
-}
-
-static void
-unschedule_operations_button_attention_style (NautilusToolbar *self)
-{
-    if (self->operations_button_attention_timeout_id != 0)
-    {
-        g_source_remove (self->operations_button_attention_timeout_id);
-        self->operations_button_attention_timeout_id = 0;
-    }
-}
-
-static void
-add_operations_button_attention_style (NautilusToolbar *self)
-{
-    GtkStyleContext *style_context;
-
-    style_context = gtk_widget_get_style_context (self->operations_button);
-
-    unschedule_operations_button_attention_style (self);
-    remove_operations_button_attention_style (self);
-
-    gtk_style_context_add_class (style_context,
-                                 "nautilus-operations-button-needs-attention");
-    self->operations_button_attention_timeout_id = g_timeout_add (NEEDS_ATTENTION_ANIMATION_TIMEOUT,
-                                                                  (GSourceFunc) 
on_remove_operations_button_attention_style_timeout,
-                                                                  self);
-}
-
-static void
-on_progress_info_cancelled (NautilusToolbar *self)
-{
-    /* Update the pie chart progress */
-    gtk_widget_queue_draw (self->operations_icon);
-
-    if (!nautilus_progress_manager_has_viewers (self->progress_manager))
-    {
-        schedule_remove_finished_operations (self);
-    }
-}
-
-static void
-on_progress_info_progress_changed (NautilusToolbar *self)
-{
-    /* Update the pie chart progress */
-    gtk_widget_queue_draw (self->operations_icon);
-}
-
-static void
-on_progress_info_finished (NautilusToolbar      *self,
-                           NautilusProgressInfo *info)
-{
-    gchar *main_label;
-    GFile *folder_to_open;
-
-    /* Update the pie chart progress */
-    gtk_widget_queue_draw (self->operations_icon);
-
-    if (!nautilus_progress_manager_has_viewers (self->progress_manager))
-    {
-        schedule_remove_finished_operations (self);
-    }
-
-    folder_to_open = nautilus_progress_info_get_destination (info);
-    /* If destination is null, don't show a notification. This happens when the
-     * operation is a trash operation, which we already show a diferent kind of
-     * notification */
-    if (!gtk_widget_is_visible (self->operations_popover) &&
-        folder_to_open != NULL)
-    {
-        add_operations_button_attention_style (self);
-        main_label = nautilus_progress_info_get_status (info);
-        nautilus_window_show_operation_notification (self->window,
-                                                     main_label,
-                                                     folder_to_open);
-        g_free (main_label);
-    }
-
-    g_clear_object (&folder_to_open);
-}
-
-static void
-disconnect_progress_infos (NautilusToolbar *self)
-{
-    GList *progress_infos;
-    GList *l;
-
-    progress_infos = nautilus_progress_info_manager_get_all_infos (self->progress_manager);
-    for (l = progress_infos; l != NULL; l = l->next)
-    {
-        g_signal_handlers_disconnect_by_data (l->data, self);
-    }
-}
-
-static void
-update_operations (NautilusToolbar *self)
-{
-    GList *progress_infos;
-    GList *l;
-    gboolean should_show_progress_button = FALSE;
-
-    disconnect_progress_infos (self);
-    g_list_store_remove_all (self->progress_infos_model);
-
-    progress_infos = get_filtered_progress_infos (self);
-    for (l = progress_infos; l != NULL; l = l->next)
-    {
-        should_show_progress_button = should_show_progress_button ||
-                                      should_show_progress_info (l->data);
-
-        g_signal_connect_swapped (l->data, "finished",
-                                  G_CALLBACK (on_progress_info_finished), self);
-        g_signal_connect_swapped (l->data, "cancelled",
-                                  G_CALLBACK (on_progress_info_cancelled), self);
-        g_signal_connect_swapped (l->data, "progress-changed",
-                                  G_CALLBACK (on_progress_info_progress_changed), self);
-        g_list_store_append (self->progress_infos_model, l->data);
-    }
-
-    g_list_free (progress_infos);
-
-    if (should_show_progress_button &&
-        !gtk_revealer_get_reveal_child (GTK_REVEALER (self->operations_revealer)))
-    {
-        add_operations_button_attention_style (self);
-        gtk_revealer_set_reveal_child (GTK_REVEALER (self->operations_revealer),
-                                       TRUE);
-        gtk_widget_queue_draw (self->operations_icon);
-    }
-
-    /* Since we removed the info widgets, we need to restore the focus */
-    if (gtk_widget_get_visible (self->operations_popover))
-    {
-        gtk_widget_grab_focus (self->operations_popover);
-    }
-}
-
-static gboolean
-on_progress_info_started_timeout (NautilusToolbar *self)
-{
-    GList *progress_infos;
-    GList *filtered_progress_infos;
-
-    update_operations (self);
-
-    /* In case we didn't show the operations button because the operation total
-     * time stimation is not good enough, update again to make sure we don't miss
-     * a long time operation because of that */
-
-    progress_infos = nautilus_progress_info_manager_get_all_infos (self->progress_manager);
-    filtered_progress_infos = get_filtered_progress_infos (self);
-    if (!nautilus_progress_manager_are_all_infos_finished_or_cancelled (self->progress_manager) &&
-        g_list_length (progress_infos) != g_list_length (filtered_progress_infos))
-    {
-        g_list_free (filtered_progress_infos);
-        return G_SOURCE_CONTINUE;
-    }
-    else
-    {
-        g_list_free (filtered_progress_infos);
-        self->start_operations_timeout_id = 0;
-        return G_SOURCE_REMOVE;
-    }
-}
-
-static void
-schedule_operations_start (NautilusToolbar *self)
-{
-    if (self->start_operations_timeout_id == 0)
-    {
-        /* Timeout is a little more than what we require for a stimated operation
-         * total time, to make sure the stimated total time is correct */
-        self->start_operations_timeout_id =
-            g_timeout_add (SECONDS_NEEDED_FOR_APROXIMATE_TRANSFER_RATE * 1000 + 500,
-                           (GSourceFunc) on_progress_info_started_timeout,
-                           self);
-    }
-}
-
-static void
-unschedule_operations_start (NautilusToolbar *self)
-{
-    if (self->start_operations_timeout_id != 0)
-    {
-        g_source_remove (self->start_operations_timeout_id);
-        self->start_operations_timeout_id = 0;
-    }
-}
-
-static void
-on_progress_info_started (NautilusProgressInfo *info,
-                          NautilusToolbar      *self)
-{
-    g_signal_handlers_disconnect_by_data (info, self);
-    schedule_operations_start (self);
-}
-
-static void
-on_new_progress_info (NautilusProgressInfoManager *manager,
-                      NautilusProgressInfo        *info,
-                      NautilusToolbar             *self)
-{
-    g_signal_connect (info, "started",
-                      G_CALLBACK (on_progress_info_started), self);
-}
-
-static void
-on_operations_icon_draw (GtkDrawingArea  *drawing_area,
-                         cairo_t         *cr,
-                         int              width,
-                         int              height,
-                         NautilusToolbar *self)
-{
-    GtkWidget *widget = GTK_WIDGET (drawing_area);
-    gfloat elapsed_progress = 0;
-    gint remaining_progress = 0;
-    gint total_progress;
-    gdouble ratio;
-    GList *progress_infos;
-    GList *l;
-    gboolean all_cancelled;
-    GdkRGBA background;
-    GdkRGBA foreground;
-    GtkStyleContext *style_context;
-
-    style_context = gtk_widget_get_style_context (widget);
-    gtk_style_context_get_color (style_context, &foreground);
-    background = foreground;
-    background.alpha *= 0.3;
-
-    all_cancelled = TRUE;
-    progress_infos = get_filtered_progress_infos (self);
-    for (l = progress_infos; l != NULL; l = l->next)
-    {
-        if (!nautilus_progress_info_get_is_cancelled (l->data))
-        {
-            all_cancelled = FALSE;
-            remaining_progress += nautilus_progress_info_get_remaining_time (l->data);
-            elapsed_progress += nautilus_progress_info_get_elapsed_time (l->data);
-        }
-    }
-
-    g_list_free (progress_infos);
-
-    total_progress = remaining_progress + elapsed_progress;
-
-    if (all_cancelled)
-    {
-        ratio = 1.0;
-    }
-    else
-    {
-        if (total_progress > 0)
-        {
-            ratio = MAX (0.05, elapsed_progress / total_progress);
-        }
-        else
-        {
-            ratio = 0.05;
-        }
-    }
-
-
-    width = gtk_widget_get_allocated_width (widget);
-    height = gtk_widget_get_allocated_height (widget);
-
-    gdk_cairo_set_source_rgba (cr, &background);
-    cairo_arc (cr,
-               width / 2.0, height / 2.0,
-               MIN (width, height) / 2.0,
-               0, 2 * G_PI);
-    cairo_fill (cr);
-    cairo_move_to (cr, width / 2.0, height / 2.0);
-    gdk_cairo_set_source_rgba (cr, &foreground);
-    cairo_arc (cr,
-               width / 2.0, height / 2.0,
-               MIN (width, height) / 2.0,
-               -G_PI / 2.0, ratio * 2 * G_PI - G_PI / 2.0);
-
-    cairo_fill (cr);
-}
-
-static void
-on_operations_popover_notify_visible (NautilusToolbar *self,
-                                      GParamSpec      *pspec,
-                                      GObject         *popover)
-{
-    if (gtk_widget_get_visible (GTK_WIDGET (popover)))
-    {
-        unschedule_remove_finished_operations (self);
-        nautilus_progress_manager_add_viewer (self->progress_manager,
-                                              G_OBJECT (self));
-    }
-    else
-    {
-        nautilus_progress_manager_remove_viewer (self->progress_manager,
-                                                 G_OBJECT (self));
-    }
-}
-
-static void
-on_progress_has_viewers_changed (NautilusProgressInfoManager *manager,
-                                 NautilusToolbar             *self)
-{
-    if (nautilus_progress_manager_has_viewers (manager))
-    {
-        unschedule_remove_finished_operations (self);
-        return;
-    }
-
-    if (nautilus_progress_manager_are_all_infos_finished_or_cancelled (manager))
-    {
-        unschedule_remove_finished_operations (self);
-        schedule_remove_finished_operations (self);
-    }
-}
-
 static void
 update_action (NautilusToolbar *self,
                const char      *action_name,
@@ -659,19 +209,6 @@ on_location_entry_focus_changed (GObject    *object,
     }
 }
 
-static GtkWidget *
-operations_list_create_widget (GObject  *item,
-                               gpointer  user_data)
-{
-    NautilusProgressInfo *info = NAUTILUS_PROGRESS_INFO (item);
-    GtkWidget *widget;
-
-    widget = nautilus_progress_info_widget_new (info);
-    gtk_widget_show (widget);
-
-    return widget;
-}
-
 static void
 nautilus_toolbar_constructed (GObject *object)
 {
@@ -689,25 +226,6 @@ nautilus_toolbar_constructed (GObject *object)
                     self->location_entry_close_button);
     g_signal_connect (self->location_entry_close_button, "clicked",
                       G_CALLBACK (on_location_entry_close), self);
-
-    self->progress_manager = nautilus_progress_info_manager_dup_singleton ();
-    g_signal_connect (self->progress_manager, "new-progress-info",
-                      G_CALLBACK (on_new_progress_info), self);
-    g_signal_connect (self->progress_manager, "has-viewers-changed",
-                      G_CALLBACK (on_progress_has_viewers_changed), self);
-
-    self->progress_infos_model = g_list_store_new (NAUTILUS_TYPE_PROGRESS_INFO);
-    gtk_list_box_bind_model (GTK_LIST_BOX (self->operations_list),
-                             G_LIST_MODEL (self->progress_infos_model),
-                             (GtkListBoxCreateWidgetFunc) operations_list_create_widget,
-                             NULL,
-                             NULL);
-    update_operations (self);
-
-    g_signal_connect (self->operations_popover, "show",
-                      (GCallback) gtk_widget_grab_focus, NULL);
-    g_signal_connect_swapped (self->operations_popover, "closed",
-                              (GCallback) gtk_widget_grab_focus, self);
     g_signal_connect (self->location_entry, "notify::has-focus",
                       G_CALLBACK (on_location_entry_focus_changed), self);
 
@@ -723,13 +241,9 @@ static void
 nautilus_toolbar_init (NautilusToolbar *self)
 {
     g_type_ensure (NAUTILUS_TYPE_HISTORY_CONTROLS);
+    g_type_ensure (NAUTILUS_TYPE_PROGRESS_INDICATOR);
 
     gtk_widget_init_template (GTK_WIDGET (self));
-
-    gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (self->operations_icon),
-                                    (GtkDrawingAreaDrawFunc) on_operations_icon_draw,
-                                    self,
-                                    NULL);
 }
 
 void
@@ -939,14 +453,6 @@ nautilus_toolbar_finalize (GObject *obj)
                              on_window_slot_destroyed, self);
         self->window_slot = NULL;
     }
-    disconnect_progress_infos (self);
-    unschedule_remove_finished_operations (self);
-    unschedule_operations_start (self);
-    unschedule_operations_button_attention_style (self);
-
-    g_clear_object (&self->progress_infos_model);
-    g_signal_handlers_disconnect_by_data (self->progress_manager, self);
-    g_clear_object (&self->progress_manager);
 
     g_signal_handlers_disconnect_by_func (self->window,
                                           on_window_focus_changed, self);
@@ -1009,11 +515,6 @@ nautilus_toolbar_class_init (NautilusToolbarClass *klass)
     gtk_widget_class_set_template_from_resource (widget_class,
                                                  "/org/gnome/nautilus/ui/nautilus-toolbar.ui");
 
-    gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, operations_button);
-    gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, operations_icon);
-    gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, operations_popover);
-    gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, operations_list);
-    gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, operations_revealer);
     gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, view_menu);
     gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, view_split_button);
     gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, app_button);
@@ -1025,8 +526,6 @@ nautilus_toolbar_class_init (NautilusToolbarClass *klass)
 
     gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, search_button);
 
-    gtk_widget_class_bind_template_callback (widget_class, on_operations_popover_notify_visible);
-
     gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_TOOLBAR);
 }
 
@@ -1220,9 +719,3 @@ nautilus_toolbar_is_menu_visible (NautilusToolbar *self)
 
     return gtk_widget_is_visible (menu);
 }
-
-gboolean
-nautilus_toolbar_is_operations_button_active (NautilusToolbar *self)
-{
-    return gtk_widget_is_visible (GTK_WIDGET (self->operations_popover));
-}
diff --git a/src/nautilus-toolbar.h b/src/nautilus-toolbar.h
index e67aeedba..a61a3cb9f 100644
--- a/src/nautilus-toolbar.h
+++ b/src/nautilus-toolbar.h
@@ -47,8 +47,6 @@ void       nautilus_toolbar_set_active_slot    (NautilusToolbar    *toolbar,
 
 gboolean   nautilus_toolbar_is_menu_visible    (NautilusToolbar *toolbar);
 
-gboolean   nautilus_toolbar_is_operations_button_active (NautilusToolbar *toolbar);
-
 void       nautilus_toolbar_on_window_constructed       (NautilusToolbar *toolbar);
 
 void nautilus_toolbar_set_window_slot (NautilusToolbar    *self,
diff --git a/src/resources/nautilus.gresource.xml b/src/resources/nautilus.gresource.xml
index 6e74c0552..e2379f8bb 100644
--- a/src/resources/nautilus.gresource.xml
+++ b/src/resources/nautilus.gresource.xml
@@ -7,6 +7,7 @@
     <file>ui/nautilus-pathbar-context-menu.ui</file>
     <file>ui/nautilus-toolbar.ui</file>
     <file>ui/nautilus-history-controls.ui</file>
+    <file>ui/nautilus-progress-indicator.ui</file>
     <file>ui/nautilus-toolbar-view-menu.ui</file>
     <file>ui/nautilus-column-chooser.ui</file>
     <file>ui/nautilus-list-view-column-editor.ui</file>
diff --git a/src/resources/ui/nautilus-progress-indicator.ui b/src/resources/ui/nautilus-progress-indicator.ui
new file mode 100644
index 000000000..a44f8d1e5
--- /dev/null
+++ b/src/resources/ui/nautilus-progress-indicator.ui
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <menu id="view_menu">
+    <section>
+      <attribute name="label" translatable="yes" context="menu item" comments="Translators: a menu item in a 
group of sorting options in a toolbar menu, with criterions such as &quot;A-Z&quot; or &quot;Last 
Modified&quot;.">Sort</attribute>
+      <!--
+           Sort section.
+
+           The toolbar code assumes this is the second item of this menu model.
+           Its contents is provided by the view.
+      -->
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_Visible Columns…</attribute>
+        <attribute name="action">view.visible-columns</attribute>
+        <attribute name="hidden-when">action-missing</attribute>
+      </item>
+    </section>
+  </menu>
+  <object class="GtkPopover" id="operations_popover">
+    <property name="child">
+      <object class="GtkScrolledWindow">
+        <property name="hscrollbar_policy">never</property>
+        <property name="max_content_height">270</property>
+        <property name="propagate_natural_height">True</property>
+        <property name="child">
+          <object class="GtkListBox" id="operations_list">
+            <property name="margin_start">6</property>
+            <property name="margin_end">6</property>
+            <property name="margin_top">6</property>
+            <property name="margin_bottom">6</property>
+            <property name="selection-mode">none</property>
+            <property name="activate-on-single-click">False</property>
+            <style>
+              <class name="operations-list"/>
+            </style>
+          </object>
+        </property>
+      </object>
+    </property>
+    <signal name="notify::visible" handler="on_operations_popover_notify_visible" 
object="NautilusProgressIndicator" swapped="yes"/>
+  </object>
+  <template class="NautilusProgressIndicator" parent="AdwBin">
+    <property name="child">
+      <object class="GtkRevealer" id="operations_revealer">
+        <property name="halign">center</property>
+        <property name="valign">center</property>
+        <property name="transition_type">slide-right</property>
+        <property name="child">
+          <object class="GtkMenuButton" id="operations_button">
+            <property name="tooltip_text" translatable="yes">Show operations</property>
+            <property name="popover">operations_popover</property>
+            <child>
+              <object class="GtkDrawingArea" id="operations_icon">
+                <property name="width_request">16</property>
+                <property name="height_request">16</property>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
+              </object>
+            </child>
+            <style>
+              <class name="image-button"/>
+            </style>
+          </object>
+        </property>
+      </object>
+    </property>
+  </template>
+</interface>
diff --git a/src/resources/ui/nautilus-toolbar.ui b/src/resources/ui/nautilus-toolbar.ui
index 083aeeb6b..aaca5de08 100644
--- a/src/resources/ui/nautilus-toolbar.ui
+++ b/src/resources/ui/nautilus-toolbar.ui
@@ -80,29 +80,6 @@
       </item>
     </section>
   </menu>
-  <object class="GtkPopover" id="operations_popover">
-    <property name="child">
-      <object class="GtkScrolledWindow">
-        <property name="hscrollbar_policy">never</property>
-        <property name="max_content_height">270</property>
-        <property name="propagate_natural_height">True</property>
-        <property name="child">
-          <object class="GtkListBox" id="operations_list">
-            <property name="margin_start">6</property>
-            <property name="margin_end">6</property>
-            <property name="margin_top">6</property>
-            <property name="margin_bottom">6</property>
-            <property name="selection-mode">none</property>
-            <property name="activate-on-single-click">False</property>
-            <style>
-              <class name="operations-list"/>
-            </style>
-          </object>
-        </property>
-      </object>
-    </property>
-    <signal name="notify::visible" handler="on_operations_popover_notify_visible" object="NautilusToolbar" 
swapped="yes"/>
-  </object>
   <template class="NautilusToolbar" parent="AdwBin">
     <child>
       <object class="AdwHeaderBar">
@@ -235,28 +212,7 @@
           </object>
         </child>
         <child type="end">
-          <object class="GtkRevealer" id="operations_revealer">
-            <property name="halign">center</property>
-            <property name="valign">center</property>
-            <property name="transition_type">slide-right</property>
-            <property name="child">
-              <object class="GtkMenuButton" id="operations_button">
-                <property name="tooltip_text" translatable="yes">Show operations</property>
-                <property name="popover">operations_popover</property>
-                <child>
-                  <object class="GtkDrawingArea" id="operations_icon">
-                    <property name="width_request">16</property>
-                    <property name="height_request">16</property>
-                    <property name="halign">center</property>
-                    <property name="valign">center</property>
-                  </object>
-                </child>
-                <style>
-                  <class name="image-button"/>
-                </style>
-              </object>
-            </property>
-          </object>
+          <object class="NautilusProgressIndicator"/>
         </child>
         <child type="end">
           <object class="GtkSeparator">


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