NSTextField дважды захватывает событие возврата ключа

У меня есть текстовое поле и две кнопки. Пользователь должен иметь возможность нажать клавишу возврата, когда они закончат редактирование текстового поля, а затем снова вернуться, чтобы активировать одну или другую кнопку в зависимости от условий. Чтобы дать понять пользователю, что он может вернуться, чтобы активировать кнопку, я временно назначаю возврат в качестве эквивалента ключа выбранной кнопки, что должно сделать его светящимся синим цветом.

Селектор отправленного действия текстового поля включает в себя следующий код:

switch (self.iNavMode) {
    case kNavModeNeutral:
        break;
    case kNavModeSaveAndNew:
        [self.window makeFirstResponder:self.btnSaveAndNew];
        [self.btnSaveAndNew setKeyEquivalent:@"\r"];
        break;
    case kNavModeSaveAndNext:
        [self.window makeFirstResponder:self.btnSaveAndNext];
        [self.btnSaveAndNext setKeyEquivalent:@"\r"];
        break;
    default:
        break;
}

Действие выбранной кнопки затем выбивает эквивалент ключа, так что кнопка не будет продолжать светиться синим цветом после того, как она подаст в отставку firstResponder:

[self.btnSaveAndNext setKeyEquivalent:@""];

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

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

1 ответ

Решение

Ну, у меня есть клудж.

Я добавил логическое свойство shouldSwallowThisReturn. И я добавил строку, которая устанавливает для этого логического значения yes в селекторе send-action текстового поля:

switch (self.iNavMode) {
    case kNavModeNeutral:
        break;
    case kNavModeSaveAndNew:
        [self.window makeFirstResponder:self.btnSaveAndNew];
        self.shouldSwallowThisReturn = YES;
        [self.btnSaveAndNew setKeyEquivalent:@"\r"];
        break;
    case kNavModeSaveAndNext:
        [self.window makeFirstResponder:self.btnSaveAndNext];
        self.shouldSwallowThisReturn = YES;
        [self.btnSaveAndNext setKeyEquivalent:@"\r"];
        break;
    default:
        break;
}

И я добавил несколько строк в действие выбранной кнопки:

if (self.shouldSwallowThisReturn) {
    self.shouldSwallowThisReturn = NO;
    return;
}
[self.btnSaveAndNext setKeyEquivalent:@""];

Таким образом, остальная часть действия кнопки выполняется только после того, как пользователь нажал return во второй раз.

Это работает, но я бы предпочел более элегантное решение.

Дальнейшее изучение руководства Apple по обработке событий показывает, что не так: по-видимому, когда вы используете IB для назначения отправленного действия текстовому полю, хотя действие отключается, когда пользователь нажимает return, этот return не регистрируется как эквивалент ключа и, следовательно, не отвечает да на запрос executeKeyEquivalent приложения, и, следовательно, приложение продолжает искать элемент управления, который ответит "да", поэтому в конечном итоге он вызывает кнопку самостоятельно.

Таким образом, мне кажется, что я действительно должен подклассировать текстовое поле и переопределить его метод executeKeyEquivalent, чтобы он возвращал yes, если keyCode равен 36 (код для ключа возврата), например так:

- (BOOL) performKeyEquivalent:(NSEvent *)theEvent {
    printf("\nThe keycode is %d", [theEvent keyCode]);
    if ([theEvent keyCode] == 36) 
        return YES;
    else 
        return NO;
}

Но происходит то, что метод переопределения вызывается, даже когда целевое текстовое поле не имеет фокуса. Действительно, он вызывается, даже когда выбранная кнопка уже является первым ответчиком. Так что теперь возврат пользователя всегда прерывается, и действие кнопки никогда не вызывается.

Я пересмотрел метод переопределения, чтобы проверить идентичность firstResponder:

- (BOOL) performKeyEquivalent:(NSEvent *)theEvent {
    printf("\nThe keycode is %d", [theEvent keyCode]);
    if ([theEvent keyCode] == 36) {
        ThisProject_AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
        id firstResponder = [appDelegate.windowController.window firstResponder];
        if ([firstResponder isKindOfClass:[NSTextView class]]) {
            printf("\nfirstResp is a field editor, a textview.");
            if ([firstResponder delegate] == self) {
                printf("\ntarget textfield is firstResponder.");
                return YES;
            }
        }
        else if ([firstResponder isKindOfClass:[NSButton class]]) {
            printf("\nfirstResp is a button.");
            return YES;
        }
    }
    return NO;
}

Оказывается, переопределение вызывается после выполнения send-action текстового поля, когда статус firstResponder уже передан кнопке. Таким образом, переопределение не помогает.

На данный момент я застрял с Kludge в верхней части этого ответа. Но должен быть какой-то способ заставить send-действие полностью захватить событие возврата ключа, которое его отключило...

Другие вопросы по тегам