UNPKG

@itwin/core-markup

Version:
177 lines 6.76 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module MarkupTools */ import { assert } from "@itwin/core-bentley"; import { MarkupApp } from "./Markup"; /* @internal */ class UndoAction { cmdName; cmdId = 0; constructor(cmdName) { this.cmdName = cmdName; } } /** created when a new element is added to the markup * @internal */ class AddAction extends UndoAction { _elem; _parent; _index; constructor(cmdName, _elem) { super(cmdName); this._elem = _elem; this._parent = _elem.parent(); assert(this._parent !== undefined); this._index = _elem.position(); } reinstate() { this._parent.add(this._elem, this._index); } reverse() { MarkupApp.markup.selected.drop(this._elem); this._elem.remove(); } } /** created when an existing element is deleted from the markup * @internal */ class DeleteAction extends UndoAction { _elem; _parent; _index; constructor(cmdName, _elem) { super(cmdName); this._elem = _elem; this._parent = _elem.parent(); assert(this._parent !== undefined); this._index = _elem.position(); } reverse() { this._parent.add(this._elem, this._index); } reinstate() { MarkupApp.markup.selected.drop(this._elem); this._elem.remove(); } } /** created when an existing element's position is moved in the display order. This can also include re-parenting * @internal */ class RepositionAction extends UndoAction { _elem; _oldIndex; _oldParent; _newParent; _newIndex; constructor(cmdName, _elem, _oldIndex, _oldParent) { super(cmdName); this._elem = _elem; this._oldIndex = _oldIndex; this._oldParent = _oldParent; this._newParent = _elem.parent(); assert(this._newParent !== undefined); this._newIndex = _elem.position(); } reinstate() { this._newParent.add(this._elem, this._newIndex); } reverse() { this._oldParent.add(this._elem, this._oldIndex); if (this._elem.inSelection) MarkupApp.markup.selected.drop(this._elem); } } /** created when an existing element's properties are modified. * @internal */ class ModifyAction extends UndoAction { _newElem; _oldElement; constructor(cmdName, _newElem, _oldElement) { super(cmdName); this._newElem = _newElem; this._oldElement = _oldElement; assert(_newElem !== undefined && _oldElement !== undefined); MarkupApp.markup.selected.replace(_oldElement, _newElem); } reinstate() { this._oldElement.replace(this._newElem); MarkupApp.markup.selected.replace(this._oldElement, this._newElem); } reverse() { this._newElem.replace(this._oldElement); MarkupApp.markup.selected.replace(this._newElem, this._oldElement); } } /** Stores the sequence of operations performed on a Markup. Facilitates undo/redo of the operations. * @public */ export class UndoManager { _currentCmd = 0; _grouped = 0; _stack = []; _currentPos = 0; _cmdName = ""; addAction(action) { this._stack.length = this._currentPos; action.cmdId = this._currentCmd; this._stack.push(action); this._currentPos = this.size; } /** @internal */ get size() { return this._stack.length; } startCommand() { if (0 === this._grouped) ++this._currentCmd; } startGroup() { this.startCommand(); ++this._grouped; } endGroup() { --this._grouped; } /** Perform a series of changes to markup elements that should all be reversed as a single operation. * @param fn the function that performs the changes to the elements. It must call the onXXX methods of this class to store * the operations in the undo buffer. * @note all of the onXXX methods of this class should *only* be called from within the callback function of this method. */ performOperation(cmdName, fn) { this._cmdName = cmdName; this.startGroup(); fn(); this.endGroup(); } /** call this from within a [[performOperation]] function *after* an element has been added to a markup */ onAdded(elem) { this.addAction(new AddAction(this._cmdName, elem)); } /** call this from within a [[performOperation]] function *before* an element is about to be deleted from a markup */ onDelete(elem) { this.addAction(new DeleteAction(this._cmdName, elem)); } /** call this from within a [[performOperation]] function *after* an element has been moved in display order in a markup */ onRepositioned(elem, oldIndex, oldParent) { this.addAction(new RepositionAction(this._cmdName, elem, oldIndex, oldParent)); } /** call this from within a [[performOperation]] function *after* an element has been modified in a markup */ onModified(newElem, oldElem) { this.addAction(new ModifyAction(this._cmdName, newElem, oldElem)); } /** determine whether there are any un-reversed operations */ get undoPossible() { return this._currentPos > 0; } /** determine whether there are any reversed operations */ get redoPossible() { return this._currentPos < this.size; } /** the name of the operation that can be undone (or undefined) */ get undoString() { return this.undoPossible ? this._stack[this._currentPos - 1].cmdName : undefined; } /** the name of the operation that can be redone (or undefined) */ get redoString() { return this.redoPossible ? this._stack[this._currentPos].cmdName : undefined; } /** reverse the most recent operation, if any */ doUndo() { if (this._currentPos === 0) return; // no operations have been performed const cmdId = this._stack[this._currentPos - 1].cmdId; while (this._currentPos > 0 && cmdId === this._stack[this._currentPos - 1].cmdId) this._stack[--this._currentPos].reverse(); } /** reinstate the most recently reversed operation, if any */ doRedo() { if (this._currentPos === this.size) return; // no operations have been reversed. const cmdId = this._stack[this._currentPos].cmdId; while (this._currentPos < this.size && cmdId === this._stack[this._currentPos].cmdId) this._stack[this._currentPos++].reinstate(); } } //# sourceMappingURL=Undo.js.map