Straightforward way to implement custom drawn NSMenuItem views - macos

I would like to have a NSMenu with several NSMenuItems in it. Those items shall behave just like "normal" NSMenuItems, with one exception: I would like to be able to draw the contents (i.e. the "foreground") of each item on my own.
That is:
I would like the operating system to draw the blue highlight when I hover with the mouse, draw the checkmark or dash (to signify "selected"/"part of multi-selection"). Moreover, I would like "normal", i.e. menu-built-in mouse/keyboard behaviour.
I would like to draw the "contents" myself: Ideally, I would imagine that I would have to implement/override one single method drawMenuItem:withCGContext:inRectangle:withTextColor, which basically obtains the item, the CGContext to draw into, the NSRect of the item, and the text color that would be used by Mac OS if it drew the item itself.
I know (suspect very stronlgy) that my wish is probably too much, but I would be perfectly fine with something adequate (e.g. I could imagine fetching the NSRect from the NSMenuItem myself, obtaining the color using something like [NSColor selectedMenuItemTextColor], and the context using something like [NSContext currentContext]).
Of course I considered Apple's instruction on this. However, doing this prevents the menu from drawing the blue highlight background and the check mark. Moreover, as far as I could see, I would have to implement e.g. mouseUp myself.
To sum it up: Is there a way to get a (almost) fully functional menu, where I only customize the contents where usually the menu item title goes (possibly even without custom views)?

The answer is: No.
Since you are replacing the entire view of the menu item you are responsible for the drawing and to handle the events.

Related

How to display elements inside NSCollectionView with various shapes

I am a rookie Cocoa guy. I need to design and implement a view which will show collection of labels on Mac OS using Xamarin. These labels will have a text and color associated with them. When shown inside the view, label should expand till it covers whole text and it will be shown with background and foreground colors.
I have attached the picture of this user control on Windows, you can see that labels inside the StackPanel are expanding till they cover the whole text. Hope this gives better idea about my ask.
The $64,000 question is "are these labels controls?" In other words, do you expect the user to click on these to do something, or are they just for display?
If your answer is "just for display", the solution is super simple: Use an NSTextField and programmatically add attributed text (NSAttributedString) to it. Attributed text attaches display properties to runs of text within the field; properties like "background color".
If you want these to be buttons that you can click on, then things get a lot more complicated.
Since you apparently want the button layout to "flow", you might look into imbedding buttons (well, button cells) into an NSTextField using attachments. This is normally how non-text content (say, an image) can be inserted, but with some fiddling it can actually be anything a control cell can draw. See How to insert a NSButton into a NSTextView? (inline).
Warning: this is not a "rookie" topic and will involve control cells and custom event handling.
If I were doing this, I'd probably just create NSButton objects for each label (choosing an appropriate style/look like NSRecessedBezelStyle), create a custom subclass of NSView to contain them, and then override the layout method to position all of the buttons the way I want.
To be thorough, I'd also override the intrinsic size methods so the whole thing could participate in auto-layout, based on the number and size of buttons it contained.

How do I get controls to draw outside a window in Objective C

I've got a search-suggestion box that I've implemented by using an NSScrollView that appears when you start typing in an NSTextField. I'd like my window to be quite short, and have the ScrollView draw partially outside the window. Like this:
But instead I get this:
What can I do?
You can't have controls that extend outside of a window. What you need to do is put them in a separate window. You generally want that window to be a child of the original window (using -[NSWindow addChildWindow:ordered:]).
For the use case you describe, you should use the built-in control, NSComboBox. If you really want to reimplement this sort of control, Apple provides the CustomMenus sample code and a related WWDC video. It specifically includes a suggestions menu to help fill in a text field.

Adding controls to NSTextView and binding them to (ranges of) characters

When editing code, Xcode is capabale of displaying in-text controls, like drop down buttons which can show context menu's. I've seen other OS X apps that handle text capable of similar features. See the attached sample.
I presume this effect is obtained using NSTextAttachmentCell - although I'm not sure whether this is the proper way to implement this.
For my own app I would like to use this technique as well.
I have the following questions:
Is NSTextAttachmentCell the correct way to implement such a feature? If not, what would be?
How do I attach a control -comparable to the one in the above sample- to a specific range of text so that its location within NSTextView is dynamic and follows layout actions?
I found this which gives some hints but does not cover the attachment to specific text ranges.
Although NSTextAttachmentCell will work, it has a disadvantage: the cell will become just a glyph in the text which was not what I wanted. It distorts the layout of the text, is selectable etc. I wanted the cell to be drawn over the text, just like the behaviour in Xcode.
The challenge was to find a way of translating a point from a Mouse Moved event to the position of a particular string of characters inside the NSTextView.
After some more digging I found a little gem in Apple's demo apps called LayoutManagerDemo. This demo shows a custom subclass of NSTextView capable of highlighting individual characters, words and lines while the mouse is hoovering its view. From there on it was pretty easy to fade in a button at the required NSPoint and then show a popup menu with some options.

make NSRect selectable

Is there a simple way to create a selectable NSRect in Cocoa? In need a rectangle that can be selected and stays selected after a mouse click.
Thanks.
NSRect is just a struct with a position and size. It's not an object that can actually do anything or have any properties other than a width and height. It sounds like what you want is to create an NSView that can be selected. (Here's Apple's Guide on the subject.)
Though not as immediate as you would like, you may be interested in the management of tracking rectangles and tracking areas performed by NSView class.
This mechanism allows you to define specific areas of your custom view. Then, an event is generated whenever the cursor enters or leaves the area, or a mouse button is pressed in this area (-mouseEntered:, -mouseExited:, -mouseDown:, -mouseUp:, -mouseDragged:, ... of NSResponder class). This up to you to define what you want your application do in response to these events (set the rectangle as selected and display it accordingly).
For an example implementation of this, take a look at the Sketch example included with the Apple developer tools (look in /Developer/Examples/AppKit). Sketch allows the user to create new graphics (including rectangles, but also ovals, lines, and text), select them, move them around in the document, etc. In particular, you'll probably want to look at the SKTGraphic class, which represents a single graphic object in the document, and the SKTGraphicView class, which is an NSView subclass that perform the actual layout and drawing, handling mouse events for dragging views around, etc.

Getting rid of the focus rect on an NSCell. Only shows up when right clicking

I have an NSCell subclass that I do all kinds of custom drawing in. The only time things seem to be entirely out of my control is when right clicking a cell to show the associated context menu. While showing it's context menu, the table (or cell - not sure which exactly is doing this) draws a focus rect. I would like to get rid of this, or at least find a way to draw my own version of it that better fits within my interface.
I have tried about 15 different methods that seem like they could give me control over this focus rect drawing itself (various first responder methods, various drawing methods, various highlight color methods, etc.), but I have yet to find something that changes anything about it.
Screenshot of the problem: http://twitpic.com/3zx2t
I am almost annoyed enough to class-dump AppKit and try to find whatever private method it's using to draw this annoying focus rect. Nothing else I do has any effect on it. Any help here would really save both my sanity and lots of future hair pulling.
Thanks so much!
Without completely overriding your table view's -drawRect: this is the only other way I know to get rid of the context menu highlight rect.
- (void)_drawContextMenuHighlightForIndexes:(NSIndexSet *)rowIndexes clipRect:(NSRect)rect {
return;
}
This is the method, used in Leopard at least, to draw the highlight around cells that will be activated upon by a context menu. It is unfortunately called directly by NSTableView's -drawRect: method and as far as I know there is no other way to signal your disinterest in having those highlights drawn.
Of course this is private API, it may stop working in Snow Leopard, or some other release. But at worst that means that somewhere along the way the context menu highlight may start up again in your programs on newer releases or some other functionality using this method may not work later. You have been warned.
On 10.5 and later, NSTableView allows you to bring up a contextual menu on any item in the table (that is, without having the side effect of changing the selection.)
It draws that 'focus rect' to indicate which item(s) are being acted on by the contextual menu.

Resources