0 people like it.
Like the snippet!
Undoable Commands
A simple implementation of an Undoable Command, with a Document to hold a stack of actions.
2 examples of UndoableCommand are given - 1 allows property changes to be remembered, and another which allows the user to execute an action with a corresponding undo.
Further examples could include CompositeUndoableCommands where the command is itself a list of commands.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
|
open System.Collections.Generic
// Base class for all UndoableCommand Objects
[<AbstractClass>]
type UndoableCommand(description:string) =
member this.Description = description
abstract Execute : unit->unit
abstract Undo : unit->unit
// Class that can handle property changes. Note we use reference cells to directly
// manipulate the underlying values
type PropertyChangedUndoableCommand<'a>(description, fieldRef, newValue:'a) =
inherit UndoableCommand(description)
let oldValue = !fieldRef
override this.Execute() = fieldRef:=newValue
override this.Undo() = fieldRef:=oldValue
// Class that executes actions to "do" and "undo"
// Obviously undo should actually undo what "do" does, but we cant enforce it
type DelegateUndoableCommand(description, doAction, undoAction) =
inherit UndoableCommand(description)
override this.Execute() = doAction()
override this.Undo() = undoAction()
//Document contains an example undo/redo stack
type Document() =
let undoStack = Stack()
let redoStack = Stack()
let execute (command : UndoableCommand) =
redoStack.Clear() //as we are executing a command any existing redo is invalidated
undoStack.Push(command)
command.Execute()
let undo() =
if undoStack.Count > 0 then
let command = undoStack.Pop()
redoStack.Push(command)
command.Undo()
let redo() =
if redoStack.Count> 0 then
let command = redoStack.Pop()
undoStack.Push(command)
command.Execute()
member this.ExecuteCommand command = execute command
member this.Undo() = undo()
member this.Redo() = redo()
member this.CanUndo = undoStack.Count > 0
member this.CanRedo = redoStack.Count > 0
//Example implementation
type SomeObject(document:Document) =
let undoableProperty = ref 50 //Because of the command we cant use mutable
member this.UndoableProperty with get() = !undoableProperty
and set(value) =
//instead of directly setting the property we create a command and
//execute it on the document.
let command = PropertyChangedUndoableCommand("Changed", undoableProperty, value)
document.ExecuteCommand(command)
let doc = Document() //Document that will hold our doings and undoings
let someObject = SomeObject(doc)
printf "Initial Value %d\n" someObject.UndoableProperty //50
someObject.UndoableProperty <- 100
printf "Updated Value %d\n" someObject.UndoableProperty //100
someObject.UndoableProperty <- 1000
printf "Updated Value %d\n" someObject.UndoableProperty //1000
doc.Undo()
printf "Undone Value %d\n" someObject.UndoableProperty // 100
doc.Undo()
printf "Undone Value %d\n" someObject.UndoableProperty // 50
doc.Undo()
printf "Undone Value %d\n" someObject.UndoableProperty // 50
doc.Undo()
printf "Undone Value %d\n" someObject.UndoableProperty // 50
doc.Redo()
printf "Redone Value %d\n" someObject.UndoableProperty // 100
doc.Redo()
printf "Redone Value %d\n" someObject.UndoableProperty // 1000
doc.Redo()
printf "Redone Value %d\n" someObject.UndoableProperty // 1000
System.Console.ReadLine()|>ignore
|
namespace System
namespace System.Collections
namespace System.Collections.Generic
Multiple items
type AbstractClassAttribute =
inherit Attribute
new : unit -> AbstractClassAttribute
Full name: Microsoft.FSharp.Core.AbstractClassAttribute
--------------------
new : unit -> AbstractClassAttribute
Multiple items
type UndoableCommand =
new : description:string -> UndoableCommand
abstract member Execute : unit -> unit
abstract member Undo : unit -> unit
member Description : string
Full name: Script.UndoableCommand
--------------------
new : description:string -> UndoableCommand
val description : string
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
val this : UndoableCommand
member UndoableCommand.Description : string
Full name: Script.UndoableCommand.Description
abstract member UndoableCommand.Execute : unit -> unit
Full name: Script.UndoableCommand.Execute
type unit = Unit
Full name: Microsoft.FSharp.Core.unit
abstract member UndoableCommand.Undo : unit -> unit
Full name: Script.UndoableCommand.Undo
Multiple items
type PropertyChangedUndoableCommand<'a> =
inherit UndoableCommand
new : description:string * fieldRef:'a ref * newValue:'a -> PropertyChangedUndoableCommand<'a>
override Execute : unit -> unit
override Undo : unit -> unit
Full name: Script.PropertyChangedUndoableCommand<_>
--------------------
new : description:string * fieldRef:'a ref * newValue:'a -> PropertyChangedUndoableCommand<'a>
val fieldRef : 'a ref
val newValue : 'a
val oldValue : 'a
val this : PropertyChangedUndoableCommand<'a>
override PropertyChangedUndoableCommand.Execute : unit -> unit
Full name: Script.PropertyChangedUndoableCommand`1.Execute
override PropertyChangedUndoableCommand.Undo : unit -> unit
Full name: Script.PropertyChangedUndoableCommand`1.Undo
Multiple items
type DelegateUndoableCommand =
inherit UndoableCommand
new : description:string * doAction:(unit -> unit) * undoAction:(unit -> unit) -> DelegateUndoableCommand
override Execute : unit -> unit
override Undo : unit -> unit
Full name: Script.DelegateUndoableCommand
--------------------
new : description:string * doAction:(unit -> unit) * undoAction:(unit -> unit) -> DelegateUndoableCommand
val doAction : (unit -> unit)
val undoAction : (unit -> unit)
val this : DelegateUndoableCommand
override DelegateUndoableCommand.Execute : unit -> unit
Full name: Script.DelegateUndoableCommand.Execute
override DelegateUndoableCommand.Undo : unit -> unit
Full name: Script.DelegateUndoableCommand.Undo
Multiple items
type Document =
new : unit -> Document
member ExecuteCommand : command:UndoableCommand -> unit
member Redo : unit -> unit
member Undo : unit -> unit
member CanRedo : bool
member CanUndo : bool
Full name: Script.Document
--------------------
new : unit -> Document
val undoStack : Stack<UndoableCommand>
Multiple items
type Stack<'T> =
new : unit -> Stack<'T> + 2 overloads
member Clear : unit -> unit
member Contains : item:'T -> bool
member CopyTo : array:'T[] * arrayIndex:int -> unit
member Count : int
member GetEnumerator : unit -> Enumerator<'T>
member Peek : unit -> 'T
member Pop : unit -> 'T
member Push : item:'T -> unit
member ToArray : unit -> 'T[]
...
nested type Enumerator
Full name: System.Collections.Generic.Stack<_>
--------------------
Stack() : unit
Stack(capacity: int) : unit
Stack(collection: IEnumerable<'T>) : unit
val redoStack : Stack<UndoableCommand>
val execute : (UndoableCommand -> unit)
val command : UndoableCommand
Stack.Clear() : unit
Stack.Push(item: UndoableCommand) : unit
abstract member UndoableCommand.Execute : unit -> unit
val undo : (unit -> unit)
property Stack.Count: int
Stack.Pop() : UndoableCommand
abstract member UndoableCommand.Undo : unit -> unit
val redo : (unit -> unit)
val this : Document
member Document.ExecuteCommand : command:UndoableCommand -> unit
Full name: Script.Document.ExecuteCommand
member Document.Undo : unit -> unit
Full name: Script.Document.Undo
member Document.Redo : unit -> unit
Full name: Script.Document.Redo
member Document.CanUndo : bool
Full name: Script.Document.CanUndo
member Document.CanRedo : bool
Full name: Script.Document.CanRedo
Multiple items
type SomeObject =
new : document:Document -> SomeObject
member UndoableProperty : int
member UndoableProperty : int with set
Full name: Script.SomeObject
--------------------
new : document:Document -> SomeObject
val document : Document
val undoableProperty : int ref
Multiple items
val ref : value:'T -> 'T ref
Full name: Microsoft.FSharp.Core.Operators.ref
--------------------
type 'T ref = Ref<'T>
Full name: Microsoft.FSharp.Core.ref<_>
val this : SomeObject
member SomeObject.UndoableProperty : int with set
Full name: Script.SomeObject.UndoableProperty
val set : elements:seq<'T> -> Set<'T> (requires comparison)
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.set
val value : int
val command : PropertyChangedUndoableCommand<int>
member Document.ExecuteCommand : command:UndoableCommand -> unit
val doc : Document
Full name: Script.doc
val someObject : SomeObject
Full name: Script.someObject
val printf : format:Printf.TextWriterFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printf
property SomeObject.UndoableProperty: int
member Document.Undo : unit -> unit
member Document.Redo : unit -> unit
type Console =
static member BackgroundColor : ConsoleColor with get, set
static member Beep : unit -> unit + 1 overload
static member BufferHeight : int with get, set
static member BufferWidth : int with get, set
static member CapsLock : bool
static member Clear : unit -> unit
static member CursorLeft : int with get, set
static member CursorSize : int with get, set
static member CursorTop : int with get, set
static member CursorVisible : bool with get, set
...
Full name: System.Console
System.Console.ReadLine() : string
val ignore : value:'T -> unit
Full name: Microsoft.FSharp.Core.Operators.ignore
More information