Closes #4644 Spinner fix
This commit is contained in:
parent
1054b99264
commit
9068d6330b
9 changed files with 262 additions and 78 deletions
|
|
@ -331,6 +331,7 @@ client/ui/icon-factory.vala
|
|||
client/ui/main-toolbar.vala
|
||||
client/ui/main-window.vala
|
||||
client/ui/monitored-progress-bar.vala
|
||||
client/ui/monitored-spinner.vala
|
||||
|
||||
client/util/util-date.vala
|
||||
client/util/util-email.vala
|
||||
|
|
|
|||
|
|
@ -73,7 +73,6 @@ public class GearyController {
|
|||
private Cancellable cancellable_open_account = new Cancellable();
|
||||
private Gee.HashMap<Geary.Account, Cancellable> inbox_cancellables
|
||||
= new Gee.HashMap<Geary.Account, Cancellable>();
|
||||
private int busy_count = 0;
|
||||
private Gee.Set<Geary.Conversation> selected_conversations = new Gee.HashSet<Geary.Conversation>();
|
||||
private Geary.Conversation? last_deleted_conversation = null;
|
||||
private Gee.LinkedList<ComposerWindow> composer_windows = new Gee.LinkedList<ComposerWindow>();
|
||||
|
|
@ -193,8 +192,6 @@ public class GearyController {
|
|||
|
||||
main_window.conversation_list_view.grab_focus();
|
||||
|
||||
set_busy(false);
|
||||
|
||||
// Start Geary.
|
||||
try {
|
||||
yield Geary.Engine.instance.open_async(GearyApplication.instance.get_user_data_directory(),
|
||||
|
|
@ -743,8 +740,6 @@ public class GearyController {
|
|||
if (folder == current_folder)
|
||||
return;
|
||||
|
||||
set_busy(true);
|
||||
|
||||
cancel_folder();
|
||||
|
||||
// This function is not reentrant. It should be, because it can be
|
||||
|
|
@ -763,6 +758,7 @@ public class GearyController {
|
|||
if (current_conversations != null) {
|
||||
yield current_conversations.stop_monitoring_async(!current_is_inbox, null);
|
||||
current_conversations = null;
|
||||
main_window.set_progress_monitor(null);
|
||||
} else if (current_folder != null && !current_is_inbox) {
|
||||
yield current_folder.close_async();
|
||||
}
|
||||
|
|
@ -801,32 +797,21 @@ public class GearyController {
|
|||
clear_new_messages("do_select_folder (inbox)", null);
|
||||
}
|
||||
|
||||
current_conversations.scan_started.connect(on_scan_started);
|
||||
current_conversations.scan_error.connect(on_scan_error);
|
||||
current_conversations.scan_completed.connect(on_scan_completed);
|
||||
current_conversations.seed_completed.connect(on_seed_completed);
|
||||
|
||||
main_window.conversation_list_store.set_conversation_monitor(current_conversations);
|
||||
main_window.conversation_list_view.set_conversation_monitor(current_conversations);
|
||||
main_window.set_progress_monitor(current_conversations.progress_monitor);
|
||||
|
||||
if (!current_conversations.is_monitoring)
|
||||
yield current_conversations.start_monitoring_async(conversation_cancellable);
|
||||
|
||||
select_folder_mutex.release(ref mutex_token);
|
||||
|
||||
set_busy(false);
|
||||
}
|
||||
|
||||
private void on_scan_started() {
|
||||
set_busy(true);
|
||||
}
|
||||
|
||||
private void on_scan_error(Error err) {
|
||||
set_busy(false);
|
||||
}
|
||||
|
||||
private void on_scan_completed() {
|
||||
set_busy(false);
|
||||
debug("Scan error: %s", err.message);
|
||||
}
|
||||
|
||||
private void on_seed_completed() {
|
||||
|
|
@ -991,8 +976,6 @@ public class GearyController {
|
|||
Cancellable old_cancellable = cancellable_message;
|
||||
cancellable_message = new Cancellable();
|
||||
|
||||
set_busy(false);
|
||||
|
||||
old_cancellable.cancel();
|
||||
}
|
||||
|
||||
|
|
@ -1104,9 +1087,8 @@ public class GearyController {
|
|||
// Mark the emails.
|
||||
Gee.List<Geary.EmailIdentifier> ids = get_selected_folder_email_ids(preview_message_only);
|
||||
if (ids.size > 0) {
|
||||
set_busy(true);
|
||||
supports_mark.mark_email_async.begin(ids, flags_to_add, flags_to_remove,
|
||||
cancellable_message, on_mark_complete);
|
||||
cancellable_message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1201,9 +1183,8 @@ public class GearyController {
|
|||
Gee.Collection<Geary.EmailIdentifier> ids
|
||||
= get_conversation_email_ids(conversation, true, only_mark_preview);
|
||||
if (ids.size > 0) {
|
||||
set_busy(true);
|
||||
supports_mark.mark_email_async.begin(Geary.Collection.to_array_list<Geary.EmailIdentifier>(ids),
|
||||
flags_to_add, flags_to_remove, cancellable_message, on_mark_complete);
|
||||
flags_to_add, flags_to_remove, cancellable_message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1213,9 +1194,8 @@ public class GearyController {
|
|||
if (supports_mark == null)
|
||||
return;
|
||||
|
||||
set_busy(true);
|
||||
supports_mark.mark_single_email_async.begin(message.id, flags_to_add, flags_to_remove,
|
||||
cancellable_message, on_mark_complete);
|
||||
cancellable_message);
|
||||
}
|
||||
|
||||
private void on_mark_as_read() {
|
||||
|
|
@ -1246,10 +1226,6 @@ public class GearyController {
|
|||
mark_selected_conversations(null, flags);
|
||||
}
|
||||
|
||||
private void on_mark_complete() {
|
||||
set_busy(false);
|
||||
}
|
||||
|
||||
private void on_mark_as_spam() {
|
||||
Geary.Folder? destination_folder = null;
|
||||
if (current_folder.special_folder_type != Geary.SpecialFolderType.SPAM) {
|
||||
|
|
@ -1285,15 +1261,9 @@ public class GearyController {
|
|||
if (supports_copy == null)
|
||||
return;
|
||||
|
||||
set_busy(true);
|
||||
supports_copy.copy_email_async.begin(ids, destination.path, cancellable_message,
|
||||
on_copy_complete);
|
||||
supports_copy.copy_email_async.begin(ids, destination.path, cancellable_message);
|
||||
}
|
||||
|
||||
private void on_copy_complete() {
|
||||
set_busy(false);
|
||||
}
|
||||
|
||||
|
||||
private void on_move_conversation(Geary.Folder destination) {
|
||||
// Nothing to do if nothing selected.
|
||||
if (selected_conversations == null || selected_conversations.size == 0)
|
||||
|
|
@ -1307,15 +1277,9 @@ public class GearyController {
|
|||
if (supports_move == null)
|
||||
return;
|
||||
|
||||
set_busy(true);
|
||||
supports_move.move_email_async.begin(ids, destination.path, cancellable_message,
|
||||
on_move_complete);
|
||||
supports_move.move_email_async.begin(ids, destination.path, cancellable_message);
|
||||
}
|
||||
|
||||
private void on_move_complete() {
|
||||
set_busy(false);
|
||||
}
|
||||
|
||||
|
||||
private void on_open_attachment(Geary.Attachment attachment) {
|
||||
if (GearyApplication.instance.config.ask_open_attachment) {
|
||||
QuestionDialog ask_to_open = new QuestionDialog.with_checkbox(main_window,
|
||||
|
|
@ -1529,7 +1493,6 @@ public class GearyController {
|
|||
|
||||
// If the user clicked the toolbar button, we want to move focus back to the message list.
|
||||
main_window.conversation_list_view.grab_focus();
|
||||
set_busy(true);
|
||||
|
||||
delete_messages.begin(get_selected_folder_email_ids(), cancellable_folder, on_delete_messages_completed);
|
||||
}
|
||||
|
|
@ -1561,8 +1524,6 @@ public class GearyController {
|
|||
} catch (Error err) {
|
||||
debug("Error, unable to delete messages: %s", err.message);
|
||||
}
|
||||
|
||||
set_busy(false);
|
||||
}
|
||||
|
||||
private void on_zoom_in() {
|
||||
|
|
@ -1586,14 +1547,6 @@ public class GearyController {
|
|||
NotificationBubble.play_sound("message-sent-email");
|
||||
}
|
||||
|
||||
public void set_busy(bool is_busy) {
|
||||
busy_count += is_busy ? 1 : -1;
|
||||
if (busy_count < 0)
|
||||
busy_count = 0;
|
||||
|
||||
main_window.set_busy(busy_count > 0);
|
||||
}
|
||||
|
||||
private void on_link_selected(string link) {
|
||||
if (link.down().has_prefix(Geary.ComposedEmail.MAILTO_SCHEME)) {
|
||||
compose_mailto(link);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public class MainWindow : Gtk.Window {
|
|||
private Gtk.Paned conversations_paned = new Gtk.Paned(Gtk.Orientation.HORIZONTAL);
|
||||
|
||||
private Gtk.ScrolledWindow conversation_list_scrolled;
|
||||
private Gtk.Spinner spinner = new Gtk.Spinner();
|
||||
private MonitoredSpinner spinner = new MonitoredSpinner();
|
||||
|
||||
public MainWindow() {
|
||||
title = GearyApplication.NAME;
|
||||
|
|
@ -82,15 +82,11 @@ public class MainWindow : Gtk.Window {
|
|||
return base.configure_event(event);
|
||||
}
|
||||
|
||||
// Displays or stops displaying busy spinner.
|
||||
public void set_busy(bool is_busy) {
|
||||
if (is_busy) {
|
||||
spinner.start();
|
||||
spinner.show();
|
||||
} else {
|
||||
spinner.stop();
|
||||
spinner.hide();
|
||||
}
|
||||
/**
|
||||
* Sets the progress monitor to display in the status bar.
|
||||
*/
|
||||
public void set_progress_monitor(Geary.ProgressMonitor? monitor) {
|
||||
spinner.set_progress_monitor(monitor);
|
||||
}
|
||||
|
||||
private void create_layout() {
|
||||
|
|
|
|||
40
src/client/ui/monitored-spinner.vala
Normal file
40
src/client/ui/monitored-spinner.vala
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/* Copyright 2013 Yorba Foundation
|
||||
*
|
||||
* This software is licensed under the GNU Lesser General Public License
|
||||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adapts a progress spinner to automatically display progress of a Geary.ProgressMonitor.
|
||||
*/
|
||||
public class MonitoredSpinner : Gtk.Spinner {
|
||||
private Geary.ProgressMonitor? monitor = null;
|
||||
|
||||
public void set_progress_monitor(Geary.ProgressMonitor? monitor) {
|
||||
if (monitor != null) {
|
||||
this.monitor = monitor;
|
||||
monitor.start.connect(on_start);
|
||||
monitor.finish.connect(on_stop);
|
||||
} else {
|
||||
this.monitor = null;
|
||||
stop();
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
public override void show() {
|
||||
if (monitor != null && monitor.is_in_progress)
|
||||
base.show();
|
||||
}
|
||||
|
||||
private void on_start() {
|
||||
start();
|
||||
show();
|
||||
}
|
||||
|
||||
private void on_stop() {
|
||||
stop();
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -16,8 +16,10 @@ public class ConversationViewer : Gtk.Box {
|
|||
| Geary.Email.Field.PREVIEW;
|
||||
|
||||
private const int ATTACHMENT_PREVIEW_SIZE = 50;
|
||||
private const int SELECT_CONVERSATION_TIMEOUT_MSEC = 100;
|
||||
private const string MESSAGE_CONTAINER_ID = "message_container";
|
||||
private const string SELECTION_COUNTER_ID = "multiple_messages";
|
||||
private const string SPINNER_ID = "spinner";
|
||||
|
||||
private enum SearchState {
|
||||
// Search/find states.
|
||||
|
|
@ -38,6 +40,33 @@ public class ConversationViewer : Gtk.Box {
|
|||
COUNT;
|
||||
}
|
||||
|
||||
// Main display mode.
|
||||
private enum DisplayMode {
|
||||
NONE = 0, // Nothing is shown (ni
|
||||
CONVERSATION, // Email conversation
|
||||
MULTISELECT, // Message indicating that <> 1 conversations are selected
|
||||
LOADING, // Loading spinner
|
||||
|
||||
COUNT;
|
||||
|
||||
// Returns the CSS id associated with this mode's DIV container.
|
||||
public string get_id() {
|
||||
switch (this) {
|
||||
case CONVERSATION:
|
||||
return MESSAGE_CONTAINER_ID;
|
||||
|
||||
case MULTISELECT:
|
||||
return SELECTION_COUNTER_ID;
|
||||
|
||||
case LOADING:
|
||||
return SPINNER_ID;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fired when the user clicks a link.
|
||||
public signal void link_selected(string link);
|
||||
|
||||
|
|
@ -90,6 +119,8 @@ public class ConversationViewer : Gtk.Box {
|
|||
private ConversationFindBar conversation_find_bar;
|
||||
private Cancellable cancellable_fetch = new Cancellable();
|
||||
private Geary.State.Machine fsm;
|
||||
private DisplayMode display_mode = DisplayMode.NONE;
|
||||
private uint select_conversation_timeout_id = 0;
|
||||
|
||||
public ConversationViewer() {
|
||||
Object(orientation: Gtk.Orientation.VERTICAL, spacing: 0);
|
||||
|
|
@ -175,13 +206,12 @@ public class ConversationViewer : Gtk.Box {
|
|||
return "message_%s".printf(id.to_string());
|
||||
}
|
||||
|
||||
public void show_multiple_selected(uint selected_count) {
|
||||
private void show_multiple_selected(uint selected_count) {
|
||||
// Remove any messages and hide the message container, then show the counter.
|
||||
clear(current_folder, current_account_information);
|
||||
set_mode(DisplayMode.MULTISELECT);
|
||||
|
||||
try {
|
||||
web_view.hide_element_by_id(MESSAGE_CONTAINER_ID);
|
||||
web_view.show_element_by_id(SELECTION_COUNTER_ID);
|
||||
|
||||
// Update the counter's count.
|
||||
WebKit.DOM.HTMLElement counter =
|
||||
web_view.get_dom_document().get_element_by_id("selection_counter") as WebKit.DOM.HTMLElement;
|
||||
|
|
@ -234,6 +264,17 @@ public class ConversationViewer : Gtk.Box {
|
|||
clear(current_folder, current_folder.account.information);
|
||||
web_view.scroll_reset();
|
||||
|
||||
if (select_conversation_timeout_id != 0)
|
||||
Source.remove(select_conversation_timeout_id);
|
||||
|
||||
// If the load is taking too long, display a spinner.
|
||||
select_conversation_timeout_id = Timeout.add(SELECT_CONVERSATION_TIMEOUT_MSEC, () => {
|
||||
if (select_conversation_timeout_id != 0)
|
||||
set_mode(DisplayMode.LOADING);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
current_conversation = Geary.Collection.get_first(conversations);
|
||||
|
||||
select_conversation_async.begin(current_conversation, current_folder,
|
||||
|
|
@ -368,13 +409,8 @@ public class ConversationViewer : Gtk.Box {
|
|||
|
||||
private void add_message(Geary.Email email) {
|
||||
// Make sure the message container is showing and the multi-message counter hidden.
|
||||
try {
|
||||
web_view.show_element_by_id(MESSAGE_CONTAINER_ID);
|
||||
web_view.hide_element_by_id(SELECTION_COUNTER_ID);
|
||||
} catch (Error e) {
|
||||
debug("Error showing/hiding containers: %s", e.message);
|
||||
}
|
||||
|
||||
set_mode(DisplayMode.CONVERSATION);
|
||||
|
||||
if (messages.contains(email))
|
||||
return;
|
||||
|
||||
|
|
@ -1756,5 +1792,24 @@ public class ConversationViewer : Gtk.Box {
|
|||
|
||||
return SearchState.SEARCH_FOLDER;
|
||||
}
|
||||
|
||||
// Sets the current display mode by displaying only the corresponding DIV.
|
||||
private void set_mode(DisplayMode mode) {
|
||||
select_conversation_timeout_id = 0; // Cancel select timers.
|
||||
|
||||
display_mode = mode;
|
||||
|
||||
try {
|
||||
for(int i = DisplayMode.NONE + 1; i < DisplayMode.COUNT; i++) {
|
||||
if ((int) mode != i)
|
||||
web_view.hide_element_by_id(((DisplayMode) i).get_id());
|
||||
}
|
||||
|
||||
if (mode != DisplayMode.NONE)
|
||||
web_view.show_element_by_id(mode.get_id());
|
||||
} catch (Error e) {
|
||||
debug("Error updating counter: %s", e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ public class Geary.App.ConversationMonitor : BaseObject {
|
|||
}
|
||||
}
|
||||
|
||||
public Geary.ProgressMonitor progress_monitor { get { return operation_queue.progress_monitor; } }
|
||||
|
||||
private ConversationSet conversations = new ConversationSet();
|
||||
private Geary.Email.Field required_fields;
|
||||
private Geary.Folder.OpenFlags open_flags;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
private class Geary.App.ConversationOperationQueue : BaseObject {
|
||||
public bool is_processing { get; private set; default = false; }
|
||||
public Geary.SimpleProgressMonitor progress_monitor { get; private set; default =
|
||||
new Geary.SimpleProgressMonitor(Geary.ProgressType.ACTIVITY); }
|
||||
|
||||
private Geary.Nonblocking.Mailbox<ConversationOperation> mailbox
|
||||
= new Geary.Nonblocking.Mailbox<ConversationOperation>();
|
||||
|
|
@ -50,7 +52,13 @@ private class Geary.App.ConversationOperationQueue : BaseObject {
|
|||
if (op is TerminateOperation)
|
||||
break;
|
||||
|
||||
if (!progress_monitor.is_in_progress)
|
||||
progress_monitor.notify_start();
|
||||
|
||||
yield op.execute_async();
|
||||
|
||||
if (mailbox.size == 0)
|
||||
progress_monitor.notify_finish();
|
||||
}
|
||||
|
||||
is_processing = false;
|
||||
|
|
|
|||
|
|
@ -538,3 +538,114 @@ blockquote {
|
|||
.search_coloring *::selection {
|
||||
background-color: #00ddff;
|
||||
}
|
||||
|
||||
#spinner {
|
||||
display: none;
|
||||
margin: 100px auto;
|
||||
width: 128px;
|
||||
}
|
||||
|
||||
/*
|
||||
Spinner code from CSSload.net
|
||||
License: http://cssload.net/en/terms_of_use
|
||||
*/
|
||||
|
||||
#spinner #floatingCirclesG {
|
||||
position:relative;
|
||||
width:128px;
|
||||
height:128px;
|
||||
-webkit-transform:scale(0.6);
|
||||
transform:scale(0.6);
|
||||
}
|
||||
|
||||
#spinner .f_circleG {
|
||||
position:absolute;
|
||||
background-color:#FFFFFF;
|
||||
height:23px;
|
||||
width:23px;
|
||||
-webkit-border-radius:12px;
|
||||
-webkit-animation-name:f_fadeG;
|
||||
-webkit-animation-duration:1.04s;
|
||||
-webkit-animation-iteration-count:infinite;
|
||||
-webkit-animation-direction:linear;
|
||||
border-radius:12px;
|
||||
animation-name:f_fadeG;
|
||||
animation-duration:1.04s;
|
||||
animation-iteration-count:infinite;
|
||||
animation-direction:linear;
|
||||
}
|
||||
|
||||
#spinner #frotateG_01 {
|
||||
left:0;
|
||||
top:52px;
|
||||
-webkit-animation-delay:0.39s;
|
||||
animation-delay:0.39s;
|
||||
}
|
||||
|
||||
#spinner #frotateG_02 {
|
||||
left:15px;
|
||||
top:15px;
|
||||
-webkit-animation-delay:0.52s;
|
||||
animation-delay:0.52s;
|
||||
}
|
||||
|
||||
#spinner #frotateG_03 {
|
||||
left:52px;
|
||||
top:0;
|
||||
-webkit-animation-delay:0.65s;
|
||||
animation-delay:0.65s;
|
||||
}
|
||||
|
||||
#spinner #frotateG_04 {
|
||||
right:15px;
|
||||
top:15px;
|
||||
-webkit-animation-delay:0.78s;
|
||||
animation-delay:0.78s;
|
||||
}
|
||||
|
||||
#spinner #frotateG_05 {
|
||||
right:0;
|
||||
top:52px;
|
||||
-webkit-animation-delay:0.91s;
|
||||
animation-delay:0.91s;
|
||||
}
|
||||
|
||||
#spinner #frotateG_06 {
|
||||
right:15px;
|
||||
bottom:15px;
|
||||
-webkit-animation-delay:1.04s;
|
||||
animation-delay:1.04s;
|
||||
}
|
||||
|
||||
#spinner #frotateG_07 {
|
||||
left:52px;
|
||||
bottom:0;
|
||||
-moz-animation-delay:1.17s;
|
||||
-webkit-animation-delay:1.17s;
|
||||
-ms-animation-delay:1.17s;
|
||||
-o-animation-delay:1.17s;
|
||||
animation-delay:1.17s;
|
||||
}
|
||||
|
||||
#spinner #frotateG_08 {
|
||||
left:15px;
|
||||
bottom:15px;
|
||||
-moz-animation-delay:1.3s;
|
||||
-webkit-animation-delay:1.3s;
|
||||
-ms-animation-delay:1.3s;
|
||||
-o-animation-delay:1.3s;
|
||||
animation-delay:1.3s;
|
||||
}
|
||||
|
||||
@-webkit-keyframes f_fadeG {
|
||||
0% {
|
||||
background-color:#000000
|
||||
}
|
||||
|
||||
100% {
|
||||
background-color:#FFFFFF
|
||||
}
|
||||
}
|
||||
|
||||
/* /Spinner */
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,24 @@
|
|||
</div>
|
||||
<div id="link_warning_template" class="link_warning">
|
||||
<img class="close_link_warning button" />
|
||||
</div>
|
||||
<div id="spinner">
|
||||
<!--
|
||||
Spinner code from CSSLoad.net
|
||||
License: http://cssload.net/en/terms_of_use
|
||||
-->
|
||||
<div id="floatingCirclesG">
|
||||
<div class="f_circleG" id="frotateG_01"></div>
|
||||
<div class="f_circleG" id="frotateG_02"></div>
|
||||
<div class="f_circleG" id="frotateG_03"></div>
|
||||
<div class="f_circleG" id="frotateG_04"></div>
|
||||
<div class="f_circleG" id="frotateG_05"></div>
|
||||
<div class="f_circleG" id="frotateG_06"></div>
|
||||
<div class="f_circleG" id="frotateG_07"></div>
|
||||
<div class="f_circleG" id="frotateG_08"></div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue