[geary/mjog/account-command-stacks: 34/77] Implement an application-level command stack
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/mjog/account-command-stacks: 34/77] Implement an application-level command stack
- Date: Tue, 5 Nov 2019 00:35:57 +0000 (UTC)
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]