[nautilus/wip/csoriano/operations: 8/8] operations: show a notification when operation finishes
- From: Carlos Soriano Sánchez <csoriano src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [nautilus/wip/csoriano/operations: 8/8] operations: show a notification when operation finishes
- Date: Tue, 14 Jul 2015 17:48:21 +0000 (UTC)
commit f191cdb79a0107e9ba5f499eb45105b46a737835
Author: Carlos Soriano <csoriano gnome org>
Date: Tue Jul 14 19:46:21 2015 +0200
operations: show a notification when operation finishes
When the operations popover is not visible, we have to give
feedback to the user about if an operation finished.
For that, show a in-app notification in case the operation popover
is closed and a operation finishes. Also, to link the notification
with the operation button, make the button pulse for a second.
libnautilus-private/nautilus-file-operations.c | 21 +++
libnautilus-private/nautilus-progress-info.c | 25 ++++
libnautilus-private/nautilus-progress-info.h | 4 +
src/nautilus-toolbar.c | 38 ++++++-
src/nautilus-window.c | 166 ++++++++++++++++++------
src/nautilus-window.h | 4 +
src/nautilus-window.ui | 69 ++++++++++
7 files changed, 285 insertions(+), 42 deletions(-)
---
diff --git a/libnautilus-private/nautilus-file-operations.c b/libnautilus-private/nautilus-file-operations.c
index 88d35ff..84bbcf7 100644
--- a/libnautilus-private/nautilus-file-operations.c
+++ b/libnautilus-private/nautilus-file-operations.c
@@ -4901,6 +4901,9 @@ nautilus_file_operations_copy_file (GFile *source_file,
job->done_callback_data = done_callback_data;
job->files = g_list_append (NULL, g_object_ref (source_file));
job->destination = g_object_ref (target_dir);
+ /* Need to indicate the destination for the operation notification open
+ * button. */
+ nautilus_progress_info_set_destination (((CommonJob *)job)->progress, target_dir);
job->target_name = g_strdup (new_name);
job->debuting_files = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref,
NULL);
@@ -4938,6 +4941,9 @@ nautilus_file_operations_copy (GList *files,
job->done_callback_data = done_callback_data;
job->files = g_list_copy_deep (files, (GCopyFunc) g_object_ref, NULL);
job->destination = g_object_ref (target_dir);
+ /* Need to indicate the destination for the operation notification open
+ * button. */
+ nautilus_progress_info_set_destination (((CommonJob *)job)->progress, target_dir);
if (relative_item_points != NULL &&
relative_item_points->len > 0) {
job->icon_positions =
@@ -5476,6 +5482,9 @@ nautilus_file_operations_move (GList *files,
job->done_callback_data = done_callback_data;
job->files = g_list_copy_deep (files, (GCopyFunc) g_object_ref, NULL);
job->destination = g_object_ref (target_dir);
+ /* Need to indicate the destination for the operation notification open
+ * button. */
+ nautilus_progress_info_set_destination (((CommonJob *)job)->progress, target_dir);
if (relative_item_points != NULL &&
relative_item_points->len > 0) {
job->icon_positions =
@@ -5802,6 +5811,9 @@ nautilus_file_operations_link (GList *files,
job->done_callback_data = done_callback_data;
job->files = g_list_copy_deep (files, (GCopyFunc) g_object_ref, NULL);
job->destination = g_object_ref (target_dir);
+ /* Need to indicate the destination for the operation notification open
+ * button. */
+ nautilus_progress_info_set_destination (((CommonJob *)job)->progress, target_dir);
if (relative_item_points != NULL &&
relative_item_points->len > 0) {
job->icon_positions =
@@ -5837,12 +5849,19 @@ nautilus_file_operations_duplicate (GList *files,
gpointer done_callback_data)
{
CopyMoveJob *job;
+ GFile *parent;
job = op_job_new (CopyMoveJob, parent_window);
job->done_callback = done_callback;
job->done_callback_data = done_callback_data;
job->files = g_list_copy_deep (files, (GCopyFunc) g_object_ref, NULL);
job->destination = NULL;
+ /* Duplicate files doesn't have a destination, since is the same as source.
+ * For that set as destination the source parent folder */
+ parent = g_file_get_parent (files->data);
+ /* Need to indicate the destination for the operation notification open
+ * button. */
+ nautilus_progress_info_set_destination (((CommonJob *)job)->progress, parent);
if (relative_item_points != NULL &&
relative_item_points->len > 0) {
job->icon_positions =
@@ -5868,6 +5887,8 @@ nautilus_file_operations_duplicate (GList *files,
NULL, /* destroy notify */
0,
job->common.cancellable);
+
+ g_object_unref (parent);
}
static gboolean
diff --git a/libnautilus-private/nautilus-progress-info.c b/libnautilus-private/nautilus-progress-info.c
index 389aaeb..e6a2c70 100644
--- a/libnautilus-private/nautilus-progress-info.c
+++ b/libnautilus-private/nautilus-progress-info.c
@@ -64,6 +64,8 @@ struct _NautilusProgressInfo
gboolean finish_at_idle;
gboolean changed_at_idle;
gboolean progress_at_idle;
+
+ GFile *destination;
};
struct _NautilusProgressInfoClass
@@ -85,6 +87,7 @@ nautilus_progress_info_finalize (GObject *object)
g_free (info->status);
g_free (info->details);
g_object_unref (info->cancellable);
+ g_clear_object (&info->destination);
if (G_OBJECT_CLASS (nautilus_progress_info_parent_class)->finalize) {
(*G_OBJECT_CLASS (nautilus_progress_info_parent_class)->finalize) (object);
@@ -614,3 +617,25 @@ nautilus_progress_info_get_elapsed_time (NautilusProgressInfo *info)
return elapsed_time;
}
+
+void
+nautilus_progress_info_set_destination (NautilusProgressInfo *info,
+ GFile *file)
+{
+ G_LOCK (progress_info);
+ g_clear_object (&info->destination);
+ info->destination = g_object_ref (file);
+ G_UNLOCK (progress_info);
+}
+
+GFile *
+nautilus_progress_info_get_destination (NautilusProgressInfo *info)
+{
+ GFile *destination;
+
+ G_LOCK (progress_info);
+ destination = g_object_ref (info->destination);
+ G_UNLOCK (progress_info);
+
+ return destination;
+}
diff --git a/libnautilus-private/nautilus-progress-info.h b/libnautilus-private/nautilus-progress-info.h
index 0ce0ad1..e842667 100644
--- a/libnautilus-private/nautilus-progress-info.h
+++ b/libnautilus-private/nautilus-progress-info.h
@@ -85,6 +85,10 @@ void nautilus_progress_info_set_elapsed_time (NautilusProgressInfo *inf
gdouble time);
gdouble nautilus_progress_info_get_elapsed_time (NautilusProgressInfo *info);
+void nautilus_progress_info_set_destination (NautilusProgressInfo *info,
+ GFile *file);
+GFile *nautilus_progress_info_get_destination (NautilusProgressInfo *info);
+
#endif /* NAUTILUS_PROGRESS_INFO_H */
diff --git a/src/nautilus-toolbar.c b/src/nautilus-toolbar.c
index d6b39de..f153ceb 100644
--- a/src/nautilus-toolbar.c
+++ b/src/nautilus-toolbar.c
@@ -59,6 +59,7 @@ struct _NautilusToolbarPrivate {
guint popup_timeout_id;
guint operations_timeout_id;
+ guint operations_button_attention_timeout_id;
GtkWidget *operations_button;
GtkWidget *view_button;
@@ -455,13 +456,48 @@ remove_finished_operations (NautilusToolbar *self)
update_operations_button_visibility (self);
}
+static gboolean
+remove_operations_button_attention_style (NautilusToolbar *self)
+{
+ GtkStyleContext *style_context;
+
+ style_context = gtk_widget_get_style_context (self->priv->operations_button);
+ gtk_style_context_remove_class (style_context,
+ "suggested-action");
+ self->priv->operations_button_attention_timeout_id = 0;
+
+ return G_SOURCE_REMOVE;
+}
+
static void
on_progress_info_finished (NautilusProgressInfo *info,
NautilusToolbar *self)
{
- if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->priv->operations_button))) {
+ GtkStyleContext *style_context;
+ gchar *main_label;
+ GFile *folder_to_open;
+
+ 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_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->priv->operations_button)) &&
+ folder_to_open != NULL) {
remove_finished_operations (self);
+ style_context = gtk_widget_get_style_context (self->priv->operations_button);
+ gtk_style_context_add_class (style_context,
+ "suggested-action");
+ self->priv->operations_button_attention_timeout_id = g_timeout_add_seconds (1,
+ (GSourceFunc)
remove_operations_button_attention_style,
+ self);
+ main_label = nautilus_progress_info_get_status (info);
+ nautilus_window_show_operation_notification (self->priv->window,
+ main_label,
+ folder_to_open);
+
}
+
+ g_clear_object (&folder_to_open);
}
static void
diff --git a/src/nautilus-window.c b/src/nautilus-window.c
index d315316..8b00d4e 100644
--- a/src/nautilus-window.c
+++ b/src/nautilus-window.c
@@ -120,6 +120,12 @@ struct _NautilusWindowPrivate {
GtkWidget *notification_delete_close;
GtkWidget *notification_delete_undo;
guint notification_delete_timeout_id;
+ GtkWidget *notification_operation;
+ GtkWidget *notification_operation_label;
+ GtkWidget *notification_operation_close;
+ GtkWidget *notification_operation_open;
+ guint notification_operation_timeout_id;
+ GFile *folder_to_open;
/* Toolbar */
GtkWidget *toolbar;
@@ -1488,30 +1494,40 @@ nautilus_window_ensure_location_entry (NautilusWindow *window)
}
static void
-nautilus_window_on_notification_delete_undo_clicked (GtkWidget *notification,
- gpointer user_data)
+remove_notifications (NautilusWindow *window)
{
- NautilusWindow *window;
-
- window = NAUTILUS_WINDOW (user_data);
+ GtkRevealerTransitionType transition_type;
- if (window->priv->notification_delete_timeout_id != 0) {
- g_source_remove (window->priv->notification_delete_timeout_id);
- window->priv->notification_delete_timeout_id = 0;
+ /* Hide it inmediatily so we can animate the new notification. */
+ transition_type = gtk_revealer_get_transition_type (GTK_REVEALER
(window->priv->notification_delete));
+ gtk_revealer_set_transition_type (GTK_REVEALER (window->priv->notification_delete),
+ GTK_REVEALER_TRANSITION_TYPE_NONE);
+ gtk_revealer_set_reveal_child (GTK_REVEALER (window->priv->notification_delete),
+ FALSE);
+ gtk_revealer_set_transition_type (GTK_REVEALER (window->priv->notification_delete),
+ transition_type);
+ if (window->priv->notification_delete_timeout_id != 0) {
+ g_source_remove (window->priv->notification_delete_timeout_id);
+ window->priv->notification_delete_timeout_id = 0;
}
- gtk_revealer_set_reveal_child (GTK_REVEALER (window->priv->notification_delete), FALSE);
- nautilus_file_undo_manager_undo (GTK_WINDOW (window));
+ transition_type = gtk_revealer_get_transition_type (GTK_REVEALER
(window->priv->notification_operation));
+ gtk_revealer_set_transition_type (GTK_REVEALER (window->priv->notification_operation),
+ GTK_REVEALER_TRANSITION_TYPE_NONE);
+ gtk_revealer_set_reveal_child (GTK_REVEALER (window->priv->notification_operation),
+ FALSE);
+ gtk_revealer_set_transition_type (GTK_REVEALER (window->priv->notification_operation),
+ transition_type);
+ if (window->priv->notification_operation_timeout_id != 0) {
+ g_source_remove (window->priv->notification_operation_timeout_id);
+ window->priv->notification_operation_timeout_id = 0;
+ }
}
+
static void
-nautilus_window_on_notification_delete_close_clicked (GtkWidget *notification,
- gpointer user_data)
+hide_notification_delete (NautilusWindow *window)
{
- NautilusWindow *window;
-
- window = NAUTILUS_WINDOW (user_data);
-
if (window->priv->notification_delete_timeout_id != 0) {
g_source_remove (window->priv->notification_delete_timeout_id);
window->priv->notification_delete_timeout_id = 0;
@@ -1520,19 +1536,26 @@ nautilus_window_on_notification_delete_close_clicked (GtkWidget *notification,
gtk_revealer_set_reveal_child (GTK_REVEALER (window->priv->notification_delete), FALSE);
}
-static gboolean
-nautilus_window_on_notification_delete_timeout (gpointer user_data)
+static void
+nautilus_window_on_notification_delete_undo_clicked (GtkWidget *notification,
+ NautilusWindow *window)
{
- NautilusWindow *window;
+ hide_notification_delete (window);
- window = NAUTILUS_WINDOW (user_data);
+ nautilus_file_undo_manager_undo (GTK_WINDOW (window));
+}
- if (window->priv->notification_delete_timeout_id != 0) {
- g_source_remove (window->priv->notification_delete_timeout_id);
- window->priv->notification_delete_timeout_id = 0;
- }
+static void
+nautilus_window_on_notification_delete_close_clicked (GtkWidget *notification,
+ NautilusWindow *window)
+{
+ hide_notification_delete (window);
+}
- gtk_revealer_set_reveal_child (GTK_REVEALER (window->priv->notification_delete), FALSE);
+static gboolean
+nautilus_window_on_notification_delete_timeout (NautilusWindow *window)
+{
+ hide_notification_delete (window);
return FALSE;
}
@@ -1566,20 +1589,9 @@ nautilus_window_on_undo_changed (NautilusFileUndoManager *manager,
{
NautilusFileUndoInfo *undo_info;
NautilusFileUndoManagerState state;
- int transition_durantion;
gchar *label;
GList *files;
- /* Hide it inmediatily so we can animate the new notification. */
- transition_durantion = gtk_revealer_get_transition_duration (GTK_REVEALER
(window->priv->notification_delete));
- gtk_revealer_set_transition_duration (GTK_REVEALER (window->priv->notification_delete), 0);
- gtk_revealer_set_reveal_child (GTK_REVEALER (window->priv->notification_delete), FALSE);
- gtk_revealer_set_transition_duration (GTK_REVEALER (window->priv->notification_delete),
transition_durantion);
- if (window->priv->notification_delete_timeout_id != 0) {
- g_source_remove (window->priv->notification_delete_timeout_id);
- window->priv->notification_delete_timeout_id = 0;
- }
-
undo_info = nautilus_file_undo_manager_get_action ();
state = nautilus_file_undo_manager_get_state ();
@@ -1597,7 +1609,7 @@ nautilus_window_on_undo_changed (NautilusFileUndoManager *manager,
gtk_label_set_markup (GTK_LABEL (window->priv->notification_delete_label), label);
gtk_revealer_set_reveal_child (GTK_REVEALER (window->priv->notification_delete),
TRUE);
window->priv->notification_delete_timeout_id = g_timeout_add_seconds
(NOTIFICATION_TIMEOUT,
-
nautilus_window_on_notification_delete_timeout,
+ (GSourceFunc)
nautilus_window_on_notification_delete_timeout,
window);
g_free (label);
}
@@ -1606,6 +1618,74 @@ nautilus_window_on_undo_changed (NautilusFileUndoManager *manager,
}
static void
+hide_notification_operation (NautilusWindow *window)
+{
+ if (window->priv->notification_operation_timeout_id != 0) {
+ g_source_remove (window->priv->notification_operation_timeout_id);
+ window->priv->notification_operation_timeout_id = 0;
+ }
+
+ gtk_revealer_set_reveal_child (GTK_REVEALER (window->priv->notification_operation), FALSE);
+ g_clear_object (&window->priv->folder_to_open);
+}
+
+static void
+on_notification_operation_open_clicked (GtkWidget *notification,
+ NautilusWindow *window)
+{
+ nautilus_window_slot_open_location (window->priv->active_slot,
+ window->priv->folder_to_open,
+ 0);
+ hide_notification_operation (window);
+}
+
+static void
+on_notification_operation_close_clicked (GtkWidget *notification,
+ NautilusWindow *window)
+{
+ hide_notification_operation (window);
+}
+
+static gboolean
+on_notification_operation_timeout (NautilusWindow *window)
+{
+ hide_notification_operation (window);
+
+ return FALSE;
+}
+
+void
+nautilus_window_show_operation_notification (NautilusWindow *window,
+ gchar *main_label,
+ GFile *folder_to_open)
+{
+ gchar *button_label;
+ gchar *folder_name;
+ NautilusFile *folder;
+
+ if (gtk_window_has_toplevel_focus (GTK_WINDOW (window)) &&
+ !NAUTILUS_IS_DESKTOP_WINDOW (window)) {
+ remove_notifications (window);
+ window->priv->folder_to_open = g_object_ref (folder_to_open);
+ folder = nautilus_file_get (folder_to_open);
+ folder_name = nautilus_file_get_display_name (folder);
+ button_label = g_strdup_printf (_("Open %s"), folder_name);
+ gtk_label_set_text (GTK_LABEL (window->priv->notification_operation_label),
+ main_label);
+ gtk_button_set_label (GTK_BUTTON (window->priv->notification_operation_open),
+ button_label);
+ gtk_revealer_set_reveal_child (GTK_REVEALER (window->priv->notification_operation), TRUE);
+ window->priv->notification_operation_timeout_id = g_timeout_add_seconds (NOTIFICATION_TIMEOUT,
+ (GSourceFunc)
on_notification_operation_timeout,
+ window);
+ g_object_unref (folder);
+ g_free (folder_name);
+ g_free (button_label);
+ }
+
+}
+
+static void
path_bar_location_changed_callback (GtkWidget *widget,
GFile *location,
NautilusWindow *window)
@@ -2104,10 +2184,7 @@ nautilus_window_finalize (GObject *object)
window->priv->sidebar_width_handler_id = 0;
}
- if (window->priv->notification_delete_timeout_id != 0) {
- g_source_remove (window->priv->notification_delete_timeout_id);
- window->priv->notification_delete_timeout_id = 0;
- }
+ remove_notifications (window);
g_signal_handlers_disconnect_by_func (nautilus_file_undo_manager_get (),
G_CALLBACK (nautilus_window_on_undo_changed),
@@ -2508,6 +2585,10 @@ nautilus_window_class_init (NautilusWindowClass *class)
gtk_widget_class_bind_template_child_private (wclass, NautilusWindow, notification_delete_label);
gtk_widget_class_bind_template_child_private (wclass, NautilusWindow, notification_delete_undo);
gtk_widget_class_bind_template_child_private (wclass, NautilusWindow, notification_delete_close);
+ gtk_widget_class_bind_template_child_private (wclass, NautilusWindow, notification_operation);
+ gtk_widget_class_bind_template_child_private (wclass, NautilusWindow, notification_operation_label);
+ gtk_widget_class_bind_template_child_private (wclass, NautilusWindow, notification_operation_open);
+ gtk_widget_class_bind_template_child_private (wclass, NautilusWindow, notification_operation_close);
properties[PROP_DISABLE_CHROME] =
g_param_spec_boolean ("disable-chrome",
@@ -2558,6 +2639,9 @@ nautilus_window_class_init (NautilusWindowClass *class)
G_CALLBACK(use_extra_mouse_buttons_changed),
NULL);
+ gtk_widget_class_bind_template_callback (wclass, on_notification_operation_open_clicked);
+ gtk_widget_class_bind_template_callback (wclass, on_notification_operation_close_clicked);
+
g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
}
diff --git a/src/nautilus-window.h b/src/nautilus-window.h
index 1612145..4b16bf7 100644
--- a/src/nautilus-window.h
+++ b/src/nautilus-window.h
@@ -146,4 +146,8 @@ void nautilus_window_sync_allow_stop (NautilusWindow *window,
NautilusWindowSlot *slot);
void nautilus_window_sync_title (NautilusWindow *window,
NautilusWindowSlot *slot);
+
+void nautilus_window_show_operation_notification (NautilusWindow *window,
+ gchar *main_label,
+ GFile *folder_to_open);
#endif
diff --git a/src/nautilus-window.ui b/src/nautilus-window.ui
index 5205ced..2a8c439 100644
--- a/src/nautilus-window.ui
+++ b/src/nautilus-window.ui
@@ -123,6 +123,75 @@
</child>
</object>
</child>
+ <child type="overlay">
+ <object class="GtkRevealer" id="notification_operation">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">center</property>
+ <property name="valign">start</property>
+ <property name="transition_duration">100</property>
+ <child>
+ <object class="GtkFrame">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">4</property>
+ <child>
+ <object class="GtkLabel" id="notification_operation_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="max_width_chars">50</property>
+ <property name="ellipsize">middle</property>
+ <property name="margin_end">30</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="notification_operation_open">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="no_show_all">True</property>
+ <property name="margin_end">6</property>
+ <signal name="clicked" handler="on_notification_operation_open_clicked"
object="NautilusWindow" swapped="no"/>
+ <style>
+ <class name="text-button"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="notification_operation_close">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="focus_on_click">False</property>
+ <signal name="clicked" handler="on_notification_operation_close_clicked"
object="NautilusWindow" swapped="no"/>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">window-close-symbolic</property>
+ <property name="icon_size">2</property>
+ </object>
+ </child>
+ <style>
+ <class name="image-button"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <style>
+ <class name="app-notification"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
</object>
</child>
</object>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]