Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 #include "chrome/browser/cocoa/tab_view.h" | 5 #import "chrome/browser/cocoa/tab_view.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | |
| 7 #include "chrome/browser/cocoa/nsimage_cache.h" | 8 #include "chrome/browser/cocoa/nsimage_cache.h" |
| 8 #include "chrome/browser/cocoa/tab_controller.h" | 9 #import "chrome/browser/cocoa/tab_controller.h" |
| 9 #include "chrome/browser/cocoa/tab_window_controller.h" | 10 #import "chrome/browser/cocoa/tab_window_controller.h" |
| 10 | 11 |
| 11 // Constants for inset and control points for tab shape. | 12 // Constants for inset and control points for tab shape. |
| 12 static const CGFloat kInsetMultiplier = 2.0/3.0; | 13 static const CGFloat kInsetMultiplier = 2.0/3.0; |
| 13 static const CGFloat kControlPoint1Multiplier = 1.0/3.0; | 14 static const CGFloat kControlPoint1Multiplier = 1.0/3.0; |
| 14 static const CGFloat kControlPoint2Multiplier = 3.0/8.0; | 15 static const CGFloat kControlPoint2Multiplier = 3.0/8.0; |
| 15 | 16 |
| 16 static const NSTimeInterval kAnimationShowDuration = 0.2; | 17 static const NSTimeInterval kAnimationShowDuration = 0.2; |
| 17 static const NSTimeInterval kAnimationHideDuration = 0.4; | 18 static const NSTimeInterval kAnimationHideDuration = 0.4; |
| 18 | 19 |
| 19 @implementation TabView | 20 @implementation TabView |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 135 - (BOOL)canBeDragged { | 136 - (BOOL)canBeDragged { |
| 136 NSWindowController *controller = [sourceWindow_ windowController]; | 137 NSWindowController *controller = [sourceWindow_ windowController]; |
| 137 if ([controller isKindOfClass:[TabWindowController class]]) { | 138 if ([controller isKindOfClass:[TabWindowController class]]) { |
| 138 TabWindowController* realController = | 139 TabWindowController* realController = |
| 139 static_cast<TabWindowController*>(controller); | 140 static_cast<TabWindowController*>(controller); |
| 140 return [realController isTabDraggable:self]; | 141 return [realController isTabDraggable:self]; |
| 141 } | 142 } |
| 142 return YES; | 143 return YES; |
| 143 } | 144 } |
| 144 | 145 |
| 146 // Find all the windows that could be a target. It has to be of the | |
| 147 // appropriate class, and visible (obviously). Note that the window cannot be | |
| 148 // a target for itself. | |
| 149 - (NSArray*)dropTargetsForController:(TabWindowController*)dragController { | |
| 150 NSMutableArray* targets = [NSMutableArray array]; | |
| 151 NSWindow* dragWindow = [dragController window]; | |
| 152 for (NSWindow* window in [NSApp windows]) { | |
| 153 if (window == dragWindow) continue; | |
| 154 if (![window isVisible]) continue; | |
| 155 NSWindowController *controller = [window windowController]; | |
| 156 if ([controller isKindOfClass:[TabWindowController class]]) { | |
| 157 TabWindowController* realController = | |
| 158 static_cast<TabWindowController*>(controller); | |
| 159 if ([realController canReceiveFrom:dragController]) { | |
| 160 [targets addObject:controller]; | |
| 161 } | |
| 162 } | |
| 163 } | |
| 164 return targets; | |
| 165 } | |
| 166 | |
| 145 // Handle clicks and drags in this button. We get here because we have | 167 // Handle clicks and drags in this button. We get here because we have |
| 146 // overridden acceptsFirstMouse: and the click is within our bounds. | 168 // overridden acceptsFirstMouse: and the click is within our bounds. |
| 147 // TODO(pinkerton/alcor): This routine needs *a lot* of work to marry Cole's | 169 // TODO(pinkerton/alcor): This routine needs *a lot* of work to marry Cole's |
| 148 // ideas of dragging cocoa views between windows and how the Browser and | 170 // ideas of dragging cocoa views between windows and how the Browser and |
| 149 // TabStrip models want to manage tabs. | 171 // TabStrip models want to manage tabs. |
| 150 | 172 |
| 151 static const CGFloat kTearDistance = 36.0; | 173 static const CGFloat kTearDistance = 36.0; |
| 152 static const NSTimeInterval kTearDuration = 0.333; | 174 static const NSTimeInterval kTearDuration = 0.333; |
| 153 | 175 |
| 154 // This is used to judge whether the mouse has moved during rapid closure; if it | 176 // This is used to judge whether the mouse has moved during rapid closure; if it |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 186 sourceTabFrame_ = [self frame]; | 208 sourceTabFrame_ = [self frame]; |
| 187 sourceController_ = [sourceWindow_ windowController]; | 209 sourceController_ = [sourceWindow_ windowController]; |
| 188 draggedController_ = nil; | 210 draggedController_ = nil; |
| 189 dragWindow_ = nil; | 211 dragWindow_ = nil; |
| 190 dragOverlay_ = nil; | 212 dragOverlay_ = nil; |
| 191 targetController_ = nil; | 213 targetController_ = nil; |
| 192 tabWasDragged_ = NO; | 214 tabWasDragged_ = NO; |
| 193 tearTime_ = 0.0; | 215 tearTime_ = 0.0; |
| 194 draggingWithinTabStrip_ = YES; | 216 draggingWithinTabStrip_ = YES; |
| 195 | 217 |
| 196 // We don't want to "tear off" a tab if there's only one in the window. Treat | 218 // If there's more than one potential window to be a drop target, we want to |
| 197 // it like we're dragging around a tab we've already detached. Note that | 219 // treat a drag of a tab just like dragging around a tab that's already |
| 198 // unit tests might have |-numberOfTabs| reporting zero since the model | 220 // detached. Note that unit tests might have |-numberOfTabs| reporting zero |
| 199 // won't be fully hooked up. We need to be prepared for that and not send | 221 // since the model won't be fully hooked up. We need to be prepared for that |
| 200 // them into the "magnetic" codepath. | 222 // and not send them into the "magnetic" codepath. |
| 223 NSArray* targets = [self dropTargetsForController:sourceController_]; | |
| 201 moveWindowOnDrag_ = | 224 moveWindowOnDrag_ = |
| 202 [sourceController_ numberOfTabs] <= 1 || ![self canBeDragged]; | 225 ([sourceController_ numberOfTabs] < 2 && ![targets count]) || |
| 226 ![self canBeDragged]; | |
|
rohitrao (ping after 24h)
2009/09/10 19:03:56
Should this be in line with the "(" above?
pink (ping after 24hrs)
2009/09/10 19:05:55
No, the first two should bind together, or'd with
pink (ping after 24hrs)
2009/09/10 19:06:31
Oops, my bad, yes. Misunderstood.
| |
| 227 // If we are dragging a tab, a window with a single tab should immediately | |
| 228 // snap off and not drag within the tab strip. | |
| 229 if (!moveWindowOnDrag_) | |
| 230 draggingWithinTabStrip_ = [sourceController_ numberOfTabs] > 1; | |
| 203 | 231 |
| 204 dragOrigin_ = [NSEvent mouseLocation]; | 232 dragOrigin_ = [NSEvent mouseLocation]; |
| 205 | 233 |
| 206 // Because we move views between windows, we need to handle the event loop | 234 // Because we move views between windows, we need to handle the event loop |
| 207 // ourselves. Ideally we should use the standard event loop. | 235 // ourselves. Ideally we should use the standard event loop. |
| 208 while (1) { | 236 while (1) { |
| 209 theEvent = | 237 theEvent = |
| 210 [NSApp nextEventMatchingMask:NSLeftMouseUpMask | NSLeftMouseDraggedMask | 238 [NSApp nextEventMatchingMask:NSLeftMouseUpMask | NSLeftMouseDraggedMask |
| 211 untilDate:[NSDate distantFuture] | 239 untilDate:[NSDate distantFuture] |
| 212 inMode:NSDefaultRunLoopMode dequeue:YES]; | 240 inMode:NSDefaultRunLoopMode dequeue:YES]; |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 279 } else { | 307 } else { |
| 280 return; | 308 return; |
| 281 } | 309 } |
| 282 } | 310 } |
| 283 | 311 |
| 284 NSPoint lastPoint = | 312 NSPoint lastPoint = |
| 285 [[theEvent window] convertBaseToScreen:[theEvent locationInWindow]]; | 313 [[theEvent window] convertBaseToScreen:[theEvent locationInWindow]]; |
| 286 | 314 |
| 287 // Do not start dragging until the user has "torn" the tab off by | 315 // Do not start dragging until the user has "torn" the tab off by |
| 288 // moving more than 3 pixels. | 316 // moving more than 3 pixels. |
| 289 NSDate* targetDwellDate = nil; // The date this target was first chosen | 317 NSDate* targetDwellDate = nil; // The date this target was first chosen. |
| 290 NSMutableArray* targets = [NSMutableArray array]; | |
| 291 | 318 |
| 292 NSPoint thisPoint = [NSEvent mouseLocation]; | 319 NSPoint thisPoint = [NSEvent mouseLocation]; |
| 293 | 320 |
| 294 // Find all the windows that could be a target. It has to be of the | |
| 295 // appropriate class, and visible (obviously). | |
| 296 if (![targets count]) { | |
| 297 for (NSWindow* window in [NSApp windows]) { | |
| 298 if (window == dragWindow_) continue; | |
| 299 if (![window isVisible]) continue; | |
| 300 NSWindowController *controller = [window windowController]; | |
| 301 if ([controller isKindOfClass:[TabWindowController class]]) { | |
| 302 TabWindowController* realController = | |
| 303 static_cast<TabWindowController*>(controller); | |
| 304 if ([realController canReceiveFrom:sourceController_]) { | |
| 305 [targets addObject:controller]; | |
| 306 } | |
| 307 } | |
| 308 } | |
| 309 } | |
| 310 | |
| 311 // Iterate over possible targets checking for the one the mouse is in. | 321 // Iterate over possible targets checking for the one the mouse is in. |
| 312 // The mouse can be in either the tab or window frame. | 322 // The mouse can be in either the tab or window frame. |
| 323 NSArray* targets = [self dropTargetsForController:draggedController_]; | |
| 313 TabWindowController* newTarget = nil; | 324 TabWindowController* newTarget = nil; |
| 314 for (TabWindowController* target in targets) { | 325 for (TabWindowController* target in targets) { |
| 315 NSRect windowFrame = [[target window] frame]; | 326 NSRect windowFrame = [[target window] frame]; |
| 316 if (NSPointInRect(thisPoint, windowFrame)) { | 327 if (NSPointInRect(thisPoint, windowFrame)) { |
| 317 NSRect tabStripFrame = [[target tabStripView] frame]; | 328 NSRect tabStripFrame = [[target tabStripView] frame]; |
| 318 tabStripFrame.origin = [[target window] | 329 tabStripFrame.origin = [[target window] |
| 319 convertBaseToScreen:tabStripFrame.origin]; | 330 convertBaseToScreen:tabStripFrame.origin]; |
| 320 if (NSPointInRect(thisPoint, tabStripFrame)) { | 331 if (NSPointInRect(thisPoint, tabStripFrame)) { |
| 321 newTarget = target; | 332 newTarget = target; |
| 322 } | 333 } |
| 323 break; | 334 break; |
| 324 } | 335 } |
| 325 } | 336 } |
| 326 | 337 |
| 327 // If we're now targeting a new window, re-layout the tabs in the old | 338 // If we're now targeting a new window, re-layout the tabs in the old |
| 328 // target and reset how long we've been hovering over this new one. | 339 // target and reset how long we've been hovering over this new one. |
| 329 if (targetController_ != newTarget) { | 340 if (targetController_ != newTarget) { |
| 330 targetDwellDate = [NSDate date]; | 341 targetDwellDate = [NSDate date]; |
| 331 [targetController_ removePlaceholder]; | 342 [targetController_ removePlaceholder]; |
| 332 targetController_ = newTarget; | 343 targetController_ = newTarget; |
| 333 if (!newTarget) { | 344 if (!newTarget) { |
| 334 tearTime_ = [NSDate timeIntervalSinceReferenceDate]; | 345 tearTime_ = [NSDate timeIntervalSinceReferenceDate]; |
| 335 tearOrigin_ = [dragWindow_ frame].origin; | 346 tearOrigin_ = [dragWindow_ frame].origin; |
| 336 } | 347 } |
| 337 } | 348 } |
| 338 | 349 |
| 339 // Create or identify the dragged controller. | 350 // Create or identify the dragged controller. |
| 340 if (!draggedController_) { | 351 if (!draggedController_) { |
| 341 // Detach from the current window and put it in a new window. | 352 // Detach from the current window and put it in a new window. If there are |
| 353 // no more tabs remaining after detaching, the source window is about to | |
| 354 // go away (it's been autoreleased) so we need to ensure we don't reference | |
| 355 // it any more. In that case the new controller becomes our source | |
| 356 // controller. | |
| 342 draggedController_ = [sourceController_ detachTabToNewWindow:self]; | 357 draggedController_ = [sourceController_ detachTabToNewWindow:self]; |
| 343 dragWindow_ = [draggedController_ window]; | 358 dragWindow_ = [draggedController_ window]; |
| 344 [dragWindow_ setAlphaValue:0.0]; | 359 [dragWindow_ setAlphaValue:0.0]; |
| 360 if (![sourceController_ numberOfTabs]) { | |
| 361 sourceController_ = draggedController_; | |
| 362 sourceWindow_ = dragWindow_; | |
| 363 } | |
| 345 | 364 |
| 346 // If dragging the tab only moves the current window, do not show overlay | 365 // If dragging the tab only moves the current window, do not show overlay |
| 347 // so that sheets stay on top of the window. | 366 // so that sheets stay on top of the window. |
| 348 // Bring the target window to the front and make sure it has a border. | 367 // Bring the target window to the front and make sure it has a border. |
| 349 [dragWindow_ setLevel:NSFloatingWindowLevel]; | 368 [dragWindow_ setLevel:NSFloatingWindowLevel]; |
| 350 [dragWindow_ orderFront:nil]; | 369 [dragWindow_ orderFront:nil]; |
| 351 [dragWindow_ makeMainWindow]; | 370 [dragWindow_ makeMainWindow]; |
| 352 [draggedController_ showOverlay]; | 371 [draggedController_ showOverlay]; |
| 353 dragOverlay_ = [draggedController_ overlayWindow]; | 372 dragOverlay_ = [draggedController_ overlayWindow]; |
| 354 // Force the new tab button to be hidden. We'll reset it on mouse up. | 373 // Force the new tab button to be hidden. We'll reset it on mouse up. |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 391 } | 410 } |
| 392 [dragWindow_ setFrameOrigin:NSMakePoint(origin.x, origin.y)]; | 411 [dragWindow_ setFrameOrigin:NSMakePoint(origin.x, origin.y)]; |
| 393 | 412 |
| 394 // If we're not hovering over any window, make the window is fully | 413 // If we're not hovering over any window, make the window is fully |
| 395 // opaque. Otherwise, find where the tab might be dropped and insert | 414 // opaque. Otherwise, find where the tab might be dropped and insert |
| 396 // a placeholder so it appears like it's part of that window. | 415 // a placeholder so it appears like it's part of that window. |
| 397 if (targetController_) { | 416 if (targetController_) { |
| 398 if (![[targetController_ window] isKeyWindow]) { | 417 if (![[targetController_ window] isKeyWindow]) { |
| 399 // && ([targetDwellDate timeIntervalSinceNow] < -REQUIRED_DWELL)) { | 418 // && ([targetDwellDate timeIntervalSinceNow] < -REQUIRED_DWELL)) { |
| 400 [[targetController_ window] orderFront:nil]; | 419 [[targetController_ window] orderFront:nil]; |
| 401 [targets removeAllObjects]; | |
|
rohitrao (ping after 24h)
2009/09/10 19:03:56
This is ok to delete because targets is never refe
pink (ping after 24hrs)
2009/09/10 19:05:55
Correct. It must have been leftover from something
| |
| 402 targetDwellDate = nil; | 420 targetDwellDate = nil; |
| 403 } | 421 } |
| 404 | 422 |
| 405 // Compute where placeholder should go and insert it into the | 423 // Compute where placeholder should go and insert it into the |
| 406 // destination tab strip. | 424 // destination tab strip. |
| 407 NSRect dropTabFrame = [[targetController_ tabStripView] frame]; | 425 NSRect dropTabFrame = [[targetController_ tabStripView] frame]; |
| 408 TabView *draggedTabView = (TabView *)[draggedController_ selectedTabView]; | 426 TabView *draggedTabView = (TabView *)[draggedController_ selectedTabView]; |
| 409 NSRect tabFrame = [draggedTabView frame]; | 427 NSRect tabFrame = [draggedTabView frame]; |
| 410 tabFrame.origin = [dragWindow_ convertBaseToScreen:tabFrame.origin]; | 428 tabFrame.origin = [dragWindow_ convertBaseToScreen:tabFrame.origin]; |
| 411 tabFrame.origin = [[targetController_ window] | 429 tabFrame.origin = [[targetController_ window] |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 451 return; | 469 return; |
| 452 | 470 |
| 453 // We are now free to re-display the new tab button in the window we're | 471 // We are now free to re-display the new tab button in the window we're |
| 454 // dragging. It will show when the next call to -layoutTabs (which happens | 472 // dragging. It will show when the next call to -layoutTabs (which happens |
| 455 // indrectly by several of the calls below, such as removing the placeholder). | 473 // indrectly by several of the calls below, such as removing the placeholder). |
| 456 [draggedController_ showNewTabButton:YES]; | 474 [draggedController_ showNewTabButton:YES]; |
| 457 | 475 |
| 458 if (draggingWithinTabStrip_) { | 476 if (draggingWithinTabStrip_) { |
| 459 if (tabWasDragged_) { | 477 if (tabWasDragged_) { |
| 460 // Move tab to new location. | 478 // Move tab to new location. |
| 479 DCHECK([sourceController_ numberOfTabs]); | |
| 461 TabWindowController* dropController = sourceController_; | 480 TabWindowController* dropController = sourceController_; |
| 462 [dropController moveTabView:[dropController selectedTabView] | 481 [dropController moveTabView:[dropController selectedTabView] |
| 463 fromController:nil]; | 482 fromController:nil]; |
| 464 } | 483 } |
| 465 } else if (targetController_) { | 484 } else if (targetController_) { |
| 466 // Move between windows. If |targetController_| is nil, we're not dropping | 485 // Move between windows. If |targetController_| is nil, we're not dropping |
| 467 // into any existing window. | 486 // into any existing window. |
| 468 NSView* draggedTabView = [draggedController_ selectedTabView]; | 487 NSView* draggedTabView = [draggedController_ selectedTabView]; |
| 469 [draggedController_ removeOverlay]; | 488 [draggedController_ removeOverlay]; |
| 470 [targetController_ moveTabView:draggedTabView | 489 [targetController_ moveTabView:draggedTabView |
| (...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 652 [[NSGraphicsContext currentContext] restoreGraphicsState]; | 671 [[NSGraphicsContext currentContext] restoreGraphicsState]; |
| 653 } | 672 } |
| 654 | 673 |
| 655 // Called when the user hits the right mouse button (or control-clicks) to | 674 // Called when the user hits the right mouse button (or control-clicks) to |
| 656 // show a context menu. | 675 // show a context menu. |
| 657 - (void)rightMouseDown:(NSEvent*)theEvent { | 676 - (void)rightMouseDown:(NSEvent*)theEvent { |
| 658 [NSMenu popUpContextMenu:[self menu] withEvent:theEvent forView:self]; | 677 [NSMenu popUpContextMenu:[self menu] withEvent:theEvent forView:self]; |
| 659 } | 678 } |
| 660 | 679 |
| 661 @end | 680 @end |
| OLD | NEW |