Как я могу заставить имена действий отмены / возврата NSUndoManager работать должным образом? - PullRequest
7 голосов
/ 23 февраля 2011

Я изучаю Какао, и я получил возможность отменить работу без особых проблем. Но setActionName: метод меня озадачивает. Вот простой пример: игрушечное приложение, окна которого содержат одну текстовую метку и две кнопки. Нажмите кнопку Вкл, и на этикетке появится надпись «Вкл.». Нажмите кнопку «Выкл.», И надпись изменится на «Выкл.». Вот два соответствующих метода (единственный код, который я написал для приложения):

-(IBAction) turnOnLabel:(id)sender
{
    [[self undoManager] registerUndoWithTarget:self selector:@selector(turnOffLabel:) object:self];
    [[self undoManager] setActionName:@"Turn On Label"];
    [theLabel setStringValue:@"On"];
}

-(IBAction) turnOffLabel:(id)sender
{
    [[self undoManager] registerUndoWithTarget:self selector:@selector(turnOnLabel:) object:self];
    [[self undoManager] setActionName:@"Turn Off Label"];
    [theLabel setStringValue:@"Off"];
}

Вот что я ожидаю:

  • Я нажимаю кнопку Вкл.
  • Метка меняется на «Вкл.»
  • В меню «Правка» есть пункт «Отменить включение метки»
  • Я щелкаю этот пункт меню
  • Метка меняется на «Выкл»
  • В меню «Правка» есть пункт «Повторить метку включения»

На самом деле, все эти вещи работают, как я ожидаю, кроме последнего. Элемент в меню «Правка» гласит «Повторить отключить метку», а не «Повторить включить метку». (Когда я щелкаю по этому пункту меню, метка действительно включается, как и следовало ожидать, но это делает название пункта меню еще более загадочным.)

Что я неправильно понимаю и как я могу заставить эти пункты меню отображать так, как я хочу?

Ответы [ 3 ]

3 голосов
/ 13 августа 2011

Помните: когда вы делаете повтор, ваш код должен установить actionName для пункта меню Undo.

Когда вы отменяете или повторное выполнение, actionName в пункте меню Redo устанавливается автоматически.

setActionName: изменяет только пункт меню Отменить . Пункт меню Повторить actionName автоматизирован.

Когда вы изначально setActionName:, когда ![[self undoManager] isUndoing], это actionName переходит к пункту меню Отменить. Когда вы выбираете Отменить ([[self undoManager] isUndoing] == YES, actionNames вы не устанавливаете), undoManager автоматически устанавливает для этого actionName пункт меню Повторить, а для предыдущей отмены actionName - пункт меню Отменить. Когда вы решите «Повторить», вам все равно придется передать actionName, чтобы перейти к пункту меню «Отменить».

Другими словами: вы должны устанавливать actionNames только тогда, когда ваш код не отменен (но вы должны установить его при первоначальном вызове или повторном выполнении).

2 голосов
/ 03 марта 2011

Поскольку ваш метод turnOnLabel: может быть вызван тремя возможными способами:

1) когда элемент управления, с которым связан выбранный элемент, выполняет свою последовательность назначения / действия (т. Е. Методы NSUndoManager isUndoing и isRedoing будут возвращать NO)

2) когда вы выполняете операцию отмены, а метод turnOnLabel: фактически вызывается NSUndoManager (т. Е. IsUndoing = YES и isRedoing = NO)

3) когда вы выполняете операцию повтора, а метод turnOnLabel: фактически вызывается NSUndoManager (то есть isUndoing = NO и isRedoing = YES)

isUndoing  isRedoing    Action
-------------------------------------------------
   0           0        Turn On Label
   0           1        Turn On Label
   1           0        Turn Off Label
   1           1        <impossible state>
2 голосов
/ 23 февраля 2011

Посмотрев пример кода, я смог это исправить, добавив условие к setActionName: вызовы в каждом методе, например так:

if (![[self undoManager] isUndoing])
    [[self undoManager] setActionName:@"Turn On Label"];

Я дам правильный ответ тому, ктомогу объяснить, почему NSUndoManager нуждается во мне для этого.

...