OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "ui/base/cocoa/underlay_opengl_hosting_window.h" | 5 #import "ui/base/cocoa/underlay_opengl_hosting_window.h" |
6 | 6 |
| 7 #import <objc/runtime.h> |
| 8 |
7 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/mac/mac_util.h" |
| 11 #include "base/mac/scoped_nsautorelease_pool.h" |
| 12 #include "base/memory/scoped_nsobject.h" |
| 13 |
| 14 @interface NSWindow (UndocumentedAPI) |
| 15 // Normally, punching a hole in a window by painting a subview with a |
| 16 // transparent color causes the shadow for that area to also not be present. |
| 17 // That feature is "content has shadow", which means that shadows are effective |
| 18 // even in the content area of the window. If, however, "content has shadow" is |
| 19 // turned off, then the transparent area of the content casts a shadow. The one |
| 20 // tricky part is that even if "content has shadow" is turned off, "the content" |
| 21 // is defined as being the scanline from the leftmost opaque part to the |
| 22 // rightmost opaque part. Therefore, to force the entire window to have a |
| 23 // shadow, make sure that for the entire content region, there is an opaque area |
| 24 // on the right and left edge of the window. |
| 25 - (void)_setContentHasShadow:(BOOL)shadow; |
| 26 @end |
| 27 |
| 28 @interface OpaqueView : NSView |
| 29 @end |
| 30 |
| 31 @implementation OpaqueView |
| 32 - (void)drawRect:(NSRect)r { |
| 33 [[NSColor blackColor] set]; |
| 34 NSRectFill(r); |
| 35 } |
| 36 @end |
| 37 |
| 38 namespace { |
| 39 |
| 40 NSComparisonResult OpaqueViewsOnTop(id view1, id view2, void* context) { |
| 41 BOOL view_1_is_opaque_view = [view1 isKindOfClass:[OpaqueView class]]; |
| 42 BOOL view_2_is_opaque_view = [view2 isKindOfClass:[OpaqueView class]]; |
| 43 if (view_1_is_opaque_view && view_2_is_opaque_view) |
| 44 return NSOrderedSame; |
| 45 if (view_1_is_opaque_view) |
| 46 return NSOrderedDescending; |
| 47 if (view_2_is_opaque_view) |
| 48 return NSOrderedAscending; |
| 49 return NSOrderedSame; |
| 50 } |
| 51 |
| 52 void RootDidAddSubview(id self, SEL _cmd, NSView* subview) { |
| 53 if (![[self window] isKindOfClass:[UnderlayOpenGLHostingWindow class]]) |
| 54 return; |
| 55 |
| 56 // Make sure the opaques are on top. |
| 57 [self sortSubviewsUsingFunction:OpaqueViewsOnTop context:NULL]; |
| 58 } |
| 59 |
| 60 } // namespace |
8 | 61 |
9 @implementation UnderlayOpenGLHostingWindow | 62 @implementation UnderlayOpenGLHostingWindow |
10 | 63 |
11 - (void)underlaySurfaceAdded { | 64 + (void)load { |
12 DCHECK_GE(underlaySurfaceCount_, 0); | 65 base::mac::ScopedNSAutoreleasePool pool; |
13 ++underlaySurfaceCount_; | |
14 | 66 |
15 // We're having the OpenGL surface render under the window, so the window | 67 // On 10.8+ the background for textured windows are no longer drawn by |
16 // needs to be not opaque. | 68 // NSGrayFrame, and NSThemeFrame is used instead <http://crbug.com/114745>. |
17 if (underlaySurfaceCount_ == 1) | 69 Class borderViewClass = NSClassFromString( |
18 [self setOpaque:NO]; | 70 base::mac::IsOSMountainLionOrLater() ? @"NSThemeFrame" : @"NSGrayFrame"); |
| 71 DCHECK(borderViewClass); |
| 72 if (!borderViewClass) return; |
| 73 |
| 74 // Install callback for added views. |
| 75 Method m = class_getInstanceMethod([NSView class], @selector(didAddSubview:)); |
| 76 DCHECK(m); |
| 77 if (m) { |
| 78 BOOL didAdd = class_addMethod(borderViewClass, |
| 79 @selector(didAddSubview:), |
| 80 reinterpret_cast<IMP>(&RootDidAddSubview), |
| 81 method_getTypeEncoding(m)); |
| 82 DCHECK(didAdd); |
| 83 } |
19 } | 84 } |
20 | 85 |
21 - (void)underlaySurfaceRemoved { | 86 - (id)initWithContentRect:(NSRect)contentRect |
22 --underlaySurfaceCount_; | 87 styleMask:(NSUInteger)windowStyle |
23 DCHECK_GE(underlaySurfaceCount_, 0); | 88 backing:(NSBackingStoreType)bufferingType |
| 89 defer:(BOOL)deferCreation { |
| 90 if ((self = [super initWithContentRect:contentRect |
| 91 styleMask:windowStyle |
| 92 backing:bufferingType |
| 93 defer:deferCreation])) { |
| 94 // The invisible opaque area technique only works > 10.5. Fortunately, hole |
| 95 // punching is used only when IOSurfaces are used to transport, and that's |
| 96 // also only on > 10.5. Also, don't mess around with things if it's not a |
| 97 // proper window with a title bar and all. |
| 98 if (base::mac::IsOSSnowLeopardOrLater() && |
| 99 windowStyle && NSTitledWindowMask) { |
| 100 [self setOpaque:NO]; |
| 101 [self _setContentHasShadow:NO]; |
24 | 102 |
25 if (underlaySurfaceCount_ == 0) | 103 NSView* rootView = [[self contentView] superview]; |
26 [self setOpaque:YES]; | 104 const NSRect rootBounds = [rootView bounds]; |
| 105 |
| 106 const CGFloat kEdgeInset = 16; |
| 107 const CGFloat kAlphaValueJustOpaqueEnough = 0.002; |
| 108 |
| 109 scoped_nsobject<NSView> leftOpaque([[OpaqueView alloc] initWithFrame: |
| 110 NSMakeRect(NSMinX(rootBounds), NSMinY(rootBounds) + kEdgeInset, |
| 111 1, NSHeight(rootBounds) - 2 * kEdgeInset)]); |
| 112 [leftOpaque setAutoresizingMask:NSViewMaxXMargin | NSViewHeightSizable]; |
| 113 [leftOpaque setAlphaValue:kAlphaValueJustOpaqueEnough]; |
| 114 [rootView addSubview:leftOpaque]; |
| 115 |
| 116 scoped_nsobject<NSView> rightOpaque([[OpaqueView alloc] initWithFrame: |
| 117 NSMakeRect(NSMaxX(rootBounds) - 1, NSMinY(rootBounds) + kEdgeInset, |
| 118 1, NSHeight(rootBounds) - 2 * kEdgeInset)]); |
| 119 [rightOpaque setAutoresizingMask:NSViewMinXMargin | NSViewHeightSizable]; |
| 120 [rightOpaque setAlphaValue:kAlphaValueJustOpaqueEnough]; |
| 121 [rootView addSubview:rightOpaque]; |
| 122 } |
| 123 } |
| 124 |
| 125 return self; |
27 } | 126 } |
28 | 127 |
29 @end | 128 @end |
OLD | NEW |