Я знаю два основных подразделения типов отмены
Для текстовых редакторов создание состояния таким способом не требует слишком больших вычислительных ресурсов, но для таких программ, как Adobe Photoshop, это может быть слишком ресурсоемким или просто невозможным.Например, для действия Blur вы укажете действие de-Blur , но оно никогда не вернет вас в исходное состояние, поскольку данные уже потеряны. Итак, в зависимости от ситуации - возможности логического обратного действия и его осуществимости, вам нужно выбрать между этими двумя широкими категориями, а затем реализовать их так, как вы хотите. Конечно, можно создать гибридную стратегию, которая будет работать на вас.
Кроме того, иногда, как в Gmail, возможна отмена с ограничением по времени, потому что действие (отправка письма) никогда не выполняется. Таким образом, вы здесь не «отменяете», вы просто «не выполняете» само действие.
Есть несколько способов сделать это, но вы можете начать смотреть на Командный шаблон . Используйте список команд для перемещения назад (Отменить) или вперед (повторить) по своим действиям. Пример на C # можно найти здесь .
Я написал два текстовых редактора с нуля, и оба они используют очень примитивную форму функции отмены / повтора. Под «примитивом» я подразумеваю, что эту функциональность было очень легко реализовать, но это неэкономично для очень больших файлов (скажем, >> 10 МБ). Однако система очень гибкая; например, он поддерживает неограниченное количество уровней отмены.
По сути, я определяю структуру вроде
type
TUndoDataItem = record
text: /array of/ string;
selBegin: integer;
selEnd: integer;
scrollPos: TPoint;
end;
, а затем определяю массив
var
UndoData: array of TUndoDataItem;
Затем каждый член этого массива определяет сохраненное состояние текста. Теперь, при каждом редактировании текста (клавиша символа вниз, возврат вниз, клавиша удаления вниз, вырезание / вставка, выделение, перемещаемое мышью и т. Д.), Я (повторно) запускаю таймер (скажем) на одну секунду. При запуске таймер сохраняет текущее состояние как новый член массива UndoData
.
При отмене (Ctrl + Z) я возвращаю редактор в состояние UndoData [UndoLevel - 1]
и уменьшаю UndoLevel
на единицу.По умолчанию UndoLevel
равен индексу последнего члена массива UndoData
. При повторном выполнении (Ctrl + Y или Shift + Ctrl + Z) я восстанавливаю редактор до состояния UndoData [UndoLevel + 1]
и увеличиваю UndoLevel
на единицу. Конечно, если таймер редактирования срабатывает, когда UndoLevel
не равна длине (минус один) массива UndoData
, я очищаю все элементы этого массива после UndoLevel
, что является обычным для платформы Microsoft Windows (но Emacs лучше, если я правильно помню - недостатком подхода Microsoft Windows является то, что если вы отмените много изменений, а затем случайно отредактируете буфер, предыдущий контент (который был отменен) безвозвратно утерян). Вы можете пропустить это сокращение массива.
В программе другого типа, например, в редакторе изображений, можно применить тот же метод, но, конечно, с совершенно другой структурой UndoDataItem
. Более продвинутый подход, который не требует столько памяти, заключается в сохранении только изменений между уровнями отмены (то есть вместо сохранения «alpha \ nbeta \ gamma» и «alpha \ nbeta \ ngamma» \ ndelta ", вы можете сохранить" alpha \ nbeta \ ngamma "и" ADD \ ndelta ", если вы понимаете, что я имею в виду). В очень больших файлах, где каждое изменение мало по сравнению с размером файла, это значительно уменьшит использование памяти данными отмены, но это сложнее реализовать и, возможно, более подвержено ошибкам.