简体   繁体   English

查看 interpretKeyEvents:但是将不需要的事件传递到响应者链上?

[英]View interpretKeyEvents: but pass unwanted ones up the responder chain?

I'd really like my custom view to work with -moveLeft: , -deleteForward: , -selectAll: , etc., but I'd also like to pass any keys I didn't care about onward up the responder chain.我真的很希望我的自定义视图与-moveLeft:-deleteForward:-selectAll:等一起使用,但我也想将我不关心的任何键向上传递到响应者链。 Right now I'm overriding -keyDown: to call [self interpretKeyEvents:[NSArray arrayWithObject:event]];现在我覆盖-keyDown:调用[self interpretKeyEvents:[NSArray arrayWithObject:event]]; , but this seems to hog all the key events, even ones my view doesn't respond to. ,但这似乎占据了所有关键事件,即使是我的观点没有回应的事件。

Is there any way to pass unwanted events up the chain, but still respond to -moveLeft: , etc.?有没有办法将不需要的事件向上传递,但仍然响应-moveLeft:等? Or do I need to implement all my own actions in -keyDown: so that I know what I did and did not respond to?或者我是否需要在-keyDown:实现我自己的所有操作,以便我知道我做了什么和没有响应?

Came across this trying to find a solution to this same problem.遇到这个试图找到解决同样问题的方法。 Never found anything online, but I came up with something that seems to work well so far.从来没有在网上找到任何东西,但我想出了一些到目前为止似乎运作良好的东西。 Here's what I'm doing:这是我在做什么:

Subclass your NSTextView (or whatever you're using) and create an instance variable to temporarily store the key down event .子类化您的 NSTextView(或您正在使用的任何东西)并创建一个实例变量来临时存储 key down event 。 . . . .

@interface MyTextView : NSTextView {
    NSEvent* _keyDownEvent;
}

@end

Then define your view's methods like so (take out the retain/release junk if you're using automatic reference counting):然后像这样定义视图的方法(如果您使用自动引用计数,则取出保留/释放垃圾):

@implementation MyTextView

- (id)initWithFrame:(NSRect)frame {
    if (self = [super initWithFrame:frame]) {
        _keyDownEvent = nil;
    }

    return self;
}

- (void)keyDown:(NSEvent*)event {
    [_keyDownEvent release];
    _keyDownEvent = [event retain];
    [super keyDown:event];
}

- (void)doCommandBySelector:(SEL)selector {
    if (_keyDownEvent && selector == @selector(noop:)) {
        if ([self nextResponder]) {
            [[self nextResponder] keyDown:[_keyDownEvent autorelease]];
        } else {
            [_keyDownEvent release];
        }
        _keyDownEvent = nil;
    } else {
        [super doCommandBySelector:selector];
    }
}

- (void)dealloc {
    [_keyDownEvent release];

    [super dealloc];
}

@end

Here's how I arrived at this.这就是我如何到达这里的。 When a key press isn't handled, you hear a beeping tone.当未处理按键时,您会听到蜂鸣声。 So, I set a breakpoint on NSBeep(), and when the program broke, I spit out a stack trace in GDB:所以,我在 NSBeep() 上设置了一个断点,当程序崩溃时,我在 GDB 中吐出一个堆栈跟踪:

#0  0x00007fff96eb1c2d in NSBeep ()
#1  0x00007fff96e6d739 in -[NSResponder doCommandBySelector:] ()
#2  0x00007fff96e6d72b in -[NSResponder doCommandBySelector:] ()
#3  0x00007fff96fda826 in -[NSWindow doCommandBySelector:] ()
#4  0x00007fff96e6d72b in -[NSResponder doCommandBySelector:] ()
#5  0x00007fff96e6d72b in -[NSResponder doCommandBySelector:] ()
#6  0x00007fff96e6d72b in -[NSResponder doCommandBySelector:] ()
#7  0x00007fff96e6d72b in -[NSResponder doCommandBySelector:] ()
#8  0x00007fff96e6d72b in -[NSResponder doCommandBySelector:] ()
#9  0x00007fff96e6d72b in -[NSResponder doCommandBySelector:] ()
#10 0x00007fff96e6d72b in -[NSResponder doCommandBySelector:] ()
#11 0x00007fff96f486ce in -[NSTextView doCommandBySelector:] ()
#12 0x00007fff96da1c93 in -[NSKeyBindingManager(NSKeyBindingManager_MultiClients) interpretEventAsCommand:forClient:] ()
#13 0x00007fff970f5382 in -[NSTextInputContext handleEvent:] ()
#14 0x00007fff96fbfd2a in -[NSView interpretKeyEvents:] ()
#15 0x00007fff96f38a25 in -[NSTextView keyDown:] ()
#16 0x0000000100012889 in -[MyTextView keyDown:] (self=0x1004763a0, _cmd=0x7fff972b0234, event=0x100197320) at /path/MyTextView.m:24
#17 0x00007fff96a16b44 in -[NSWindow sendEvent:] ()
#18 0x00007fff969af16d in -[NSApplication sendEvent:] ()
#19 0x00007fff969451f2 in -[NSApplication run] ()
#20 0x00007fff96bc3b88 in NSApplicationMain ()
#21 0x00000001000015e2 in main (argc=3, argv=0x7fff5fbff8f0) at /path/main.m:12

What's happening is this: When the key down event isn't used for text input, a "noop" command is sent up the response chain.发生的事情是这样的:当按键按下事件不用于文本输入时,“noop”命令被发送到响应链上。 By default this triggers a beep when it falls off the response chain.默认情况下,当它脱离响应链时会触发蜂鸣声。 In my solution, the NSTextView subclass catches the noop command and instead throws the original keyDown event down the response chain.在我的解决方案中, NSTextView 子类捕获 noop 命令,而是将原始 keyDown 事件沿响应链向下抛出。 Then your NSWindow or other views will get any unused keyDown events as normal.然后您的 NSWindow 或其他视图将正常获取任何未使用的 keyDown 事件。

This is my swift implementation of @daxnitro's answer, and seems to work:这是我对@daxnitro 的回答的快速实施,并且似乎有效:

import Cocoa

class EditorTextView: NSTextView {

    private var keyDownEvent: NSEvent?

    required init?(coder aCoder: NSCoder) {
        super.init(coder: aCoder)
    }

    override init() {
        super.init()
    }

    override init(frame frameRect: NSRect, textContainer aTextContainer: NSTextContainer!) {
        super.init(frame: frameRect, textContainer: aTextContainer)
    }

    override func keyDown(event: NSEvent) {
        keyDownEvent = event
        super.keyDown(event)
    }

    override func doCommandBySelector(aSelector: Selector) {
        if aSelector != NSSelectorFromString("noop:") {
            super.doCommandBySelector(aSelector)
        } else if  keyDownEvent != nil {
            self.nextResponder?.keyDown(keyDownEvent!)
        }
        keyDownEvent = nil
    }

}

I had a closely related issue with a custom NSTextView in an NSTableView .我有一个与NSTableView的自定义NSTextView密切相关的问题。 I wanted to be able to shift-select text in the NSTextView , but when all the text was selected, pass the shift-select up the responder chain to the NSTableView .我希望能够在NSTextView移动选择文本,但是当所有文本都被选中时,将响应者链上的 shift-select 传递给NSTableView

My solution was simply to override responds(to:) on my NSTextView and decide whether I wanted to handle it there.我的解决方案只是在我的NSTextView上覆盖NSTextView responds(to:)并决定我是否想在那里处理它。

override func responds(to aSelector: Selector!) -> Bool  {
  if aSelector == #selector(moveUpAndModifySelection(_:)) {
    return selectedRange().location != 0
  }
            
  return super.responds(to: aSelector)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM