Subclass NSScrollView drawRect: method

I am customizing the user interface for one of my applications, and the idea is that the text area initially borders on gray when it is out of focus and when it focuses, the border becomes bright white. My app uses a dark theme, and for a single-line NSTextField this works fine.

I am having problems with a subclass of NSTextView . To change the border correctly, I had to actually subclass the parent NSScrollView , but I still see strange behavior. (See Screenshot below.) I want a red border to fill the entire scroll view, as this will allow me to smooth (rather than fill that just for testing) the path, creating a nice border, Instead, the red box seems to only fill internal children's performance.

The following code snippet, which is a subclass of NSScrollView :

 - (void)drawRect:(NSRect)dirtyRect { [super drawRect:dirtyRect]; NSRect borderRect = self.bounds; borderRect.origin.y += 1; borderRect.size.width -= 1; borderRect.size.height -= 4; BOOL inFocus = ([[self window] firstResponder] == self); if (!inFocus) { inFocus = [self anySubviewHasFocus:self]; } if (inFocus) { [[NSColor colorWithDeviceRed:.8 green:.2 blue:0 alpha:1] set]; } else { [[NSColor colorWithDeviceRed:.1 green:.8 blue:0 alpha:1] set]; } [NSGraphicsContext saveGraphicsState]; [[NSGraphicsContext currentContext] setShouldAntialias:NO]; [NSBezierPath fillRect:borderRect]; [NSGraphicsContext restoreGraphicsState]; NSLog(@"My bounds: %@", NSStringFromRect(borderRect)); NSLog(@"Super (%@) bounds: %@", [self superview], NSStringFromRect(borderRect)); } 

Creates a screenshot as shown below. Also see Log Output, which assumes that the entire view needs to be populated. This is the only result that is ever displayed, regardless of the size of the text inside. Entering carriage returns increases the height of the red , but does not create another output. (And I would like the red box to fill all the borders.)

 2011-04-08 21:30:29.789 MyApp[6515:903] My bounds: {{0, 1}, {196, 87}} 2011-04-08 21:30:29.789 MyApp[6515:903] Super (<EditTaskView: 0x3a0b150>) bounds: {{0, 1}, {196, 87}} 

Drawing issue

Edit: Thanks to Josh Caswell for his answer. See the correct behavior below when it is not focused and when focusing.

No focus

Focus

+4
source share
1 answer

As ughoavgfhw noted, NSScrollView usually doesnโ€™t make any drawing and probably has a weird interaction with its child views in this way. I would suggest drawing something like the following in your text view: to draw this custom focus ring that you want *:

 // We're going to be modifying the state for this, // so allow it to be restored later [NSGraphicsContext saveGraphicsState]; // Choose the correct color; isFirstResponder is a custom // ivar set in becomeFirstResponder and resignFirstResponder if( isFirstResponder && [[self window] isKeyWindow]){ [myFocusedColor set]; } else { [myNotFocusedColor set]; } // Create two rects, one slightly outset from the bounds, // one slightly inset NSRect bounds = [self bounds]; NSRect innerRect = NSInsetRect(bounds, 2, 2); NSRect outerRect = NSMakeRect(bounds.origin.x - 2, bounds.origin.y - 2, bounds.size.width + 4, bounds.size.height + 4); // Create a bezier path using those two rects; this will // become the clipping path of the context NSBezierPath * clipPath = [NSBezierPath bezierPathWithRect:outerRect]; [clipPath appendBezierPath:[NSBezierPath bezierPathWithRect:innerRect]]; // Change the current clipping path of the context to // the enclosed area of clipPath; "enclosed" defined by // winding rule. Drawing will be restricted to this area. // NB that the winding rule makes the order that the // rects were added to the path important. [clipPath setWindingRule:NSEvenOddWindingRule]; [clipPath setClip]; // Fill the rect; drawing is clipped and the inner rect // is not drawn in [[NSBezierPath bezierPathWithRect:outerRect] fill]; [NSGraphicsContext restoreGraphicsState]; 

This should be a reasonable approximation of what AppKit does when it draws a focus ring. Of course, AppKit can be allowed to draw out of sight - I canโ€™t guarantee that itโ€™s absolutely safe, but you seem to get a 3 px margin for the game. If you would like, you could draw the whole ring within the borders. In any case, the true focus ring expands slightly (2 pixels) (as I did here).

Apple docs on setting crop area .

EDIT: After reading your comments on this, I understand that I may have long buried in a real answer. Try either subclassing NSClipView , or switching the scroll view clip view to do this, or using a custom view to represent the document.


*: You can also put this in the drawing code of a subclass of a custom view, which is set as the document view NSScrollView ; then your textual representation may be a subpoint of this. Or replace the custom subclass of NSClipView .

+7
source

Source: https://habr.com/ru/post/1347326/


All Articles