Как оформить отмену и повтор в текстовом редакторе? - PullRequest
14 голосов
/ 27 августа 2010

Часть моего проекта - это написать текстовый редактор, который используется для ввода какого-то правила, а приложение компилируется и запускается.Написание компилятора было завершено, а выпуск бета-версии.В финальной версии мы должны добавить отмену и повтор в текстовом редакторе. Я использую файл и периодически сохраняю его для текстового редактора.Как оформить отмену и возврат в мой текстовый редактор?Что такое изменение в структуре персистента файла?

Ответы [ 8 ]

14 голосов
/ 27 августа 2010

Вы можете смоделировать свои действия как команды , которые хранятся в двух стеках.Один для отмены, другой для повторения.Вы можете составлять свои команды для создания более высокоуровневых команд, например, когда вы хотите отменить действия макроса, например;или если вы хотите сгруппировать отдельные нажатия клавиш для одного слова или фразы в одном действии.

Каждое действие в вашем редакторе (или действие повтора) генерирует новую команду отмены, которая входит в стек отмены (итакже очищает стек повторного выполнения).Каждое действие отмены генерирует соответствующую команду повторения, которая входит в стек повторения.

Вы также можете, как указано в комментариях derekerdmann , объединять команды отмены и повторения в один тип команды, который знает, как отменить и повторить свое действие.

8 голосов
/ 27 августа 2010

Есть два хороших способа сделать это:

  • шаблон проектирования «Команда»

  • с использованием только OO над неизменяемыми объектами, где все является просто неизменяемыми объектами, состоящими из неизменных объектов, сделанными из неизменных объектов (это менее распространено, но удивительно элегантно, если все сделано правильно) 1012 *

Преимущество использования ОО над неизменяемыми объектами над наивной командой или наивным отменением / повторением заключается в том, что вам не нужно много думать об этом: не нужно «отменять» эффект действия и не нужно » Переиграйте "все команды. Все, что вам нужно, это указатель на огромный список неизменяемых объектов.

Поскольку объекты неизменны, все «состояния» могут быть невероятно легкими, поскольку большинство объектов можно кэшировать / повторно использовать в любом состоянии.

«ОО над неизменными предметами» - это чистая жемчужина. Вероятно, не станет мейнстримом раньше, чем через 10 лет; )

P.S .: выполнение ОО над неизменяемыми объектами также удивительно упрощает параллельное программирование.

6 голосов
/ 27 августа 2010

Если вы не хотите ничего необычного, вы можете просто добавить UndoManager . Ваш Document будет запускать UndoableEdit каждый раз, когда вы добавляете или удаляете текст. Чтобы отменить и повторить каждое изменение, просто вызовите эти методы в UndoManager.

Недостатком этого является то, что UndoManager добавляет новое редактирование каждый раз, когда пользователь что-то вводит, поэтому при вводе «apple» у вас останется 5 изменений, которые можно отменить по одному. Для моего текстового редактора я написал оболочку для правок, в которой хранится время, в которое он был сделан, в дополнение к изменению и смещению текста, а также UndoableEditListener, который объединяет новые правки с предыдущими, если между короткими промежутками времени их (у меня 0,5 секунды хорошо работает).

Это хорошо работает для общего редактирования, но вызывает проблемы, когда выполняется массовая замена. Если у вас был документ с 5000 экземплярами «apple», и вы хотели заменить его на «orange», то в итоге вы получили бы 5000 правок, в которых были сохранены «apple», «orange» и смещение. Чтобы уменьшить объем используемой памяти, я рассмотрел это как отдельный случай для обычных правок и вместо этого храню «яблоко», «апельсин» и массив из 5000 смещений. Я еще не удосужился применить это, но я знаю, что это вызовет некоторые головные боли, когда несколько строк соответствуют условию поиска (например, поиск без учета регистра, поиск по регулярному выражению).

4 голосов
/ 27 августа 2010

Ух, какое совпадение - буквально за последний час я реализовал отмену / повтор в моем текстовом редакторе WYSIWYG:

Основная идея - либо сохранить все содержимое текстового редактора в массиве, либо разницу между последним редактированием.

Обновите этот массив в значимых точках, то есть каждые несколько символов (проверяйте длину содержимого при каждом нажатии клавиши, если его значение превышает, скажем, 20 символов, то создайте точку сохранения). Также при изменениях стиля (если форматированный текст), добавления изображений (если это разрешено), вставки текста и т. Д. Вам также нужен указатель (просто переменная int), чтобы указать, какой элемент в массиве является текущим состоянием редактор)

Сделать массив установленной длины. Каждый раз, когда вы добавляете точку сохранения, добавляйте ее в начало массива и перемещайте все остальные точки данных вниз на одну. (последний элемент в массиве будет забыт, когда у вас будет столько точек сохранения)

Когда пользователь нажимает кнопку отмены, проверьте, совпадает ли текущее содержимое редактора с последним сохранением (если нет, то пользователь внес изменения с последней точки сохранения, поэтому сохраните текущее содержимое редактора (чтобы его можно было переделать), сделать редактор равным последней точке сохранения и сделать указатель переменной = 1 (2-й элемент в массиве). Если они одинаковые, изменений не было начиная с последней точки сохранения, поэтому вам нужно отменить к точке до этого. Для этого увеличьте значение указателя + 1 и сделайте содержимое редактора = значением указателя.

Чтобы повторить, просто уменьшите значение указателя на 1 и загрузите содержимое массива (убедитесь, что вы достигли конца массива).

Если пользователь вносит изменения после отмены, переместите ячейку массива указанных значений вверх в ячейку 0 и переместите остальные на ту же величину (вы не хотите возвращаться к другим материалам после того, как они сделали разные правки).

Еще один важный момент - убедитесь, что вы добавляете точку сохранения только в том случае, если содержимое текстового редактора действительно изменилось (в противном случае вы получите дублирующиеся точки сохранения, и вам может показаться, что отмена ничего не делает.

Я не могу помочь вам с особенностями Java, но я рад ответить на любые другие ваши вопросы,

Nico

4 голосов
/ 27 августа 2010

Вы можете сделать это двумя способами:

  • хранить список состояний редактора и указатель в списке; отмена перемещает указатель назад и восстанавливает там состояние, передвигается вместо этого вперед, делая что-то, выбрасывает все за пределы указателя и вставляет состояние как новый верхний элемент;
  • не сохраняет состояния, но действия, которые требуют, чтобы для каждого действия у вас было противодействие отмене эффектов этого действия

В моем редакторе (диаграмме) есть четыре уровня изменения состояния:

  • фрагменты действия: они являются частью большого действия и не могут быть отменены или отменены отдельно. (например, перемещение мыши)
  • действия: один или несколько фрагментов действия, которые формируют значимое изменение, которое может быть отменено или переделано, но которые не отражены в отредактированном документе как измененные на диске (например, выбор элементов)
  • изменения документа: одно или несколько действий, которые изменяют отредактированный документ так, как он будет сохранен на диск (например, изменение, добавление или удаление элементов)
  • документ сохраняет: текущее состояние документа явно сохраняется на диск - в этот момент мой редактор выбрасывает историю отмен, так что вы не можете отменить после сохранения
3 голосов
/ 27 августа 2010

Вот фрагмент, который показывает, как SWT поддерживает операции Undo / Redo. Возьмите его в качестве практического примера (или используйте его напрямую, если ваш редактор основан на SWT):

SWT Undo Redo

3 голосов
/ 27 августа 2010

Это задание для шаблона команды .

2 голосов
/ 27 августа 2010
...