Chromium Code Reviews| Index: ui/views/cocoa/bridged_content_view.mm |
| diff --git a/ui/views/cocoa/bridged_content_view.mm b/ui/views/cocoa/bridged_content_view.mm |
| index 56e53abe4eaf4e947ae921953083f16ada4f8924..deb211faa5e3675d76b1315b5961e800bff87350 100644 |
| --- a/ui/views/cocoa/bridged_content_view.mm |
| +++ b/ui/views/cocoa/bridged_content_view.mm |
| @@ -5,6 +5,7 @@ |
| #import "ui/views/cocoa/bridged_content_view.h" |
| #include "base/logging.h" |
| +#import "base/mac/mac_util.h" |
| #import "base/mac/scoped_nsobject.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "skia/ext/skia_utils_mac.h" |
| @@ -16,6 +17,9 @@ |
| #import "ui/events/keycodes/keyboard_code_conversion_mac.h" |
| #include "ui/gfx/canvas_paint_mac.h" |
| #include "ui/gfx/geometry/rect.h" |
| +#include "ui/gfx/path.h" |
| +#import "ui/gfx/path_mac.h" |
| +#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" |
| #include "ui/strings/grit/ui_strings.h" |
| #include "ui/views/controls/menu/menu_config.h" |
| #include "ui/views/controls/menu/menu_controller.h" |
| @@ -26,6 +30,17 @@ using views::MenuController; |
| namespace { |
| +// Returns true if all four corners of |rect| are contained inside |path|. |
| +bool IsRectInsidePath(NSRect rect, NSBezierPath* path) { |
| + return [path containsPoint:rect.origin] && |
| + [path containsPoint:NSMakePoint(rect.origin.x + rect.size.width, |
| + rect.origin.y)] && |
| + [path containsPoint:NSMakePoint(rect.origin.x, |
| + rect.origin.y + rect.size.height)] && |
| + [path containsPoint:NSMakePoint(rect.origin.x + rect.size.width, |
| + rect.origin.y + rect.size.height)]; |
| +} |
| + |
| // Convert a |point| in |source_window|'s AppKit coordinate system (origin at |
| // the bottom left of the window) to |target_window|'s content rect, with the |
| // origin at the top left of the content area. |
| @@ -117,6 +132,20 @@ bool DispatchEventToMenu(views::Widget* widget, ui::KeyboardCode key_code) { |
| owner:self |
| userInfo:nil]); |
| [self addTrackingArea:cursorTrackingArea_.get()]; |
| + |
| + // Get Notified whenever the view bounds or frame change. |
| + [self setPostsBoundsChangedNotifications:YES]; |
| + [self setPostsFrameChangedNotifications:YES]; |
| + [[NSNotificationCenter defaultCenter] |
| + addObserver:self |
| + selector:@selector(boundsDidChangeNotification:) |
| + name:NSViewBoundsDidChangeNotification |
| + object:self]; |
| + [[NSNotificationCenter defaultCenter] |
| + addObserver:self |
| + selector:@selector(boundsDidChangeNotification:) |
| + name:NSViewFrameDidChangeNotification |
| + object:self]; |
| } |
| return self; |
| } |
| @@ -306,6 +335,15 @@ bool DispatchEventToMenu(views::Widget* widget, ui::KeyboardCode key_code) { |
| hostedView_->SetSize(gfx::Size(newSize.width, newSize.height)); |
| } |
| +- (void)viewDidEndLiveResize { |
| + // We prevent updating the window mask and clipping the border around the |
| + // view, during a live resize. Hence update the window mask and redraw the |
| + // view after resize has completed. |
| + [super viewDidEndLiveResize]; |
|
karandeepb
2016/02/04 03:39:28
This optimisation to prevent drawing during a live
|
| + [self setWindowMask]; |
| + [self setNeedsDisplay:YES]; |
| +} |
| + |
| - (void)drawRect:(NSRect)dirtyRect { |
| // Note that BridgedNativeWidget uses -[NSWindow setAutodisplay:NO] to |
| // suppress calls to this when the window is known to be hidden. |
| @@ -320,6 +358,31 @@ bool DispatchEventToMenu(views::Widget* widget, ui::KeyboardCode key_code) { |
| yRadius:radius] fill]; |
| } |
| + // On OS versions earlier than Yosemite, to generate a drop shadow, we set an |
| + // opaque background. This distorts windows with non rectangular shapes. To |
| + // get around this, fill the path outside the window boundary with clearColor |
| + // and tell Cococa to regenerate drop shadow. See crbug.com/543671. |
| + if (windowMask_ && ![self inLiveResize] && |
| + !IsRectInsidePath(dirtyRect, windowMask_)) { |
| + gfx::ScopedNSGraphicsContextSaveGState state; |
| + DLOG(WARNING) << "Drawing\n"; |
| + // The outer rectangular path corresponding to the window. |
| + NSBezierPath* outerPath = [NSBezierPath bezierPathWithRect:[self bounds]]; |
| + |
| + [outerPath appendBezierPath:windowMask_]; |
| + [outerPath setWindingRule:NSEvenOddWindingRule]; |
| + [[NSGraphicsContext currentContext] |
| + setCompositingOperation:NSCompositeCopy]; |
| + [[NSColor clearColor] set]; |
| + |
| + // Fill the region between windowMask_ and its outer rectangular path |
| + // with clear color. This causes the window to have the shape described |
| + // by windowMask_. |
| + [outerPath fill]; |
| + // Regerate drop shadow around the window boundary. |
| + [[self window] invalidateShadow]; |
| + } |
| + |
| // If there's a layer, painting occurs in BridgedNativeWidget::OnPaintLayer(). |
| if (hostedView_->GetWidget()->GetLayer()) |
| return; |
| @@ -741,4 +804,42 @@ bool DispatchEventToMenu(views::Widget* widget, ui::KeyboardCode key_code) { |
| return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point]; |
| } |
| +// Update windowMask_ depending on the current view bounds. |
| +- (void)setWindowMask { |
| + // Ensure a live resize is not in progress. |
| + DCHECK(![self inLiveResize]); |
| + |
| + views::Widget* widget = hostedView_->GetWidget(); |
| + if (widget->non_client_view() && base::mac::IsOSMavericksOrEarlier()) { |
| + const NSRect frameRect = [self bounds]; |
| + gfx::Path mask; |
| + widget->non_client_view()->GetWindowMask(gfx::Size(frameRect.size), &mask); |
| + if (!mask.isEmpty()) { |
| + windowMask_.reset([gfx::CreateNSBezierPathFromSkPath(mask) retain]); |
| + |
| + // Convert to AppKit coordinate system. |
| + NSAffineTransform* flipTransform = [NSAffineTransform transform]; |
| + [flipTransform translateXBy:0.0 yBy:frameRect.size.height]; |
| + [flipTransform scaleXBy:1.0 yBy:-1.0]; |
| + [windowMask_ transformUsingAffineTransform:flipTransform]; |
| + } |
| + } |
| +} |
| + |
| +// Called whenever the view's bounds or frame change. Used to update |
| +// windowMask_. |
| +- (void)boundsDidChangeNotification:(NSNotification*)notification { |
| + // Ensure that the notification was posted by this object. |
| + DCHECK_EQ(notification.object, self); |
| + // We don't update the window mask during a live resize, instead it is done |
| + // after the resize is completed in viewDidEndLiveResize:. |
| + if (![self inLiveResize]) |
| + [self setWindowMask]; |
| +} |
| + |
| +- (void)dealloc { |
| + [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| + [super dealloc]; |
| +} |
| + |
| @end |