OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_con
troller.h" |
| 6 |
| 7 #include <map> |
| 8 |
| 9 #include "base/logging.h" |
| 10 #include "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_in
fo.h" |
| 11 #include "chrome/browser/ui/cocoa/constrained_window/constrained_window_animatio
n.h" |
| 12 |
| 13 namespace { |
| 14 |
| 15 // A list of all sheet controllers. |
| 16 NSMutableArray* g_SheetControllers; |
| 17 |
| 18 } // namespace |
| 19 |
| 20 // An invisible overlay window placed on top of the sheet's parent view. |
| 21 // This window blocks interaction with the underlying view. |
| 22 @interface CWSheetOverlayWindow : NSWindow { |
| 23 scoped_nsobject<ConstrainedWindowSheetController> controller_; |
| 24 } |
| 25 @end |
| 26 |
| 27 @interface ConstrainedWindowSheetController () |
| 28 - (id)initWithParentWindow:(NSWindow*)parentWindow; |
| 29 - (ConstrainedWindowSheetInfo*)findSheetInfoForParentView:(NSView*)parentView; |
| 30 - (ConstrainedWindowSheetInfo*)findSheetInfoForSheet:(NSWindow*)sheet; |
| 31 - (void)onParentWindowWillClose:(NSNotification*)note; |
| 32 - (void)onParentViewFrameDidChange:(NSNotification*)note; |
| 33 - (void)updateSheetPosition:(NSView*)parentView; |
| 34 - (NSRect)overlayWindowFrameForParentView:(NSView*)parentView; |
| 35 - (NSPoint)originForSheet:(NSWindow*)sheet |
| 36 inContainerRect:(NSRect)containerRect; |
| 37 - (void)onOveralyWindowMouseDown:(CWSheetOverlayWindow*)overlayWindow; |
| 38 - (void)animationDidEnd:(NSAnimation*)animation; |
| 39 - (void)closeSheetWithoutAnimation:(ConstrainedWindowSheetInfo*)info; |
| 40 @end |
| 41 |
| 42 @implementation CWSheetOverlayWindow |
| 43 |
| 44 - (id)initWithContentRect:(NSRect)rect |
| 45 controller:(ConstrainedWindowSheetController*)controller { |
| 46 if ((self = [super initWithContentRect:rect |
| 47 styleMask:NSBorderlessWindowMask |
| 48 backing:NSBackingStoreBuffered |
| 49 defer:NO])) { |
| 50 [self setOpaque:NO]; |
| 51 [self setBackgroundColor:[NSColor clearColor]]; |
| 52 [self setIgnoresMouseEvents:NO]; |
| 53 [self setReleasedWhenClosed:NO]; |
| 54 controller_.reset([controller retain]); |
| 55 } |
| 56 return self; |
| 57 } |
| 58 |
| 59 - (void)mouseDown:(NSEvent*)event { |
| 60 [controller_ onOveralyWindowMouseDown:self]; |
| 61 } |
| 62 |
| 63 @end |
| 64 |
| 65 @implementation ConstrainedWindowSheetController |
| 66 |
| 67 + (ConstrainedWindowSheetController*) |
| 68 controllerForParentWindow:(NSWindow*)parentWindow { |
| 69 DCHECK(parentWindow); |
| 70 for (ConstrainedWindowSheetController* controller in g_SheetControllers) { |
| 71 if ([parentWindow isEqual:controller->parentWindow_]) |
| 72 return controller; |
| 73 } |
| 74 |
| 75 scoped_nsobject<ConstrainedWindowSheetController> controller( |
| 76 [[ConstrainedWindowSheetController alloc] |
| 77 initWithParentWindow:parentWindow]); |
| 78 if (!g_SheetControllers) |
| 79 g_SheetControllers = [[NSMutableArray alloc] init]; |
| 80 [g_SheetControllers addObject:controller]; |
| 81 return controller; |
| 82 } |
| 83 |
| 84 + (ConstrainedWindowSheetController*)controllerForSheet:(NSWindow*)sheet { |
| 85 for (ConstrainedWindowSheetController* controller in g_SheetControllers) { |
| 86 if ([controller findSheetInfoForSheet:sheet]) |
| 87 return controller; |
| 88 } |
| 89 return nil; |
| 90 } |
| 91 |
| 92 - (void)showSheet:(NSWindow*)sheet |
| 93 forParentView:(NSView*)parentView { |
| 94 DCHECK(sheet); |
| 95 DCHECK(parentView); |
| 96 if (!activeView_.get()) |
| 97 activeView_.reset([parentView retain]); |
| 98 |
| 99 NSRect rect = [self overlayWindowFrameForParentView:parentView]; |
| 100 scoped_nsobject<NSWindow> overlayWindow( |
| 101 [[CWSheetOverlayWindow alloc] initWithContentRect:rect |
| 102 controller:self]); |
| 103 [sheet setFrameOrigin:[self originForSheet:sheet inContainerRect:rect]]; |
| 104 |
| 105 scoped_nsobject<ConstrainedWindowSheetInfo> info( |
| 106 [[ConstrainedWindowSheetInfo alloc] initWithSheet:sheet |
| 107 parentView:parentView |
| 108 overlayWindow:overlayWindow]); |
| 109 [sheets_ addObject:info]; |
| 110 if (![activeView_ isEqual:parentView]) { |
| 111 [info hideSheet]; |
| 112 } else { |
| 113 scoped_nsobject<NSAnimation> animation( |
| 114 [[ConstrainedWindowAnimationShow alloc] initWithWindow:sheet]); |
| 115 [info setAnimation:animation]; |
| 116 [animation startAnimation]; |
| 117 } |
| 118 |
| 119 [parentWindow_ addChildWindow:overlayWindow |
| 120 ordered:NSWindowAbove]; |
| 121 [overlayWindow addChildWindow:sheet |
| 122 ordered:NSWindowAbove]; |
| 123 |
| 124 [parentView setPostsFrameChangedNotifications:YES]; |
| 125 [[NSNotificationCenter defaultCenter] |
| 126 addObserver:self |
| 127 selector:@selector(onParentViewFrameDidChange:) |
| 128 name:NSViewFrameDidChangeNotification |
| 129 object:parentView]; |
| 130 } |
| 131 |
| 132 - (void)closeSheet:(NSWindow*)sheet { |
| 133 ConstrainedWindowSheetInfo* info = [self findSheetInfoForSheet:sheet]; |
| 134 DCHECK(info); |
| 135 |
| 136 if (![activeView_ isEqual:[info parentView]]) { |
| 137 [self closeSheetWithoutAnimation:info]; |
| 138 return; |
| 139 } |
| 140 |
| 141 scoped_nsobject<NSAnimation> animation( |
| 142 [[ConstrainedWindowAnimationHide alloc] initWithWindow:sheet]); |
| 143 [animation setDelegate:self]; |
| 144 [info setAnimation:animation]; |
| 145 [animation startAnimation]; |
| 146 } |
| 147 |
| 148 |
| 149 - (void)parentViewDidBecomeActive:(NSView*)parentView { |
| 150 [[self findSheetInfoForParentView:activeView_] hideSheet]; |
| 151 activeView_.reset([parentView retain]); |
| 152 [[self findSheetInfoForParentView:activeView_] showSheet]; |
| 153 } |
| 154 |
| 155 - (int)sheetCount { |
| 156 return [sheets_ count]; |
| 157 } |
| 158 |
| 159 - (void)endAnimationForSheet:(NSWindow*)sheet { |
| 160 [[[self findSheetInfoForSheet:sheet] animation] stopAnimation]; |
| 161 } |
| 162 |
| 163 - (id)initWithParentWindow:(NSWindow*)parentWindow { |
| 164 if ((self = [super init])) { |
| 165 parentWindow_.reset([parentWindow retain]); |
| 166 sheets_.reset([[NSMutableArray alloc] init]); |
| 167 |
| 168 [[NSNotificationCenter defaultCenter] |
| 169 addObserver:self |
| 170 selector:@selector(onParentWindowWillClose:) |
| 171 name:NSWindowWillCloseNotification |
| 172 object:parentWindow_]; |
| 173 } |
| 174 return self; |
| 175 } |
| 176 |
| 177 - (ConstrainedWindowSheetInfo*)findSheetInfoForParentView:(NSView*)parentView { |
| 178 for (ConstrainedWindowSheetInfo* info in sheets_.get()) { |
| 179 if ([parentView isEqual:[info parentView]]) |
| 180 return info; |
| 181 } |
| 182 return NULL; |
| 183 } |
| 184 |
| 185 - (ConstrainedWindowSheetInfo*)findSheetInfoForSheet:(NSWindow*)sheet { |
| 186 for (ConstrainedWindowSheetInfo* info in sheets_.get()) { |
| 187 if ([sheet isEqual:[info sheet]]) |
| 188 return info; |
| 189 } |
| 190 return NULL; |
| 191 } |
| 192 |
| 193 - (void)onParentWindowWillClose:(NSNotification*)note { |
| 194 [[NSNotificationCenter defaultCenter] |
| 195 removeObserver:self |
| 196 name:NSWindowWillCloseNotification |
| 197 object:parentWindow_]; |
| 198 |
| 199 // Close all sheets. |
| 200 NSArray* sheets = [NSArray arrayWithArray:sheets_]; |
| 201 for (ConstrainedWindowSheetInfo* info in sheets) |
| 202 [self closeSheetWithoutAnimation:info]; |
| 203 |
| 204 // Delete this instance. |
| 205 [g_SheetControllers removeObject:self]; |
| 206 if (![g_SheetControllers count]) { |
| 207 [g_SheetControllers release]; |
| 208 g_SheetControllers = nil; |
| 209 } |
| 210 } |
| 211 |
| 212 - (void)onParentViewFrameDidChange:(NSNotification*)note { |
| 213 [self updateSheetPosition:[note object]]; |
| 214 } |
| 215 |
| 216 - (void)updateSheetPosition:(NSView*)parentView { |
| 217 if (![activeView_ isEqual:parentView]) |
| 218 return; |
| 219 ConstrainedWindowSheetInfo* info = |
| 220 [self findSheetInfoForParentView:parentView]; |
| 221 DCHECK(info); |
| 222 NSRect rect = [self overlayWindowFrameForParentView:parentView]; |
| 223 [[info overlayWindow] setFrame:rect display:YES]; |
| 224 [[info sheet] setFrameOrigin:[self originForSheet:[info sheet] |
| 225 inContainerRect:rect]]; |
| 226 } |
| 227 |
| 228 - (NSRect)overlayWindowFrameForParentView:(NSView*)parentView { |
| 229 NSRect viewFrame = [parentView frame]; |
| 230 viewFrame = [[parentView superview] convertRect:viewFrame toView:nil]; |
| 231 viewFrame.origin = [[parentView window] convertBaseToScreen:viewFrame.origin]; |
| 232 return viewFrame; |
| 233 } |
| 234 |
| 235 - (NSPoint)originForSheet:(NSWindow*)sheet |
| 236 inContainerRect:(NSRect)containerRect { |
| 237 NSSize sheetSize = [sheet frame].size; |
| 238 NSPoint origin; |
| 239 origin.x = |
| 240 NSMinX(containerRect) + (NSWidth(containerRect) - sheetSize.width) / 2.0; |
| 241 origin.y = NSMaxY(containerRect) + 5 - sheetSize.height; |
| 242 return origin; |
| 243 } |
| 244 |
| 245 - (void)onOveralyWindowMouseDown:(CWSheetOverlayWindow*)overlayWindow { |
| 246 ConstrainedWindowSheetInfo* info = nil; |
| 247 for (ConstrainedWindowSheetInfo* curInfo in sheets_.get()) { |
| 248 if ([overlayWindow isEqual:[curInfo overlayWindow]]) { |
| 249 info = curInfo; |
| 250 break; |
| 251 } |
| 252 } |
| 253 DCHECK(info); |
| 254 if ([[info animation] isAnimating]) |
| 255 return; |
| 256 |
| 257 scoped_nsobject<NSAnimation> animation( |
| 258 [[ConstrainedWindowAnimationPulse alloc] initWithWindow:[info sheet]]); |
| 259 [info setAnimation:animation]; |
| 260 [animation startAnimation]; |
| 261 } |
| 262 |
| 263 - (void)animationDidEnd:(NSAnimation*)animation { |
| 264 ConstrainedWindowSheetInfo* info = nil; |
| 265 for (ConstrainedWindowSheetInfo* curInfo in sheets_.get()) { |
| 266 if ([animation isEqual:[curInfo animation]]) { |
| 267 info = curInfo; |
| 268 break; |
| 269 } |
| 270 } |
| 271 DCHECK(info); |
| 272 |
| 273 // To avoid reentrancy close the sheet in the next event cycle. |
| 274 [self performSelector:@selector(closeSheetWithoutAnimation:) |
| 275 withObject:info |
| 276 afterDelay:0]; |
| 277 } |
| 278 |
| 279 - (void)closeSheetWithoutAnimation:(ConstrainedWindowSheetInfo*)info { |
| 280 if (![sheets_ containsObject:info]) |
| 281 return; |
| 282 |
| 283 [[NSNotificationCenter defaultCenter] |
| 284 removeObserver:self |
| 285 name:NSViewFrameDidChangeNotification |
| 286 object:[info parentView]]; |
| 287 |
| 288 [[info animation] stopAnimation]; |
| 289 [parentWindow_ removeChildWindow:[info overlayWindow]]; |
| 290 [[info overlayWindow] removeChildWindow:[info sheet]]; |
| 291 [[info sheet] close]; |
| 292 [[info overlayWindow] close]; |
| 293 [sheets_ removeObject:info]; |
| 294 } |
| 295 |
| 296 @end |
OLD | NEW |