// The base command class defines the common interface for all // concrete commands. abstractclassCommand is protected field app: Application protected field editor: Editor protected field backup: text
// Make a backup of the editor's state. method saveBackup()is backup= editor.text
// Restore the editor's state. method undo() is editor.text = backup
// The execution method is declared abstract to force all // concrete commands to provide their own implementations. // The method must return true or false depending on whether // the command changes the editor's state. abstract method execute()
// The concrete commands go here. classCopyCommandextendsCommand is // The copy command isn't saved to the history since it // doesn't change the editor's state. method execute() is app.clipboard = editor.getSelection() returnfalse
classCutCommandextendsCommand is // The cut command does change the editor's state, therefore // it must be saved to the history. And it'll be saved as // long as the method returns true. method execute() is saveBackup() app.clipboard = editor.getSelection() editor.deleteSelection() returntrue
classPasteCommandextendsCommand is method execute() is saveBackup() editor.replaceSelection(app.clipboard) returntrue
// The undo operation is also a command. classUndoCommandextendsCommand is method execute() is app.undo() returnfalse
// The global command history is just a stack. classCommandHistory is private field history: array of Command
// Last in... method push(c: Command) is // Push the command to the end of the history array.
// ...first out method pop():Command is // Get the most recent command from the history.
// The editor class has actual text editing operations. It plays // the role of a receiver: all commands end up delegating // execution to the editor's methods. classEditor is field text: string
method getSelection() is // Return selected text.
method deleteSelection() is // Delete selected text.
method replaceSelection(text) is // Insert the clipboard's contents at the current // position.
// The application class sets up object relations. It acts as a // sender: when something needs to be done, it creates a command // object and executes it. classApplication is field clipboard: string field editors: array of Editors field activeEditor: Editor field history: CommandHistory
// The code which assigns commands to UI objects may look // like this. method createUI() is // ... copy = function() { executeCommand( newCopyCommand(this, activeEditor)) } copyButton.setCommand(copy) shortcuts.onKeyPress("Ctrl+C", copy)
// Execute a command and check whether it has to be added to // the history. method executeCommand(command) is if(command.execute()) history.push(command)
// Take the most recent command from the history and run its // undo method. Note that we don't know the class of that // command. But we don't have to, since the command knows // how to undo its own action. method undo()is command= history.pop() if (command != null) command.undo()
defexecute(self) -> None: print(f"SimpleCommand: See, I can do simple things like printing" f"({self._payload})")
classComplexCommand(Command): """ However, some commands can delegate more complex operations to other objects, called "receivers." """
def__init__(self, receiver: Receiver, a: str, b: str) -> None: """ Complex commands can accept one or several receiver objects along with any context data via the constructor. """
self._receiver = receiver self._a = a self._b = b
defexecute(self) -> None: """ Commands can delegate to any methods of a receiver. """
print("ComplexCommand: Complex stuff should be done by a receiver object", end="") self._receiver.do_something(self._a) self._receiver.do_something_else(self._b)
classReceiver: """ The Receiver classes contain some important business logic. They know how to perform all kinds of operations, associated with carrying out a request. In fact, any class may serve as a Receiver. """
defdo_something(self, a: str) -> None: print(f"\nReceiver: Working on ({a}.)", end="")
defdo_something_else(self, b: str) -> None: print(f"\nReceiver: Also working on ({b}.)", end="")
classInvoker: """ The Invoker is associated with one or several commands. It sends a request to the command. """
defdo_something_important(self) -> None: """ The Invoker does not depend on concrete command or receiver classes. The Invoker passes a request to a receiver indirectly, by executing a command. """
print("Invoker: Does anybody want something done before I begin?") ifisinstance(self._on_start, Command): self._on_start.execute()
Invoker: Does anybody want something done before I begin? SimpleCommand: See, I can do simple things like printing (Say Hi!) Invoker: ...doing something really important... Invoker: Does anybody want something done after I finish? ComplexCommand: Complex stuff should be done by a receiver object Receiver: Working on (Send email.) Receiver: Also working on (Save report.)
use cursive::{ traits::Nameable, views::{Dialog, EditView}, Cursive, };
use command::{Command, CopyCommand, CutCommand, PasteCommand};
/// An application context to be passed into visual component callbacks. /// It contains a clipboard and a history of commands to be undone. #[derive(Default)] structAppContext { clipboard: String, history: Vec<Box<dyn Command>>, }
/// Executes a command and then pushes it to a history array. fnexecute(app: &mut Cursive, mut command: implCommand + 'static) { if command.execute(app) { app.with_user_data(|context: &mut AppContext| { context.history.push(Box::new(command)); }); } }
/// Pops the last command and executes an undo action. fnundo(app: &mut Cursive) { letmut context = app.take_user_data::<AppContext>().unwrap(); ifletSome(mut command) = context.history.pop() { command.undo(app) } app.set_user_data(context); }