问题
I have a custom NSTextField and I'd like to detect double clicks by the user in the text field. My goal: I want to be able to double click on a parenthesis in an expression, such as "(2+2) = 4" and have it select everything inside the matching parentheses. Thought I could do this with...
- (void)textView:(NSTextView *)textView doubleClickedOnCell:(id <NSTextAttachmentCell>)cell inRect:(NSRect)cellFrame atIndex:(NSUInteger)charIndex;
but it never gets called in my custom NSTextField.
Then I thought I could override -mouseDown, but that isn't getting called either. I'm stumped. Any suggestions for what should be an easy function to implement.
Thanks!
Philip
回答1:
A text field does not handling editing, as such. When a text field has focus, a text view is added to the window, overlapping the area of the text field. This is called the "field editor" and it is responsible for handling editing.
It seems the most likely place for you to change the behavior of a double-click is in the text storage object used by that text view. NSTextStorage
inherits from NSMutableAttributedString
which inherits from NSAttributedString
which has a -doubleClickAtIndex:
method. That method returns the range of the text that should be selected by a double-click at a particular index.
So, you'll want to implement a subclass of NSTextStorage
that overrides that method and returns a different result in some circumstances. NSTextStorage
is a semi-abstract base class of a class cluster. Subclassing it requires a bit more than usual. You have to implement the primitive methods of NSAttributedString
and NSMutableAttributedString
. See the docs about it.
There are a few places to customize the field editor by replacing its text storage object with an instance of your class:
- You could implement a custom subclass of
NSTextFieldCell
. Set your text field to use this as its cell. In your subclass, override-fieldEditorForView:
. In your override, instantiate anNSTextView
. Obtain itslayoutManager
and call-replaceTextStorage:
on that, passing it an instance of your custom text storage class. (This is easier than putting together the hierarchy of objects that is involved with text editing, although you could do that yourself.) Set thefieldEditor
property of the text view to true and return it. - In your window delegate, implement
-windowWillReturnFieldEditor:toObject:
. Create, configure, and return anNSTextView
using your custom text storage, as above.
回答2:
The answer from Ken Thomases here is correct in its analysis of the issue regarding the field editor and how to replace it, but the solution it then recommends – replacing the NSTextStorage
of the field editor – is not the correct solution, according to Apple. In their doc they specifically recommend that for delimiter-balancing the selectionRangeForProposedRange:granularity:
method should be used. Once you have a custom field editor going, as per Ken's answer, you should therefore use the solution for NSTextView
here, applied to a custom NSTextView
subclass that you use for your field editor.
In case it is of interest, using NSTextStorage
's doubleClickAtIndex:
method for delimiter-balancing is probably the wrong solution for several trivial reasons: (1) because Apple says so, (2) because subclassing NSTextStorage
is complicated and error-prone, and (3) because NSTextView
provides a method specifically intended for the purpose of doing things like delimiter-balancing. But it is also wrong for a non-trivial reason: (4) that doubleClickAtIndex:
is documented as "Returns the range of characters that form a word (or other linguistic unit) surrounding the given index, taking language characteristics into account". So doubleClickAtIndex:
is really about how the linguistic units of the text (i.e. words) are defined, and redefining those in some way to make delimiter-balancing work would probably break other aspects of word-level text processing. For example, I would guess that it would be pretty tricky to make double-click-drag (dragging out a selection word by word) work properly if you have overridden doubleClickAtIndex:
to do delimiter balancing. Cocoa may use doubleClickAtIndex:
for other aspects of word-finding too, and may add more uses of it in the future. Since a delimiter-balanced section of text is not a "word", who knows what weirdness might result.
来源:https://stackoverflow.com/questions/32408868/how-to-detect-double-click-in-nstextfield