Отмена и повтор действий в визуальном редакторе пользовательского интерфейса — это незаменимые функции, которые помогают избежать ошибок при редактировании и делают использование редактора более удобным.
Если вы случайно переместили элемент или установили неправильное свойство в своём проекте, вы можете использовать сочетание клавиш Ctrl+Z для отмены последнего действия. Если после отмены вы хотите быстро вернуться к предыдущему состоянию, используйте сочетание клавиш Ctrl+R для повтора отменённого действия.
Исходный код проекта можно найти по ссылке renderUndo.
Каждый экземпляр сцены создаёт объект UndoObj, который представляет собой оболочку над списком UndoList. Разница между ними заключается в том, что обычно редактор реагирует на изменения узлов при отмене и повторе действий.
"ui:scene_change"(event, message) {
let runScene = this.$.scene.getRunScene();
if(!runScene._undo)
runScene._undo = new UndoObj();
}
Список UndoList хранит историю изменений сцены. Его конструктор выглядит следующим образом:
class UndoList extends EventEmitter {
constructor (type) {
super()
//истина, если изменение вызывает событие
this._silent = false
//разделяется на локальный и глобальный типы, локальное событие изменения только уведомляет локально, глобальное обычно уведомляет весь редактор
this._type = type
//текущая группа команд
this._curGroup = new CommandGroup()
//список групп операций
this._groups = []
//записывает текущую позицию информации
this._position = -1
//позиция сохранения при последнем сохранении
this._savePosition = -1
}
}
Когда происходит новое действие, например, изменение положения узла, вызывается функция:
add (cmd) {
this._clearRedo()
if (this._curGroup.isCanCombine(cmd)) {
this._curGroup.combineCommand(cmd)
} else {
this.commit()
this._curGroup.add(cmd)
}
this._changed('add-command')
}
Поскольку вся система отмены и повтора является линейной, при появлении нового действия предыдущая операция повтора становится бессмысленной. В этом случае сначала очищается список повторов, то есть список команд после текущей позиции. Затем мы проверяем, можно ли объединить команду с текущей группой. Если возможно, мы пытаемся объединить команды.
//Функция CommandGroup
isCanCombine (other) {
if (this._commands.length == 0) {
return true
}
for ( let i = 0; i < this._commands.length; ++i) {
if (this._commands[i].isCanCombine(other)) {
return true
}
}
if (this._time && Math.abs(this._time - other.info.time) < 1000) {
return true
}
return false
}
//функция Command
isCanCombine (other) {
if (!this.info || !other.info) {
return false
}
if (this.info.op != other.info.op) {
return false
}
if (this.info.uuid != other.info.uuid) {
return false
}
if (this.info.op == 'prop' && (this.info.prop != other.info.prop)) {
return false
}
if (Math.abs(this.info.time - other.info.time) >= 1000) {
return false
}
return true
}
В CommandGroup мы просматриваем все команды и определяем, можно ли их объединить или последняя команда была создана недавно. Мы считаем, что команды можно объединить, если они соответствуют определённым условиям. Если объединение возможно, команды объединяются и значения свойств обновляются.
Почему такая конструкция?
Потому что вся система отмены предполагает, что она ничего не знает о других системах, кроме ограниченного интерфейса Add. Другие системы не предоставляют никакой другой информации об интерфейсе. Ниже приведены два примера:
- При редактировании в редакторе перемещение узла вызывает событие mousemove каждые 350 мс. В этот момент свойства узла часто меняются, но мы рассматриваем это как одно действие и ожидаем, что после отмены перемещения узел должен вернуться в исходное состояние, а не в промежуточное. В таких случаях мы объединяем операции с одинаковыми свойствами в течение определённого времени.
- Когда мы перемещаем или изменяем свойства нескольких узлов одновременно в редакторе, ожидается, что отмена вернёт все узлы к их состоянию до модификации. Поэтому мы объединяем все операции в одну группу.
Если объединение невозможно, создаётся новая группа команд, и информация о позиции обновляется.
Отмена (Undo)
undo () {
// проверяем, можем ли мы отменить текущую группу
if (this._curGroup.canCommit()) {
this._curGroup.undo()
this._changed('undo-cache')
this._groups.push(this._curGroup)
this._curGroup = new CommandGroup()
return true
}
// проверка возможности отмены
if (this._position < 0) {
return false
}
пусть group = this._groups[this._position]
group.undo()
this._position--
this._changed('undo')
вернуть истину
}
При отмене возможны следующие ситуации:
//Команда отмены
undo () {
пусть node = cocosGetItemByUUID(this.info.scene, this.info.uuid)
если (this.info.op == 'prop') {
если (!node) {
вернуть ложь
}
если (this.info.doPropChange) {
this.info.doPropChange(узел, this.info.prop, this.info.oldValue)
} иначе {
NodePropChange(узел, this.info.prop, this.info.oldValue)
}
вернуть истину
}
console.warn('Пожалуйста, реализуйте функцию отмены в вашей команде')
}
Каждая команда списка сначала пытается получить узел, а затем устанавливает новое и старое значения в соответствии со свойствами узла.
Повтор (Redo)
redo () {
// проверить возможность повтора
если (это._позиция >= это._группы.длина - 1) {
вернуть ложь
}
это._позиция++
пусть группа = это._групп[это._позиция]
группа.redo()
это._изменено('повторить')
Сначала проверяется, возможно ли повторить действие. Если да, позиция увеличивается, берётся соответствующая группа из списка групп и выполняется операция повтора.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )