The default value for [NSTextView selectedTextAttributes]
is unusable in my app, because i allow the user to select colors (syntax highlighting) that are almost exactly the same as the background color.
I have written some math to determine a suitable color and can use this to set it:
textView.selectedTextAttributes = @{
NSBackgroundColorAttributeName: [NSColor yellowColor],
NSForegroundColorAttributeName: [NSColor redColor]
};
But when the window is in the background, it still uses the system default light grey.
I've attached screenshots of the above code with active vs inactive window. — how can I change the selected text background colour of the inactive window?
You can override the colour by overriding drawing method of NSLayoutManager
.
final class LayoutManager1: NSLayoutManager {
override func fillBackgroundRectArray(rectArray: UnsafePointer<NSRect>, count rectCount: Int, forCharacterRange charRange: NSRange, color: NSColor) {
let color1 = color == NSColor.secondarySelectedControlColor() ? NSColor.redColor() : color
color1.setFill()
super.fillBackgroundRectArray(rectArray, count: rectCount, forCharacterRange: charRange, color: color1)
color.setFill()
}
}
And replace NSTextView
's layout manager to it.
textView.textContainer!.replaceLayoutManager(layoutManager1)
Here's full working example.
As @Kyle asks for reason of setFill
, I add some update.
From Apple manual:
... the charRange and color parameters are passed in merely for informational purposes; the color is already set in the graphics state. If for any reason you modify it, you must restore it before returning from this method. ...
Which means passing-in other color into super
call has no effect, and you just need to
NSColor.setFill
to make it work with super
call.
Also, the manual requires to set it back to original one.
It's not when the window is in the background it's when the NSTextView is not selected. I don't think you can change that behavior.
You could create an attributed string and add the NSBackgroundColorAttributeName attribute to the range of the selected text when it loses focus. The attributed string stays the same color even when the focus is lost.
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:@"hello world"];
[string addAttribute:NSForegroundColorAttributeName value:[NSColor redColor] range:NSMakeRange(1, 7)];
[string addAttribute:NSBackgroundColorAttributeName value:[NSColor yellowColor] range:NSMakeRange(1, 7)];
[self.myTextView insertText:string];
EDIT by Abhi Beckert: this is how I implemented this answer (note I also had to disable the built in selected text attributes, or else they override the ones I'm setting):
@implementation MyTextView
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (!(self = [super initWithCoder:aDecoder]))
return nil;
// disable built in selected text attributes
self.selectedTextAttributes = @{};
return self;
}
- (id)initWithFrame:(NSRect)frameRect textContainer:(NSTextContainer *)container
{
if (!(self = [super initWithFrame:frameRect textContainer:container]))
return nil;
// disable built in selected text attributes
self.selectedTextAttributes = @{};
return self;
}
- (void)setSelectedRanges:(NSArray *)ranges affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)stillSelectingFlag
{
// remove from old ranges
for (NSValue *value in self.selectedRanges) {
if (value.rangeValue.length == 0)
continue;
[self.textStorage removeAttribute:NSBackgroundColorAttributeName range:value.rangeValue];
}
// apply to new ranges
for (NSValue *value in ranges) {
if (value.rangeValue.length == 0)
continue;
[self.textStorage addAttribute:NSBackgroundColorAttributeName value:[NSColor yellowColor] range:value.rangeValue];
}
[super setSelectedRanges:ranges affinity:affinity stillSelecting:stillSelectingFlag];
}
@end
You can specify that your NSTextView
should be treated as first responder by overriding layoutManagerOwnsFirstResponder(in:) from NSLayoutManager and the selection will use your defined attributes.
In Swift 5.1 would be:
override func layoutManagerOwnsFirstResponder(in window: NSWindow) -> Bool {
true
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With