How to change the effect of focusing on an NSTextField on OSX - macos

In CSS, you can change the effect of focusing on something using:
.myelement:focus { ... }
While in Cocoa, the text fields always have an ugly blue glow. How do I change the effect of focusing on an NSTextField (or not have it do anything at all)?

The Cocoa equivalent of the command above would be:
[[textField window] makeFirstResponder:textField];
As to changing the appearance, are you asking how to change it for all controls in all applications ("globally") or just for a text field in your own app? There's no API for a global change, so system hacks are your only avenue. Good luck.
For controls you own (those that belong to your own application), you can set the focus ring type in Interface Builder or by code at runtime like this:
[textField setFocusRingType:NSFocusRingTypeNone];
// (or NSFocusRingTypeDefault or NSFocusRingTypeExterior)

Related

NSImageView always displays on top of any other kind of view

I'm working on a database application that has a graphical editor that is very similar to Interface Builder. Unlike IB, however, this editor can be flipped from editor mode to live mode, where the user interface is fully operational (buttons can be clicked, text edited, etc.) To do this, the graphical editor uses standard Appkit interface classes -- NSButton, NSTextView, etc. The editor itself is implemented with a custom subclass of NSView. All of the user interface elements are subviews of this custom NSView, new elements are added using the addSubview: method, making the new element the topmost visible element (note -- the views are not layer-backed, just regular views). The user can also use Bring-to-Front and Send-to-Back commands to change the ordering of the subviews. This movie shows two overlapping NSButton elements (for illustration purposes, of course normally you would never overlap them), and how the program can re-arrange the subview to change the Z-order of the user interface elements.
The problem is, this works with every kind of interface element except NSImageView. In the movie before there are two elements, an NSButton and an NSImageView. The NSButton is actually "on top" the whole time, the NSImageView element should appear behind the button, but no matter the order of the subviews, the NSImageView always appears on top.
If there are two overlapping NSImageView objects, the visible stacking order between them is unpredictable, but they will always appear above all other objects, no matter what the order of the subviews is.
A possibly useful clue is that if I implement my own custom view that draws an image directly in it's drawRect: method, that works fine. So that's one possible solution, but I am reluctant because that means re-implementing a large swath of useful features that NSImageView normally takes care of, some of which are quite complex like supporting animated GIF display. Other than this layering/z-order issue, everything else about NSImageView works fine.
Perhaps NSImageView is using layer-backing without my asking for it, so it isn't mixing in properly with my other objects? I can't find any documentation that indicates this. I am not linking against the QuartzCore framework.
Here is the code that adds the NSImageView element as a subview to the graphic editor view.
- (void)objectDidAppearBelow:(NSView *)nextView
{
FormView * formView = [FormWindowController currentFormView]; // get view element will be placed into
NSScrollView * imageContainer = [[NSScrollView alloc] initWithFrame:insideBorderRect];
ImageView * ixView = [[ImageView alloc] initWithFrame:[self insideFormObjectBorder:objectRectangle]];
[ixView setOwnerObject:self];
[imageContainer setDocumentView:ixView];
[imageContainer setAutoresizesSubviews:YES];
[shapeView addSubview:imageContainer Below:nextView];
imageDocumentView = ixView; // save weak reference to image view so it can be manipulated
}
In other places there is nearly identical code for NSButton (in several variations for push buttons, radio buttons, etc.), NSTextView, NSTableView (for lists and matrixes), NSSlider, NSScroller, NSSegmentedControl, even WebView. All the others work correctly with overlapping objects, including WebView, only NSImageView doesn't work as expected.
For my reference this is #429 in the Panorama X issue tracker.
I discussed this problem with Apple engineers at WWDC 2018. It turns out that as I suspected, in some cases Appkit will use layer backing for NSImageView even if you did not ask for it! So the best solution is to switch all views to layer backing (which will happen automatically with Mojave).
In this particular case the NSImageView was inside a NSScrollView, which I didn't mention because I didn't think it was important (my bad). It turns out, this is the case where Appkit thinks using layer backing would be a good idea (to optimize scrolling). So another way to fix this is to subclass NSImageView (which I had already done for other reasons) and add this method (written on the spot by an Apple engineer who prefers to remain uncredited).
+ (BOOL)isCompatibleWithResponsiveScrolling {
if (NSAppKitVersionNumber <= 1561. /* NSAppKitVersionNumber10_13 */) {
return NO;
} else {
return YES;
}
}
I was assured that this is all part of the public, documented API, though the documentation is minimal (surprise surprise). There is some discussion of responsive scrolling in the What's new in OS X 10.9 release notes. The check with NSAppKitVersionNumber is to make sure to turn off this patch when running on Mojave, since everything is layer backed.

Straightforward way to implement custom drawn NSMenuItem views

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.

Change object color in Xcode

I'm new to Xcode (Mac development, not iOS), and for some reason I can't figure out how to even change any object's color (text, background or really anything). Every site seems to say to click on the object, go to the attributes inspector and all of those options are under 'view'. However, in my Xcode (5.1.1) all it shows under 'view' is tag, focus ring, drawing, and auto-resizing. Am I missing something obvious?
On OS X, NSView does not have an intrinsic backgroundColor property. Thus, you cannot set the color of a view from Interface Builder. You have to create an NSView subclass and override -drawRect: or -updateLayer to make it the color you want. Even then, that color will not show up in Interface Builder. (This changes in Xcode 6, which is still in beta as of this writing.)
Is this somewhat annoying? Yeah. But that's the way it is.
As for changing the text of an object, you should be able to do it from the Attributes inspector, but only if it's something that already has text (i.e. a text field, text view, or button). An arbitrary custom view does not have text, so you can't set it in Interface Builder.
You should easily be able to set the background color of whatever UI element you want programmatically.
[viewObject setBackgroundColor:[UIColor greenColor]];
As for editing a xib for OSX I'm not sure. I have only done iOS development.

NSOutlineView with transparent field editor

I'm working with a NSOutlineView located on a HUD panel. I configured it so that it doesn't draw its background. Everything looks fine until I double click to edit a cell.
The field editor draws its background and focus ring which completely ruin the whole user experience.
This is what I'm doing in the subclass of NSTextFieldCell:
- (NSText *)setUpFieldEditorAttributes:(NSText *)textObj
{
NSText *text = [super setUpFieldEditorAttributes:textObj];
[text setDrawsBackground:YES];
[text setBackgroundColor:[NSColor darkGrayColor]];
return text;
}
If I use setDrawsBackground:NO it's completely ignored and I get a white background. My solution is far from being good because I can't touch the alpha component of the color (if I do that, again the field editor will use another color as a background), but at least I don't get a white background.
I'm wondering if there's an actual solution to this problem. Do I have to provide my own field editor? Is it worth it?
What I want is simply a field editor with no background and no focus ring, just the cursor blinking.
Thanks!
The problem is that the white background is drawn by NSTableView when it's sent -editColumn:row:withEvent:select:. It fills the cell's rect with +[NSColor textBackgroundColor].
If there's a public API for overriding the current setting for named colors from the developer colorspace, we could set it inside an override of -editColumn:row:withEvent:select: or the like. I do not recall such an API (pointers are appreciated). ALSO: I've only tested this code on Snow Leopard (even the Leopard SDK addendum below). Verify the code against the actual SDKs and runtime environments you intend to support.
NSTableView has a private accessor it uses for the fill color, but it's a read-only property. No setter, so we can't just change the value on a standard NSTableView. We must subclass it. (Since you want the same behavior in an outlineView and NSOutlineView is already a subclass of NSTableView, we're going to subclass NSOutlineView. But, aside from the superclass, the code is identical.)
#interface ASCOutlineView : NSOutlineView {
}
#end
#implementation ASCOutlineView
- _textBackgroundColor
{
return ([NSColor clearColor]);
}
#end
seems to be all one needs to prevent that glaring white block from ruining your HUD when editing table cells in Snow Leopard.
Apps compiled against the Leopard SDK need a little more support though. Leopard's tableViews may have hard-coded some rendering properties so we need to override a choice method.
NSTextFieldCells are actually wrappers for NSTextViews so they can be used inside controls. They normally share the same textView instance, which is managed by the window (or its subclass, panel, in this case). NSTableView alters the settings of the NSTextFieldCell to conform to system UI settings for editing data. Mostly. The NSTextFieldCell then propagates those settings to the NSTextView. At any point along this pipeline we can override a method or two to alter the values of those properties to match our own UI.
I use -[NSTextFieldCell setDrawsBackground:] because it requires little effort to get correct. It's also important to keep the internal state as consistent with the effect we're hoping to achieve in the event some other object might depend on that state.
#interface ASCTextFieldCell : NSTextFieldCell {
}
#end
#implementation ASCTextFieldCell
- (void)setDrawsBackground: (BOOL)flag
{
[super setDrawsBackground: NO];
}
#end
And preventing the focus ring from appearing while the cell's being edited is a simple matter of changing the setting of its focus ring type. Frustratingly, IB doesn't provide access to this property, so it must be done programmatically:
for(eachColumn in [hudOutlineView tableColumns])
{
columnCell = [[ASCTextFieldCell alloc] initTextCell: #""];
[eachColumn setDataCell: columnCell];
if([columnCell respondsToSelector: #selector(setFocusRingType:)] != NO)
[(NSTextFieldCell *)columnCell setFocusRingType: NSFocusRingTypeNone];
}
It looks like there is other background behind field editor, which is drawn as white.
Probably, NSCell, or background of row, whatever else.

Cocoa Pop-up Window Similar to iCal

I want to open an overlay window (pop up window) when a user selects a cell in my NSTableView similar to selecting an event in iCal. Selecting the event in iCal shows a Window to edit the event, but does so by smoothly animating the window open and adding an arrow pointing to the even in the underlying calendar. Does anyone know what is being used here? Is this a bunch of hidden/custom APIs or is this available for public use?
The editor pane appears to be a custom borderless, transparent window with a custom view (the view defines the shape and therefore the shadow it casts). Learn more here. You might even use MAAttachedWindow.
Regarding animation, it's as simple as asking the window's animator to animate the frame and the alpha value (grouping them together). You'll probably want to set everything up directly in its "start position" first (ie, while the window is off-screen, set its alpha to zero, and its frame to some smaller version so it "zooms in" a la iCal), then put it on screen and start the grouped animation:
[NSAnimationContext beginGrouping];
[[window animator] setFrame:someNewSlightlyLargerFrame];
[[window animator] setAlphaValue:1.0];
[NSAnimationContext endGrouping];
Once the grouping is ended, the animation will begin (asynchronously) and your code execution will continue. Something to think about is getting everything "staged" first (including making sure the subviews of your window are already updated beforehand, so they don't change in the middle of your animation ... unless you want them to).
So the two separate techniques you need to understand are a custom window and basic Cocoa animation.
If you're using OSX 10.7 and above, NSPopover will do the job you're looking for.
Are you talking about selecting even from a list at the bottom of iCal app?
Not sure what exactly you are referring to but there is an api for animating transformations within a timespan.
Looking at other Apple's applications, Apple's developers utilize the same api available to anyone else (mostly I guess). There is lots of stuff that can be customized and Apple customizes whatever is required until it looks right from design point of view ...

Resources