blob: 4f37d9e3989ce363bfcfebbf9ea7e724fcc3444b [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/mac/sdk_forward_declarations.h"
#import "ui/base/cocoa/nsview_additions.h"
#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
#include "base/logging.h"
@implementation NSView (ChromeAdditions)
- (CGFloat)cr_lineWidth {
// All shipping retina macs run at least 10.7.
if (![self respondsToSelector:@selector(convertSizeFromBacking:)])
return 1;
return [self convertSizeFromBacking:NSMakeSize(1, 1)].width;
}
- (BOOL)cr_isMouseInView {
NSPoint mouseLoc = [[self window] mouseLocationOutsideOfEventStream];
mouseLoc = [[self superview] convertPoint:mouseLoc fromView:nil];
return [self hitTest:mouseLoc] == self;
}
- (BOOL)cr_isBelowView:(NSView*)otherView {
NSArray* subviews = [[self superview] subviews];
NSUInteger selfIndex = [subviews indexOfObject:self];
DCHECK(NSNotFound != selfIndex);
NSUInteger otherIndex = [subviews indexOfObject:otherView];
DCHECK(NSNotFound != otherIndex);
return selfIndex < otherIndex;
}
- (BOOL)cr_isAboveView:(NSView*)otherView {
return ![self cr_isBelowView:otherView];
}
- (void)cr_ensureSubview:(NSView*)subview
isPositioned:(NSWindowOrderingMode)place
relativeTo:(NSView *)otherView {
DCHECK(place == NSWindowAbove || place == NSWindowBelow);
BOOL isAbove = place == NSWindowAbove;
if ([[subview superview] isEqual:self] &&
[subview cr_isAboveView:otherView] == isAbove) {
return;
}
[subview removeFromSuperview];
[self addSubview:subview
positioned:place
relativeTo:otherView];
}
- (NSColor*)cr_keyboardFocusIndicatorColor {
return [[NSColor keyboardFocusIndicatorColor]
colorWithAlphaComponent:0.5 / [self cr_lineWidth]];
}
- (void)cr_recursivelyInvokeBlock:(void (^)(id view))block {
block(self);
for (NSView* subview in [self subviews])
[subview cr_recursivelyInvokeBlock:block];
}
- (void)cr_recursivelySetNeedsDisplay:(BOOL)flag {
[self setNeedsDisplay:YES];
for (NSView* child in [self subviews])
[child cr_recursivelySetNeedsDisplay:flag];
}
static NSView* g_ancestorBeingDrawnFrom = nil;
static NSView* g_childBeingDrawnTo = nil;
- (void)cr_drawUsingAncestor:(NSView*)ancestorView inRect:(NSRect)dirtyRect
clippedToAncestorBounds:(BOOL)clipToAncestorBounds {
gfx::ScopedNSGraphicsContextSaveGState scopedGSState;
NSRect frame = [self convertRect:[self bounds] toView:ancestorView];
NSAffineTransform* transform = [NSAffineTransform transform];
if ([self isFlipped] == [ancestorView isFlipped]) {
[transform translateXBy:-NSMinX(frame) yBy:-NSMinY(frame)];
} else {
[transform translateXBy:-NSMinX(frame) yBy:NSMaxY(frame)];
[transform scaleXBy:1.0 yBy:-1.0];
}
[transform concat];
// This can be made robust to recursive calls, but is as of yet unneeded.
DCHECK(!g_ancestorBeingDrawnFrom && !g_childBeingDrawnTo);
g_ancestorBeingDrawnFrom = ancestorView;
g_childBeingDrawnTo = self;
NSRect drawRect = [self convertRect:dirtyRect toView:ancestorView];
if (clipToAncestorBounds) {
drawRect = NSIntersectionRect([ancestorView bounds], drawRect);
}
[ancestorView drawRect:drawRect];
g_childBeingDrawnTo = nil;
g_ancestorBeingDrawnFrom = nil;
}
- (void)cr_drawUsingAncestor:(NSView*)ancestorView inRect:(NSRect)dirtyRect {
[self cr_drawUsingAncestor:ancestorView
inRect:dirtyRect
clippedToAncestorBounds:YES];
}
- (NSView*)cr_viewBeingDrawnTo {
if (!g_ancestorBeingDrawnFrom)
return self;
DCHECK(g_ancestorBeingDrawnFrom == self);
return g_childBeingDrawnTo;
}
- (void)cr_setAccessibilityLabel:(NSString*)label {
if (@available(macOS 10.10, *)) {
self.accessibilityLabel = label;
} else {
[self accessibilitySetOverrideValue:label
forAttribute:NSAccessibilityDescriptionAttribute];
}
}
@end