Есть какие то типовые варианты создания функции отмена?
Модератор: Модераторы
Есть какие то типовые варианты создания функции отмена?
Функция Отмена последних действий иногда очень полезная, но понятия не имею какими методами ее можно организовать в программах.
Если вся программа построена на классах наследованных от TObject например, то в каждом случае надо самому все реализовывать или есть какие то средства для "сливания" предыдущих значений памяти в хранилище отдельное?
Если вся программа построена на классах наследованных от TObject например, то в каждом случае надо самому все реализовывать или есть какие то средства для "сливания" предыдущих значений памяти в хранилище отдельное?
Реализовывать нужно самому.
Например хранить отдельно "старые" объекты и новые.
Например хранить отдельно "старые" объекты и новые.
MiniQ писал(а):Например хранить отдельно "старые" объекты и новые.
Я что то зациклился на мысли о хранении значения переменных, полностью отдельно объект и правда проще слить. Спасибо.
Я стараюсь хранить минимально возможный набор данных для отката - сохранять целый объект накладно, особенно при возможности массированых изменений. Вообще тема сложная и индивидуальная - единого решения нет.
Совет - undo\redo нужно вводить какможно раньше (ну или хотябы продумать как оно будет), внедрять это хозяйство в уже рабочий код - та еще каторга
Совет - undo\redo нужно вводить какможно раньше (ну или хотябы продумать как оно будет), внедрять это хозяйство в уже рабочий код - та еще каторга
Есть такое слово, как сериализация, оно поддерживается наследниками TPersistent.
Если логика работы представлена в виде дерева наследников TPersistent, которые пишут лог своих изменений, то возможно построение функции записи макроса работы программы, и макроса текущего состояния.
Сам подумываю над таким компонентом.
Если логика работы представлена в виде дерева наследников TPersistent, которые пишут лог своих изменений, то возможно построение функции записи макроса работы программы, и макроса текущего состояния.
Сам подумываю над таким компонентом.
-
Mirage
- энтузиаст
- Сообщения: 881
- Зарегистрирован: 06.05.2005 20:29:07
- Откуда: Russia
- Контактная информация:
Есть паттерн, сильно облегчающий реализацию undo/redo, а также дающий доп. плюшки:
Каждый тип операции модификации данных (коих по идее немног - изменение свойств чего-то, модификация данных и т.п.) оборачивается в класс с одним методом - Perform() допустим.
Первый его вызов совершает модификацию данных, второй - отменяет. Третий опять совершает и т.д.
Как это делается - сериализацией или как-то по-другому уже другой вопрос. Зависит.
И да, желательно сразу так делать, хотя, если программа написана вменяемо, то выделить классы операций должно быть несложно и уже существующем коде.
Каждый тип операции модификации данных (коих по идее немног - изменение свойств чего-то, модификация данных и т.п.) оборачивается в класс с одним методом - Perform() допустим.
Первый его вызов совершает модификацию данных, второй - отменяет. Третий опять совершает и т.д.
Как это делается - сериализацией или как-то по-другому уже другой вопрос. Зависит.
И да, желательно сразу так делать, хотя, если программа написана вменяемо, то выделить классы операций должно быть несложно и уже существующем коде.
>>оборачивается в класс с одним методом - Perform() допустим.
не все операции одинаково выглядят в ундо\редо, такчто метода лучше 2
не все операции одинаково выглядят в ундо\редо, такчто метода лучше 2
Mirage писал(а):Первый его вызов совершает модификацию данных, второй - отменяет. Третий опять совершает и т.д.
Что-то мудрено. А если мне нужно взглянуть, как было 10 операций назад? Т.е. откатить назад 10 операций и потом вернуть все в текущее состояние.
Вот этот патерн "первый вызов, второй вызов" как будет работать?
- Лекс Айрин
- долгожитель
- Сообщения: 5723
- Зарегистрирован: 19.02.2013 16:54:51
- Откуда: Волгоград
- Контактная информация:
MiniQ писал(а):Что-то мудрено. А если мне нужно взглянуть, как было 10 операций назад?
Думаю, проще сделать ссылку на следующее/предыдущее изменение. (то есть, использовать список операций)
помница когда писал свой текстовый супер редактор с БЖиШ везде советовали TCollection
-
Mirage
- энтузиаст
- Сообщения: 881
- Зарегистрирован: 06.05.2005 20:29:07
- Откуда: Russia
- Контактная информация:
zub писал(а):не все операции одинаково выглядят в ундо\редо, такчто метода лучше 2
Я тоже некоторое время так думал. Но потом понял, почему все же лучше один - применить, либо отменить два и более раз подряд тогда не получится и данные не потеряются.
MiniQ писал(а):А если мне нужно взглянуть, как было 10 операций назад? Т.е. откатить назад 10 операций и потом вернуть все в текущее состояние.Вот этот патерн "первый вызов, второй вызов" как будет работать?
Собственно, для этого и делается. При применении операции вызываем Perform() и кладем её в стек. При отмене - из стека достаем, кладем в другой стек и снова вызываем Perform(). При повторении (redo) достаем из второго стека, применяем, кладем в первый.
Можно и 10 шагов откатить и накатить обратно.
Еще оба стека красиво в один массив ложатся, с индексом текущей операции. Массив просто обрезается по текущему индексу при добавлении новой операции.
А какие проблемы могут быть с двойным вазовом? при работе не с "командой", а со стеком это сделать не получится, даже если постараться
>>Еще оба стека красиво в один массив ложатся, с индексом текущей операции. Массив просто обрезается по текущему индексу при добавлении новой операции.
у меня в конечном итоге так и получилось
>>Еще оба стека красиво в один массив ложатся, с индексом текущей операции. Массив просто обрезается по текущему индексу при добавлении новой операции.
у меня в конечном итоге так и получилось
Если так рассуждать то можно хоть что сделать)) Множественный вызов Perform тоже ничего хорошего не сулит.
Имхо ниче доставать ненадо, надо просто делать UndoStack.Undo или UndoStack.Redo. Возня с "операцией" должна быть только на этапе ее создания и подготовки, после помещения в стек нечего к ней лезти.
Как ни крути а Perform будет универсальным (с одинаковой реализацией ундо\редо) только для простых случаев изменения данных. Например добавление\удаление элемента массива уже не такая универсальная операция.
Имхо ниче доставать ненадо, надо просто делать UndoStack.Undo или UndoStack.Redo. Возня с "операцией" должна быть только на этапе ее создания и подготовки, после помещения в стек нечего к ней лезти.
Как ни крути а Perform будет универсальным (с одинаковой реализацией ундо\редо) только для простых случаев изменения данных. Например добавление\удаление элемента массива уже не такая универсальная операция.
-
Mirage
- энтузиаст
- Сообщения: 881
- Зарегистрирован: 06.05.2005 20:29:07
- Откуда: Russia
- Контактная информация:
zub писал(а):Множественный вызов Perform тоже ничего хорошего не сулит.
Это как раз штатная ситуация. Потерь данных точно нет. В отличии от повторного вызова Undo.
zub писал(а):Имхо ниче доставать ненадо, надо просто делать UndoStack.Undo или UndoStack.Redo. Возня с "операцией" должна быть только на этапе ее создания и подготовки, после помещения в стек нечего к ней лезти.
Можно, конечно, коллекцию (стек) нагрузить знаниями о том, что там хранятся операции и что их нельзя отдавать, и что надо контролировать чтобы они правильно вызывались. Но это как раз не "просто делать". Тут прям напрячься надо.
zub писал(а):Как ни крути а Perform будет универсальным (с одинаковой реализацией ундо\редо) только для простых случаев изменения данных. Например добавление\удаление элемента массива уже не такая универсальная операция.
То, что он универсальный это не принципиальный момент. Может и не быть универсальным. Может хранить флажок, что делаем в следующий раз: добавляем или удаляем.
Принципиально то, что все максимально просто, вся информация в самой операции и как её не мучай, потери данных не будет.
А коллекция останется коллекцией. Её можно даже будет в любой момент заменить на другую с аналогичным интерфейсом, как и должно быть с коллекциями.
