NSTextField 两次捕获返回键事件

发布于 2024-10-18 21:24:12 字数 807 浏览 4 评论 0原文

我有一个文本字段和两个按钮。用户完成编辑文本字段后应该能够按回车键,然后根据条件再次返回以激活其中一个按钮。为了让用户清楚他们可以返回来激活按钮,我暂时将返回指定为所选按钮的等效键,这应该使其发出蓝光。

文本字段的发送操作选择器包含以下代码:

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;
}

所选按钮的操作然后敲出等效的键,以便按钮在退出后不会继续发出蓝光响应者:

[self.btnSaveAndNext setKeyEquivalent:@""];

问题是当用户从文本字段返回时,返回键事件以某种方式被捕获两次,并且程序会自行激活按钮,即使用户实际上没有再次按下返回键。

有没有一种方法可以完全捕获并处理第一个返回键事件,这样就不会发生这种情况?

I have a textfield and two buttons. The user should be able to press return when they’re done editing the textfield, and then return again to activate one or the other of the buttons, depending on conditions. In order to make it clear to the user that they can return to activate the button, I temporarily assign return as the chosen button’s key equivalent, which should make it glow blue.

The textfield’s sent-action selector includes this code:

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;
}

The chosen button’s action then knocks out the key equivalent, so that the button won’t continue to glow blue once it resigns firstResponder:

[self.btnSaveAndNext setKeyEquivalent:@""];

The problem is that when the user returns out of the textfield, the return key event is somehow captured twice, and the program activates the button on its own, even though the user has not actually pressed return again.

Is there a way that I can completely capture and dispose of the first return key event so this doesn’t happen?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

明月松间行 2024-10-25 21:24:13

好吧,我有一个杂凑。

我添加了一个布尔属性shouldSwallowThisReturn。我在文本字段的发送操作选择器中添加了一行,将此布尔值设置为 yes:

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:@""];

因此,按钮操作的其余部分仅在用户第二次按回车键后才执行。

这可行,但我更喜欢更优雅的解决方案。

进一步检查 Apple 的事件处理指南会发现问题出在哪里:显然,当您使用 IB 将发送操作分配给文本字段时,尽管当用户按回车键时会触发该操作,但回车键不会注册为等效键,因此不会对应用程序的 PerformKeyEquivalent 查询响应 yes,因此应用程序会不断寻找将响应 yes 的控件,因此它最终会自行调用该按钮。

所以看来我真正应该做的是子类化文本字段并重写它的performKeyEquivalent方法,以便如果keyCode是36(返回键的代码)它返回yes,如下所示:

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

但是发生的情况是重写方法甚至被调用当目标文本字段没有焦点时。事实上,即使所选按钮已经是firstResponder,它也会被调用。所以现在用户的返回总是被抢占,并且按钮的操作永远不会被调用。

我修改了重写方法来检查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;
}

事实证明,当firstResponder状态已经转移到按钮时,重写是在执行文本字段的发送操作之后调用的。所以覆盖没有帮助。

现在,我陷入了这个答案顶部的混乱之中。但必须有某种方法来获取发送操作以完全捕获启动它的返回键事件......

Well, I’ve got a kludge.

I added a boolean property shouldSwallowThisReturn. And I added a line that sets this boolean to yes in the textfield’s sent-action selector:

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;
}

And I added a few lines to the chosen button’s action:

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

So the rest of the button’s action executes only after the user has pressed return a second time.

This works, but I would prefer a more elegant solution.

Further examination of Apple’s event-handling guide suggests what’s wrong: Apparently, when you use IB to assign a sent-action to a textfield, although the action is set off when the user presses return, that return doesn’t register as a key equivalent, and therefore doesn’t respond yes to the app’s performKeyEquivalent query, and therefore the app keeps looking for a control that will respond yes, so it ends up calling the button on its own.

So it seems what I really should do is subclass the textfield and override its performKeyEquivalent method so that it returns yes if the keyCode is 36 (the code for the return key), like this:

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

But what happens is that the override method is called even when the target textfield does not have focus. Indeed, it gets called even when the chosen button is already firstResponder. So now the user’s return is always preempted and the button’s action is never called.

I revised the override method to check the identity of the 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;
}

It turns out that the override is called after the execution of the textfield’s sent-action, when the firstResponder status has already been transferred to the button. So the override doesn't help.

For now, I’m stuck with the kludge at the top of this answer. But there must be some way to get a sent-action to fully capture the return key event that set it off…

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文