How to get highlighted text from my NSTextField? - objective-c

It must be certainly easy but I haven't found anything in the doc.
The behavior I need is exactly as when you right-click any portion of text and then you can do some action with it.
For the moment I have a my own custom NSTextField class which re-implment 'mouseDown' action. This part works ! I thought I could get the selected portion of text of my nstextfield thanks to 'theEvent' but apparently it is not possible.

I don't actually think you can do this with NSTextField. I think you have to use an NSTextView instead and then make use of the -selectedRanges method.
EDIT: I should have said, you can't do this directly (i.e., there's no NSTextField method for doing this). I think rather, you have to use the field editor (which is itself an NSTextView) associated with the window in order to do this. Here's the apple guide for using the field editor.

NSString *selectedText = ((NSTextFieldCell*)textField.selectedCell).stringValue;

Related

How can I make an NSTextField that has focus redraw itself?

I've subclassed and defined my own custom NSFormatter and NSTextField classes in order properly display SMPTE time code values (HH:MM:SS:FF). When the user selects to use drop-frame for the fps value, the format changes to HH:MM:SS;FF (semi-colon at the end). I have this all working well except for one final hurdle.
When changing the format, the NSTextField with the focus does not update it's display until it's focus is lost. Here is a screenshot to illustrate:
I have tried sending the NSTextField a setNeedsDisplay message when the format is changed, as well as sending it a display message. Nothing seems to work so far, and the text field with focus refuses to update/redraw itself until it loses focus.
Any suggestions of what I need to do to make this happen?
I did manage to solve this problem now. It may be a bit of a hack, but basically what I ended up doing was retrieving the NSTextField's field editor with currentEditor and sending it as an object with the posted notification for when the format changes.
With the field editor I then just had it replaceCharactersInRange:withString: to change the last ":" to ";" and vice-versa. Though it may not be optimal, it works perfectly.
In fact, I've posted the solution to github for anyone else who might find this useful.
https://github.com/cfloisand/smpteformatter

NSTextField accessibility - How to provide alternate text for voiceover

It will probably be obvious, but I have never done any work with NSAccessibility before so I'm assuming what I'm asking is something simple I've overlooking.
I have an NSTextField displaying a duration like this, 15:39. This text field is a subview of an NSTableCellView in a view-based NSTableView.
When VoiceOver is enabled, it currently reads off, "one five, three nine", which seems completely useless. Instead, I want it to say "Duration is 15 minutes, 39 seconds."
I can produce the desired string, but I cannot figure out which accessibility attributes I have to set to make this happen.
I've tried setting the accessibility description in IB, which has no effect (whether setting it on the NSTextField or NSTextFieldCell).
I've also tried overriding accessibilityValue: and accessibilityAttributeValue:forParameter: in order to provide custom attribute values for:
NSAccessibilityNumberOfCharactersAttribute
NSAccessibilityStringForRangeParameterizedAttribute
NSAccessibilityAttributedStringForRangeParameterizedAttribute
NSAccessibilityStringForRangeParameterizedAttribute.
This seemed to be the right track since that does allow me to replace what is read aloud by voice over, however, providing any NSRange for NSAccessibilityVisibleCharacterRangeAttribute that doesn't match the length of the "15:39" string causes voice over to completely skip this field when reading off the NSTableCellView's contents. So, the best I've been able to do is get Voice over to say "Durat" instead of reading off "15:39" :(
Everything I've tried, I've tried on NSTextField and NSTextFieldCell.
Ideally, I'd prefer to do what I'd do in iOS and just set the accessibilityLabel of the NSTableCellView, but I see no reasonable way of doing this in AppKit. Hopefully I'm just missing something.
I was able to achieve this simply by setting accessibilityValueDescription on the NSTextField. This method is part of the new Accessibility API on OS X 10.10 and higher. With the older API, you may be able to use kAXValueDescriptionAttribute to achieve the same thing.
The solution for overriding the text read by Voice Over was much simpler than I thought. All I had to do was override the value returned for NSAccessibilityAttributedStringForRangeParameterizedAttribute:
// The displayed text for this text field is "45m".
// The voice over for this field incorrectly reads "forty five meters" instead of "forty five minutes".
// The following forces it to read "Duration forty five minutes"
-(id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter {
return ([attribute isEqualToString:NSAccessibilityAttributedStringForRangeParameterizedAttribute])
? [[NSAttributedString alloc] initWithString:#"Duration: 45min"];
: [super accessibilityAttributeValue:attribute forParameter:parameter];
}
I believe I've answered my own question, at least enough to control what's read off when clicking a table cell view.
By overriding accessibilityIsIgnored to return "NO" in my NSTableCellView sub-class I was able to specify exactly what I wanted to be read off for the table cell by overriding the NSAccessibilityTitleAttribute for the cell. I had not tried this before b/c I had misunderstood the purpose of the accessibilityIsIgnored selector.
From the documentation for accessibilityIsIgnored:
When asking for an object’s children, ignored children should not be included; instead, the ignored children should be replaced by their own unignored children.
I'd like to be able to control exactly what is read off for the individual NSTextFields in the future, but controlling what is read off for an entire NSTableCellView is actually ideal for my particular situation.

How to insert a NSButton into a NSTextView? (inline)

I have a NSTextView and a custom NSButton. What I want to do is to insert that button (60x16 in size) to the end of the NSTextView.
How can I do something like that? I've been trying to search around on how to do this but I'm not getting anywhere.
Where should I begin? Thanks
I believe this question is pretty similar to yours:
Buttons inside an NSTextView / NSTextStorage
Quote from the question:
how do I get an NSButton to appear inside the text and react to
clicks?
Note that the issue is not fully solved there, but it seems the OP got a good head start. Hopefully you can take some clues from the discussion.
Here is one answer:
NSTextAttachment holds the contents of an attachment; it is the value
of the NSAttachmentAttributeName attribute for the
NSAttachmentCharacter in the attributed string. The contents are
usually given by an NSFileWrapper, but this is not required; you can
create an empty NSTextAttachment with a nil file wrapper.
NSTextAttachmentCell handles display and user interaction for the
attachment. By default an NSTextAttachment will create an
NSTextAttachmentCell to display itself, depending on the contents of
the attachment; in the generic case this will just be an image cell
displaying an icon.
If you wish, however, you can supply a custom NSTextAttachmentCell for
your attachment. It need not be an member of the class
NSTextAttachmentCell; it only needs to conform to the
NSTextAttachmentCell protocol. Actually, even that is not strictly
necessary; it only needs to implement a few of the basic methods for
sizing and drawing. Most cells already do this.
You will, however, need to deal with mouse events yourself. The
methods you'll probably want to implement would be
wantsToTrackMouseForEvent:inRect:ofView:atCharacterIndex: and
trackMouse:inRect:ofView:atCharacterIndex:. The character index here
should let you determine which portion of the text is relevant.

Changing the text on a button when a tab is selected in a tabview

I'm trying to learn objective-c by making a GUI application using Xcode 3. I'm wondering if it is possible to change the text of a button outside of a tab view depending on which tab in the tabview is selected? As I said I am trying to learn objective-c so please act as though I know next to nothing in your answer. I should probably mention that I tried making a NSObject and tried to define an IBAction in a .h and .m file but that didn't seem to work. (I have tried setting some breakpoints in those files none of which were ever reached leading me to think something isn't wired the way think it is.)
Sorry for the long winded explanation.
Thanks for all the help!
I actually figured it out. What I needed was object delegation, this page, and of course Google to help get the correct syntax for the actual implementation.

Best way to capture key events in NSTextView?

I'm slowly learning Objective-C and Cocoa, and the only way I see so far to capture key events in Text Views is to use delegation, but I'm having trouble finding useful documentation and examples on how to implement such a solution. Can anyone point me in the right direction or supply some first-hand help?
Generally, the way you implement it is simply to add the required function to your view's controller, and set its delegate. For example, if you want code to run when the view loads, you just delegate your view to the controller, and implement the awakeFromNib function.
So, to detect a key press in a text view, make sure your controller is the text view's delegate, and then implement this:
- (void)keyUp:(NSEvent *)theEvent
Note that this is an inherited NSResponder method, not a NSTextView method.
Just a tip for syntax highlighting:
Don't highlight the whole text view at once - it's very slow. Also don't highlight the last edited text using -editedRange - it's very slow too if the user pastes a large body of text into the text view.
Instead you need to highlight the visible text which is done like this:
NSRect visibleRect = [[[textView enclosingScrollView] contentView] documentVisibleRect];
NSRange visibleRange = [[textView layoutManager] glyphRangeForBoundingRect:visibleRect inTextContainer:[textView textContainer]];
Then you feed visibleRange to your highlighting code.
It's important to tell us what you're really trying to accomplish — the higher-level goal that you think capturing key events in an NSTextView will address.
For example, when someone asks me how to capture key events in an NSTextField what they really want to know is how to validate input in the field. That's done by setting the field's formatter to an instance of NSFormatter (whether one of the formatters included in Cocoa or a custom one), not by processing keystrokes directly.
So given that example, what are you really trying to accomplish?
I've done some hard digging, and I did find an answer to my own question. I'll get at it below, but thanks to the two fellas who replied. I think that Stack Overflow is a fantastic site already--I hope more Mac developers find their way in once the beta is over--this could be a great resource for other developers looking to transition to the platform.
So, I did, as suggested by Danny, find my answer in delegation. What I didn't understand from Danny's post was that there are a set of delegate-enabled methods in the delegating object, and that the delegate must implement said events. And so for a TextView, I was able to find the method textDidChange, which accomplished what I wanted in an even better way than simply capturing key presses would have done. So if I implement this in my controller:
- (void)textDidChange:(NSNotification *)aNotification;
I can respond to the text being edited. There are, of course, other methods available, and I'm excited to play with them, because I know I'll learn a whole lot as I do. Thanks again, guys.