Drawing focus ring in NSTextView when using scaleUnitSquareToSize:

2k views Asked by At

As of OSX 10.7, Apple suggests using drawFocusRingMask and focusRingMaskBounds methods as the way to draw a focus ring around NSView based objects instead of how it was done previously in 10.6 (using NSSetFocusRingStyle() in drawRect:). (see release notes).

Tested on OSX 10.10 :

I have an NSTextView descendent which implements basic focus ring drawing as suggested:

- (void)drawFocusRingMask {
  NSRectFill([self bounds]);
}
- (NSRect)focusRingMaskBounds {
  return [self bounds];
} 

This NSTextView is programmatically added directly to the content view (without any scroll views).

This works well for the most part, however, when using scaleUnitSquareToSize: to scale the NSTextView, the focus ring drawn is completely incorrect.

So calling:

[textView scaleUnitSquareToSize:NSMakeSize(1.5, 1.5)];

Looks like this:

enter image description here

Scaling it further up or down results in even more skew of the focus ring rect. Reseting the scale of the NSTextView back to {1.0, 1.0} causes the focus ring to draw correctly again.

I assume this is some sort of a bug in the focus ring code. It seems to preform transformations on the graphics context used for drawing the focus ring before calling drawFocusRingMask / focusRingMaskBounds (internal class _NSAutomaticFocusRing is responsible for this).

Any idea how to fix this? Should I find a way to transform the context to the proper coordinates (so far without success)? Or is there any other way to force the focus ring to behave properly?

2

There are 2 answers

0
jsbox On

I have struggled with this bug for a long time. Here's a solution that seems to work:

Sub-class the NSTextField or NSTextView so that you are instantiating, e.g., a ZoomableTextField. Override its drawFocusRingMask: method, so that the override does nothing:

- (void)drawFocusRingMask
{
}

Then in the zoomable superview in which a ZoomableTextField is embedded, inside the superview's drawRect: method, call a new method of the ZoomableTextField, drawPossibleFocus, which looks like this:

- (void)drawPossibleFocus
{
    NSWindow *win = [self window];
    if ([self currentEditor]==[win firstResponder] && [win isKeyWindow]) {
        [NSGraphicsContext saveGraphicsState];
        NSSetFocusRingStyle(NSFocusRingOnly);
        NSRectFill([self frame]);
        [NSGraphicsContext restoreGraphicsState];
        }
}
0
jsbox On

Overriding the drawFocusRingMask method (via a sub-class of NSTextField) allows the width of the focus ring to be corrected by "manually" applying the super-view's scale factor, but changing the height doesn't work. My guess is that the height of the focus ring is being pinned to the text size or original text field bounds before scaling.

This all on system 10.7.5. So I'm simply turning the focus ring off when the text field's super-view is in a zoomed state, until I can figure out a workaround or whatever the right thing to do is.