Class: SC.UndoManager
Extends
SC.Object.
This is a simple undo manager. It manages groups of actions which return something to an earlier state. It's your responsibility to make sure that these functions successfully undo the action, and register an undo action of their own (allowing redo).
Using SC.UndoManager
You should create one SC.UndoManager
instance for each thing you want to
allow undo on. For example, if a controller manages a single record, but
you have two fields that should each have their own undo stack, you should
create two separate managers.
Register undo functions via the registerUndoAction
, which takes a target,
action, context and optional human-readable action name. Trigger actions by
calling the undo
and redo
methods. Actions will be called with context
as their only argument.
Optionally, you can group undo actions; groups of actions are triggered together.
Simple Example: A single value
This example attaches an undo manager to a controller and registers an undo
function each time the value of value
changes. It also exposes methods to
trigger undos and redos, triggered via buttons in the included stub view class.
// Controller:
MyApp.myController = SC.ObjectController.create({
// Content, with `value`.
content: SC.Object.create({ value: 'Hello, World.' }),
// Undo manager.
valueUndoManager: SC.UndoManager.create(),
// Undo action.
_valueUndoAction(val) {
// This call will trigger the controller's `value` observer, triggering the registration
// of another undo; the UndoManager will automatically and correctly interpret this as
// the registration of a redo method.
this.set('value', val);
},
// Value observer; tracks `value` and registers undos.
valueDidChange: function() {
// Get the values.
var value = this.get('value'),
previousValue = this._previousValue,
that = this;
// Update previous value.
this._previousValue = value;
// GATEKEEP: If the current value is the same as the previous value, there's nothing to do.
if (previousValue === value) return;
// GATEKEEP: If there is no previous value, it's probably our initial spinup. We don't want
// to register an undo-back-to-undefined method, so we should return. (Your situation may be
// different.)
if (SC.none(previousValue)) return;
// Otherwise, register an undo function. (previousValue is accessed via the closure.)
this.undoManager.registerUndoAction(this, this._valueUndoAction, previousValue);
}.observes('value')
});
// Stub view:
MyApp.UndoableValueView = SC.View.extend({
childViews: ['labelView', 'undoButtonView', 'redoButtonView'],
labelView: SC.LabelView.extend({
layout: { height: 24 },
isEdiable: YES,
valueBinding: 'MyApp.myController.value'
}),
undoButtonView: SC.ButtonView.extend({
layout: { height: 24, width: 60, bottom: 0 },
title: 'Undo',
isEnabledBinding: SC.Binding.oneWay('MyApp.myController.valueUndoManager.canUndo'),
target: 'MyApp.myController.valueUndoManager',
action: 'undo'
}),
redoButtonView: SC.ButtonView.extend({
layout: { height: 24, width: 60, bottom: 0, right: 0 },
title: 'Redo',
isEnabledBinding: SC.Binding.oneWay('MyApp.myController.valueUndoManager.canRedo'),
target: 'MyApp.myController.valueUndoManager',
action: 'redo'
})
});
Advanced: Grouping undos
Undo events registered by registerUndoAction
will undo or redo one at a time. If you wish,
you can group undo events into groups (for example, if you wish to group all undos which happen
within a short duration of each other). Groups are fired all at once when undo
or redo
is
called.
To start a new undo group, call beginUndoGroup
; to register undo functions to the currently-
open group, call registerGroupedUndoAction
; finally, to mark the end of a grouped set of undo
functions, call endUndoGroup
. In most cases, you will not need to call beginUndoGroup
and
endUndoGroup
: if you call registerUndoAction
, any open group will be closed, and a new group
will be created and left open; calling registerGroupedUndoAction
will simply add to the
currently-open group, creating a new one if necessary. This means that in practice, you can call
registerUndoAction
to close previous groups and begin a new one, and registerGroupedUndoAction
to add to an existing group.
If undo
is called while an undo group is open, UndoManager will simply close the group for
you before executing it. This allows you to safely leave groups open pending possible additional
undo actions.
Defined in: undo_manager.js
Field Summary
- canRedo
- canUndo
- isRedoing
- isUndoing
- maxStackLength
- redoActionName
- redoActionTimestamp
- undoActionName
- undoActionTimestamp
- Fields borrowed from SC.Object:
- concatenatedProperties, isDestroyed, isObject, nextProperty, object, property, target, toInvalidate
- Fields borrowed from SC.Observable:
- isObservable
Instance Methods
- beginUndoGroup(name)
- endUndoGroup()
- redo()
- registerGroupedUndoAction(target, action, context, name)
- registerUndoAction(target, action, context, name)
- reset()
- setActionName(name)
- undo()
Field Detail
True if there is an redo action on the stack. Use to validate your menu item or enable your button.
- Default value:
- NO
True if there is an undo action on the stack. Use to validate your menu item or enable your button.
- Default value:
- NO
- Default value:
- NO
- Default value:
- NO
The maximum number of undo groups the receiver holds. The undo stack is unlimited by default.
- Default value:
- 0
If name arguments are passed into registerUndoAction
or related methods, then this property
will expose the last redo action's name. You can use this to show the user what type of
action will be redone (for example "Redo typing" or "Redo delete").
- Default value:
- null
- Default value:
- null
If name arguments are passed into registerUndoAction
or related methods, then this property
will expose the last undo action's name. You can use this to show the user what type of action
will be undone (for example "typing" or "delete").
- Default value:
- null
- Default value:
- null
Instance Method Detail
Begins a new undo group.
Whenever you start an action that you expect to need to bundle under a single undo action in the menu, you should begin an undo group. This way any undo actions registered by other parts of the application will be automatically bundled into this one action.
When you are finished performing the action, balance this with a call to
endUndoGroup()
. (You can call undo
or redo
with an open group; the group
will simply be closed and processed as normal.)
- Parameters:
- name String
Ends a group of undo functions. All functions in an undo group will be undone or redone
together when undo
or redo
is called.
- See:
- beginUndoGroup()
- Returns:
- Boolean
- YES if succeeded, NO otherwise.
Registers an undo action to the current group. If no group is open, opens a new one.
Registers an undo action. If called while an undo is in progress (i.e. from your undo method, or from observers which it triggers), registers a redo action instead.
- Parameters:
- name String
- Returns:
- Boolean
- YES if succeeded, NO otherwise.