[nautilus/wip/toolbar-start-end: 73/77] toolbar: Factor out operations progress indicator
- From: António Fernandes <antoniof src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [nautilus/wip/toolbar-start-end: 73/77] toolbar: Factor out operations progress indicator
- Date: Sat, 6 Aug 2022 22:53:08 +0000 (UTC)
commit 5464e723e0b3df6a7efea1acb4fc97229eb6731f
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 6a1e21f28..a060526e8 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..ce857047c
--- /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 b202597d3..a4273405e 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 "A-Z" or "Last
Modified".">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]