4

Intro

When my custom NSTextField is in "text editing mode" and the field editor has been placed in front of it as firstResponder I no longer get the keystrokes through NSTextField.keyDown(...). I understand that the keystrokes are now being routed through the field editor. Most online recommendations are to override the following method within the delegate of the custom NSTextField:

control(_:textView:doCommandBySelector:)

I have indeed overridden this method but it doesn't seem to get called? See code below:

class FocusDelegate: NSObject, NSTextFieldDelegate{

    func control(control: NSControl, textView: NSTextView, doCommandBySelector commandSelector: Selector) -> Bool {

        println("FocusDelegate")
        if commandSelector == Selector("cancelOperation:"){
            println("cancelOperation")
            control.abortEditing()
            return true
        }
        return false
    }
}

The only other thing I do is set the delegate of my custom NSTextField to an instance of the class above. I have also tried placing the method above directly into the custom NSTextField class - it doesn't cause any errors but it also doesn't get called.

Questions

  1. What am I missing above in getting the delegate call to work?
  2. Other than creating my own custom field editor is this the only way? Can the textDidChange(notification: NSNotification) not be interrogated to yield the keys pressed?
  3. In using the control(_:textView:doCommandBySelector:) delegate method, how do I trap the pressing of keys that do NOT have standard key bindings. In particular, I want to intercept the key combination "shift+enter" which does not map to any standard selector. (Implied Question: can you only map to standard key-action methods in NSResponder?)
Sam
  • 2,745
  • 3
  • 20
  • 42
  • When you say formula editor, do you mean field editor? – JWWalker Mar 06 '15 at 20:27
  • @JWWalker LOL, was confusing what I am using it for with its official name! Thanks for pointing that out. – Sam Mar 07 '15 at 05:19
  • Do you need to use the underscore (`_`) in your function's signature to get it to match? The docs have `func control(_ control: NSControl, textView textView: NSTextView, doCommandBySelector command: Selector)`. If you use that signature, does it get called? – Ken Thomases Mar 09 '15 at 17:03
  • @KenThomases I think the underscore is a red-herring. The code above was auto-completed by Xcode. See my answer below. – Sam Mar 09 '15 at 19:15

2 Answers2

3

I believe I have achieved greater clarity with regard to the workings of NSTextField its delegates and its fieldEditor and its delegates. Yes, each one has its own delegate and the NSTextField is automatically set up as the delegate of the fieldEditor.

I was adding a delegate to the host NSTextField. This will NOT get called when the fieldEditor is firstResponder, which was what I was concerned with. This is also not the important delegate for the scenario above.

What appears to be useful is making the NSTextField conform to the NSTextViewDelegate protocol and specifically overriding the method:

 func textView(textView: NSTextView, shouldChangeTextInRange affectedCharRange: NSRange, replacementString: String) -> Bool

to trap various keypresses.

The notification in textDidChange(notification: NSNotification) is not of use in this scenario because it is reported AFTER the keypress.

Sam
  • 2,745
  • 3
  • 20
  • 42
1

Intercepting pressed keys in textView:shouldChangeTextInRange:replacementString: is not ideal because that is called rather late, and is also called when the user pastes from the clipboard, for instance.

A probably cleaner way is to intercept textView:doCommandBySelector:, which gets called for all kinds of special keys such as tab, cursor, return and enter, for which you'll then get specific selectors that let you determine which special key was invoked.

For some keys, however, such as Return (CR) and Enter (ETX), you can't tell them apart, because both use the same "insertNewline:" selector.

In this case, you can check whether the currently handled event is a keydown event, and if so, check which key it was.

Here's a quick test code that lets you monitor the selector names and the involved keycode:

-(BOOL)textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector
{
    NSEvent *ev = self.view.window.currentEvent;
    NSLog (@"selector: %s, key: %d", commandSelector, (int)ev.keyCode);
    return NO;
}
Thomas Tempelmann
  • 11,045
  • 8
  • 74
  • 149