[geary/mjog/account-command-stacks: 34/77] Implement an application-level command stack



commit 88eb8f50402c2fd8d0252c67c1587ce7c51b9f65
Author: Michael Gratton <mike vee net>
Date:   Sat Oct 5 10:49:16 2019 +1000

    Implement an application-level command stack
    
    This will provide the basis for handling undo/redo in the main window.

 src/client/application/application-controller.vala | 36 ++++++++++++++++++-
 src/client/components/main-window.vala             | 42 +++++++++++++++++++++-
 2 files changed, 76 insertions(+), 2 deletions(-)
---
diff --git a/src/client/application/application-controller.vala 
b/src/client/application/application-controller.vala
index 2b070eeb..d5b167c2 100644
--- a/src/client/application/application-controller.vala
+++ b/src/client/application/application-controller.vala
@@ -127,6 +127,8 @@ public class Application.Controller : Geary.BaseObject {
     // Null if no folder ever selected
     private Geary.Account? current_account = null;
 
+    private Application.CommandStack commands { get; protected set; }
+
     private Cancellable cancellable_folder = new Cancellable();
     private Cancellable cancellable_search = new Cancellable();
     private Cancellable cancellable_open_account = new Cancellable();
@@ -207,8 +209,10 @@ public class Application.Controller : Geary.BaseObject {
         );
         this.plugin_manager.load();
 
+        this.commands = new CommandStack();
+
         // Create the main window (must be done after creating actions.)
-        main_window = new MainWindow(this.application);
+        main_window = new MainWindow(this.application, this.commands);
         main_window.retry_service_problem.connect(on_retry_service_problem);
         main_window.notify["has-toplevel-focus"].connect(on_has_toplevel_focus);
 
@@ -292,6 +296,36 @@ public class Application.Controller : Geary.BaseObject {
         this.expunge_accounts.begin();
     }
 
+    /** Un-does the last executed application command, if any. */
+    public async void undo() {
+        this.commands.undo.begin(
+            this.open_cancellable,
+            (obj, res) => {
+                try {
+                    this.commands.undo.end(res);
+                } catch (GLib.Error err) {
+                    // XXX extract account info somehow
+                    report_problem(new Geary.ProblemReport(err));
+                }
+            }
+        );
+    }
+
+    /** Re-does the last undone application command, if any. */
+    public async void redo() {
+        this.commands.redo.begin(
+            this.open_cancellable,
+            (obj, res) => {
+                try {
+                    this.commands.redo.end(res);
+                } catch (GLib.Error err) {
+                    // XXX extract account info somehow
+                    report_problem(new Geary.ProblemReport(err));
+                }
+            }
+        );
+    }
+
     /** Closes all accounts and windows, releasing held resources. */
     public async void close_async() {
         // Cancel internal processes early so they don't block
diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala
index 2d034cbb..82138736 100644
--- a/src/client/components/main-window.vala
+++ b/src/client/components/main-window.vala
@@ -205,6 +205,8 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
     private Geary.TimeoutManager update_ui_timeout;
     private int64 update_ui_last = 0;
 
+    private Application.CommandStack commands { get; protected set; }
+
 
     [GtkChild]
     private Gtk.Box main_layout;
@@ -250,7 +252,8 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
     public signal void on_shift_key(bool pressed);
 
 
-    public MainWindow(GearyApplication application) {
+    public MainWindow(GearyApplication application,
+                      Application.CommandStack commands) {
         Object(
             application: application,
             show_menubar: false
@@ -265,6 +268,12 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
         setup_layout(application.config);
         on_change_orientation();
 
+        this.commands = commands;
+        this.commands.executed.connect(on_command_execute);
+        this.commands.undone.connect(on_command_undo);
+        this.commands.redone.connect(on_command_execute);
+        update_command_actions();
+
         this.application.engine.account_available.connect(on_account_available);
         this.application.engine.account_unavailable.connect(on_account_unavailable);
 
@@ -674,6 +683,15 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
         update_headerbar();
     }
 
+    private void update_command_actions() {
+        get_action(GearyApplication.ACTION_UNDO).set_enabled(
+            this.commands.can_undo
+        );
+        get_action(GearyApplication.ACTION_REDO).set_enabled(
+            this.commands.can_redo
+        );
+    }
+
     private void update_ui() {
         // Only update if we haven't done so within the last while
         int64 now = GLib.get_monotonic_time() / (1000 * 1000);
@@ -1030,12 +1048,34 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
         update_ui();
     }
 
+    private void on_command_execute(Application.Command command) {
+        if (command.executed_label != null) {
+            Components.InAppNotification ian =
+                new Components.InAppNotification(command.executed_label);
+            ian.set_button(_("Undo"), "win." + GearyApplication.ACTION_UNDO);
+            add_notification(ian);
+        }
+        update_command_actions();
+    }
+
+    private void on_command_undo(Application.Command command) {
+        if (command.undone_label != null) {
+            Components.InAppNotification ian =
+                new Components.InAppNotification(command.undone_label);
+            ian.set_button(_("Redo"), "win." + GearyApplication.ACTION_REDO);
+            add_notification(ian);
+        }
+        update_command_actions();
+    }
+
     // Action callbacks
 
     private void on_undo() {
+        this.application.controller.undo.begin();
     }
 
     private void on_redo() {
+        this.application.controller.redo.begin();
     }
 
     private void on_close() {


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