| 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 #import "chrome/browser/cocoa/tab_view.h" | 5 #import "chrome/browser/cocoa/tab_view.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "chrome/browser/cocoa/nsimage_cache.h" | 8 #include "chrome/browser/cocoa/nsimage_cache.h" |
| 9 #import "chrome/browser/cocoa/tab_controller.h" | 9 #import "chrome/browser/cocoa/tab_controller.h" |
| 10 #import "chrome/browser/cocoa/tab_window_controller.h" | 10 #import "chrome/browser/cocoa/tab_window_controller.h" |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 42 closeTrackingArea_.reset( | 42 closeTrackingArea_.reset( |
| 43 [[NSTrackingArea alloc] initWithRect:[closeButton_ bounds] | 43 [[NSTrackingArea alloc] initWithRect:[closeButton_ bounds] |
| 44 options:NSTrackingMouseEnteredAndExited | | 44 options:NSTrackingMouseEnteredAndExited | |
| 45 NSTrackingActiveAlways | 45 NSTrackingActiveAlways |
| 46 owner:self | 46 owner:self |
| 47 userInfo:nil]); | 47 userInfo:nil]); |
| 48 [closeButton_ addTrackingArea:closeTrackingArea_.get()]; | 48 [closeButton_ addTrackingArea:closeTrackingArea_.get()]; |
| 49 } | 49 } |
| 50 | 50 |
| 51 - (void)dealloc { | 51 - (void)dealloc { |
| 52 // Cancel any delayed requests that may still be pending (drags or hover). |
| 53 [NSObject cancelPreviousPerformRequestsWithTarget:self]; |
| 52 // [self gtm_unregisterForThemeNotifications]; | 54 // [self gtm_unregisterForThemeNotifications]; |
| 53 [closeButton_ removeTrackingArea:closeTrackingArea_.get()]; | 55 [closeButton_ removeTrackingArea:closeTrackingArea_.get()]; |
| 54 [super dealloc]; | 56 [super dealloc]; |
| 55 } | 57 } |
| 56 | 58 |
| 57 // Overridden so that mouse clicks come to this view (the parent of the | 59 // Overridden so that mouse clicks come to this view (the parent of the |
| 58 // hierarchy) first. We want to handle clicks and drags in this class and | 60 // hierarchy) first. We want to handle clicks and drags in this class and |
| 59 // leave the background button for display purposes only. | 61 // leave the background button for display purposes only. |
| 60 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { | 62 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { |
| 61 return YES; | 63 return YES; |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 157 TabWindowController* realController = | 159 TabWindowController* realController = |
| 158 static_cast<TabWindowController*>(controller); | 160 static_cast<TabWindowController*>(controller); |
| 159 if ([realController canReceiveFrom:dragController]) { | 161 if ([realController canReceiveFrom:dragController]) { |
| 160 [targets addObject:controller]; | 162 [targets addObject:controller]; |
| 161 } | 163 } |
| 162 } | 164 } |
| 163 } | 165 } |
| 164 return targets; | 166 return targets; |
| 165 } | 167 } |
| 166 | 168 |
| 169 // Call to clear out transient weak references we hold during drags. |
| 170 - (void)resetDragControllers { |
| 171 draggedController_ = nil; |
| 172 dragWindow_ = nil; |
| 173 dragOverlay_ = nil; |
| 174 sourceController_ = nil; |
| 175 sourceWindow_ = nil; |
| 176 targetController_ = nil; |
| 177 } |
| 178 |
| 167 // Handle clicks and drags in this button. We get here because we have | 179 // Handle clicks and drags in this button. We get here because we have |
| 168 // overridden acceptsFirstMouse: and the click is within our bounds. | 180 // overridden acceptsFirstMouse: and the click is within our bounds. |
| 169 // TODO(pinkerton/alcor): This routine needs *a lot* of work to marry Cole's | 181 // TODO(pinkerton/alcor): This routine needs *a lot* of work to marry Cole's |
| 170 // ideas of dragging cocoa views between windows and how the Browser and | 182 // ideas of dragging cocoa views between windows and how the Browser and |
| 171 // TabStrip models want to manage tabs. | 183 // TabStrip models want to manage tabs. |
| 172 | 184 |
| 173 static const CGFloat kTearDistance = 36.0; | 185 static const CGFloat kTearDistance = 36.0; |
| 174 static const NSTimeInterval kTearDuration = 0.333; | 186 static const NSTimeInterval kTearDuration = 0.333; |
| 175 | 187 |
| 176 // This is used to judge whether the mouse has moved during rapid closure; if it | 188 // This is used to judge whether the mouse has moved during rapid closure; if it |
| (...skipping 14 matching lines...) Expand all Loading... |
| 191 [closeButton_ mouseDown:theEvent]; | 203 [closeButton_ mouseDown:theEvent]; |
| 192 return; | 204 return; |
| 193 } | 205 } |
| 194 } | 206 } |
| 195 | 207 |
| 196 // Fire the action to select the tab. | 208 // Fire the action to select the tab. |
| 197 if ([[controller_ target] respondsToSelector:[controller_ action]]) | 209 if ([[controller_ target] respondsToSelector:[controller_ action]]) |
| 198 [[controller_ target] performSelector:[controller_ action] | 210 [[controller_ target] performSelector:[controller_ action] |
| 199 withObject:self]; | 211 withObject:self]; |
| 200 | 212 |
| 213 [self resetDragControllers]; |
| 214 |
| 201 // Resolve overlay back to original window. | 215 // Resolve overlay back to original window. |
| 202 sourceWindow_ = [self window]; | 216 sourceWindow_ = [self window]; |
| 203 if ([sourceWindow_ isKindOfClass:[NSPanel class]]) { | 217 if ([sourceWindow_ isKindOfClass:[NSPanel class]]) { |
| 204 sourceWindow_ = [sourceWindow_ parentWindow]; | 218 sourceWindow_ = [sourceWindow_ parentWindow]; |
| 205 } | 219 } |
| 206 | 220 |
| 207 sourceWindowFrame_ = [sourceWindow_ frame]; | 221 sourceWindowFrame_ = [sourceWindow_ frame]; |
| 208 sourceTabFrame_ = [self frame]; | 222 sourceTabFrame_ = [self frame]; |
| 209 sourceController_ = [sourceWindow_ windowController]; | 223 sourceController_ = [sourceWindow_ windowController]; |
| 210 draggedController_ = nil; | |
| 211 dragWindow_ = nil; | |
| 212 dragOverlay_ = nil; | |
| 213 targetController_ = nil; | |
| 214 tabWasDragged_ = NO; | 224 tabWasDragged_ = NO; |
| 215 tearTime_ = 0.0; | 225 tearTime_ = 0.0; |
| 216 draggingWithinTabStrip_ = YES; | 226 draggingWithinTabStrip_ = YES; |
| 217 | 227 |
| 218 // If there's more than one potential window to be a drop target, we want to | 228 // If there's more than one potential window to be a drop target, we want to |
| 219 // treat a drag of a tab just like dragging around a tab that's already | 229 // treat a drag of a tab just like dragging around a tab that's already |
| 220 // detached. Note that unit tests might have |-numberOfTabs| reporting zero | 230 // detached. Note that unit tests might have |-numberOfTabs| reporting zero |
| 221 // since the model won't be fully hooked up. We need to be prepared for that | 231 // since the model won't be fully hooked up. We need to be prepared for that |
| 222 // and not send them into the "magnetic" codepath. | 232 // and not send them into the "magnetic" codepath. |
| 223 NSArray* targets = [self dropTargetsForController:sourceController_]; | 233 NSArray* targets = [self dropTargetsForController:sourceController_]; |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 371 [draggedController_ showOverlay]; | 381 [draggedController_ showOverlay]; |
| 372 dragOverlay_ = [draggedController_ overlayWindow]; | 382 dragOverlay_ = [draggedController_ overlayWindow]; |
| 373 // Force the new tab button to be hidden. We'll reset it on mouse up. | 383 // Force the new tab button to be hidden. We'll reset it on mouse up. |
| 374 [draggedController_ showNewTabButton:NO]; | 384 [draggedController_ showNewTabButton:NO]; |
| 375 //if (![targets count]) | 385 //if (![targets count]) |
| 376 // [dragOverlay_ setHasShadow:NO]; | 386 // [dragOverlay_ setHasShadow:NO]; |
| 377 tearTime_ = [NSDate timeIntervalSinceReferenceDate]; | 387 tearTime_ = [NSDate timeIntervalSinceReferenceDate]; |
| 378 tearOrigin_ = sourceWindowFrame_.origin; | 388 tearOrigin_ = sourceWindowFrame_.origin; |
| 379 } | 389 } |
| 380 | 390 |
| 391 DCHECK(draggedController_); |
| 392 DCHECK(sourceController_); |
| 393 |
| 394 // When the user first tears off the window, we want slide the window to |
| 395 // the current mouse location (to reduce the jarring appearance). We do this |
| 396 // by calling ourselves back with additional mouseDragged calls (not actual |
| 397 // events). |tearProgress| is a normalized measure of how far through this |
| 398 // tear "animation" (of length kTearDuration) we are and has values [0..1]. |
| 399 // We use sqrt() so the animation is non-linear (slow down near the end |
| 400 // point). |
| 381 float tearProgress = [NSDate timeIntervalSinceReferenceDate] - tearTime_; | 401 float tearProgress = [NSDate timeIntervalSinceReferenceDate] - tearTime_; |
| 382 tearProgress /= kTearDuration; | 402 tearProgress /= kTearDuration; // Normalize. |
| 383 tearProgress = sqrtf(MAX(MIN(tearProgress, 1.0), 0.0)); | 403 tearProgress = sqrtf(MAX(MIN(tearProgress, 1.0), 0.0)); |
| 384 | 404 |
| 385 // Move the dragged window to the right place on the screen. | 405 // Move the dragged window to the right place on the screen. |
| 386 NSPoint origin = sourceWindowFrame_.origin; | 406 NSPoint origin = sourceWindowFrame_.origin; |
| 387 origin.x += (thisPoint.x - dragOrigin_.x); | 407 origin.x += (thisPoint.x - dragOrigin_.x); |
| 388 origin.y += (thisPoint.y - dragOrigin_.y); | 408 origin.y += (thisPoint.y - dragOrigin_.y); |
| 389 | 409 |
| 390 if (tearProgress < 1) { | 410 if (tearProgress < 1) { |
| 391 // If the tear animation is not complete, call back to ourself with the | 411 // If the tear animation is not complete, call back to ourself with the |
| 392 // same event to animate even if the mouse isn't moving. | 412 // same event to animate even if the mouse isn't moving. We need to make |
| 413 // sure these get cancelled in mouseUp:. |
| 393 [NSObject cancelPreviousPerformRequestsWithTarget:self]; | 414 [NSObject cancelPreviousPerformRequestsWithTarget:self]; |
| 394 [self performSelector:@selector(mouseDragged:) | 415 [self performSelector:@selector(mouseDragged:) |
| 395 withObject:theEvent | 416 withObject:theEvent |
| 396 afterDelay:1.0f/30.0f]; | 417 afterDelay:1.0f/30.0f]; |
| 397 | 418 |
| 398 origin.x = (1 - tearProgress) * tearOrigin_.x + tearProgress * origin.x; | 419 // Set the current window origin based on how far we've progressed through |
| 420 // the tear animation. |
| 421 origin.x = (1 - tearProgress) * tearOrigin_.x + tearProgress * origin.x; |
| 399 origin.y = (1 - tearProgress) * tearOrigin_.y + tearProgress * origin.y; | 422 origin.y = (1 - tearProgress) * tearOrigin_.y + tearProgress * origin.y; |
| 400 } | 423 } |
| 401 | 424 |
| 402 if (targetController_) { | 425 if (targetController_) { |
| 403 // In order to "snap" two windows of different sizes together at their | 426 // In order to "snap" two windows of different sizes together at their |
| 404 // toolbar, we can't just use the origin of the target frame. We also have | 427 // toolbar, we can't just use the origin of the target frame. We also have |
| 405 // to take into consideration the difference in height. | 428 // to take into consideration the difference in height. |
| 406 NSRect targetFrame = [[targetController_ window] frame]; | 429 NSRect targetFrame = [[targetController_ window] frame]; |
| 407 NSRect sourceFrame = [dragWindow_ frame]; | 430 NSRect sourceFrame = [dragWindow_ frame]; |
| 408 origin.y = NSMinY(targetFrame) + | 431 origin.y = NSMinY(targetFrame) + |
| (...skipping 27 matching lines...) Expand all Loading... |
| 436 [targetController_ insertPlaceholderForTab:self | 459 [targetController_ insertPlaceholderForTab:self |
| 437 frame:tabFrame | 460 frame:tabFrame |
| 438 yStretchiness:0]; | 461 yStretchiness:0]; |
| 439 [targetController_ layoutTabs]; | 462 [targetController_ layoutTabs]; |
| 440 } else { | 463 } else { |
| 441 [dragWindow_ makeKeyAndOrderFront:nil]; | 464 [dragWindow_ makeKeyAndOrderFront:nil]; |
| 442 } | 465 } |
| 443 BOOL chromeShouldBeVisible = targetController_ == nil; | 466 BOOL chromeShouldBeVisible = targetController_ == nil; |
| 444 | 467 |
| 445 if (chromeIsVisible_ != chromeShouldBeVisible) { | 468 if (chromeIsVisible_ != chromeShouldBeVisible) { |
| 469 // TODO(pinkerton): There appears to be a race-condition in CoreAnimation |
| 470 // where if we use animators to set the alpha values, we can't guarantee |
| 471 // that we cancel them. This has the side effect of sometimes leaving |
| 472 // the dragged window translucent or invisible. We should re-visit this, |
| 473 // but for now, don't animate the alpha change. |
| 446 [dragWindow_ setHasShadow:YES]; | 474 [dragWindow_ setHasShadow:YES]; |
| 475 [[draggedController_ overlayWindow] setAlphaValue:1.0]; |
| 447 if (targetController_) { | 476 if (targetController_) { |
| 448 [NSAnimationContext beginGrouping]; | 477 [dragWindow_ setAlphaValue:0.0]; |
| 449 [[NSAnimationContext currentContext] setDuration:0.00001]; | |
| 450 [[dragWindow_ animator] setAlphaValue:0.0]; | |
| 451 [NSAnimationContext endGrouping]; | |
| 452 [[draggedController_ overlayWindow] setHasShadow:YES]; | 478 [[draggedController_ overlayWindow] setHasShadow:YES]; |
| 453 [[[draggedController_ overlayWindow] animator] setAlphaValue:1.0]; | |
| 454 } else { | 479 } else { |
| 455 [[draggedController_ overlayWindow] setAlphaValue:1.0]; | 480 [dragWindow_ setAlphaValue:0.5]; |
| 456 [[draggedController_ overlayWindow] setHasShadow:NO]; | 481 [[draggedController_ overlayWindow] setHasShadow:NO]; |
| 457 [[dragWindow_ animator] setAlphaValue:0.5]; | |
| 458 } | 482 } |
| 459 chromeIsVisible_ = chromeShouldBeVisible; | 483 chromeIsVisible_ = chromeShouldBeVisible; |
| 460 } | 484 } |
| 461 } | 485 } |
| 462 | 486 |
| 463 - (void)mouseUp:(NSEvent *)theEvent { | 487 - (void)mouseUp:(NSEvent *)theEvent { |
| 464 // The drag/click is done. If the user dragged the mouse, finalize the drag | 488 // The drag/click is done. If the user dragged the mouse, finalize the drag |
| 465 // and clean up. | 489 // and clean up. |
| 466 | 490 |
| 467 // Special-case this to keep the logic below simpler. | 491 // Special-case this to keep the logic below simpler. |
| 468 if (moveWindowOnDrag_) | 492 if (moveWindowOnDrag_) |
| 469 return; | 493 return; |
| 470 | 494 |
| 495 // Cancel any delayed -mouseDragged: requests that may still be pending. |
| 496 [NSObject cancelPreviousPerformRequestsWithTarget:self]; |
| 497 |
| 471 // We are now free to re-display the new tab button in the window we're | 498 // We are now free to re-display the new tab button in the window we're |
| 472 // dragging. It will show when the next call to -layoutTabs (which happens | 499 // dragging. It will show when the next call to -layoutTabs (which happens |
| 473 // indrectly by several of the calls below, such as removing the placeholder). | 500 // indrectly by several of the calls below, such as removing the placeholder). |
| 474 [draggedController_ showNewTabButton:YES]; | 501 [draggedController_ showNewTabButton:YES]; |
| 475 | 502 |
| 476 if (draggingWithinTabStrip_) { | 503 if (draggingWithinTabStrip_) { |
| 477 if (tabWasDragged_) { | 504 if (tabWasDragged_) { |
| 478 // Move tab to new location. | 505 // Move tab to new location. |
| 479 DCHECK([sourceController_ numberOfTabs]); | 506 DCHECK([sourceController_ numberOfTabs]); |
| 480 TabWindowController* dropController = sourceController_; | 507 TabWindowController* dropController = sourceController_; |
| 481 [dropController moveTabView:[dropController selectedTabView] | 508 [dropController moveTabView:[dropController selectedTabView] |
| 482 fromController:nil]; | 509 fromController:nil]; |
| 483 } | 510 } |
| 484 } else if (targetController_) { | 511 } else if (targetController_) { |
| 485 // Move between windows. If |targetController_| is nil, we're not dropping | 512 // Move between windows. If |targetController_| is nil, we're not dropping |
| 486 // into any existing window. | 513 // into any existing window. |
| 487 NSView* draggedTabView = [draggedController_ selectedTabView]; | 514 NSView* draggedTabView = [draggedController_ selectedTabView]; |
| 488 [draggedController_ removeOverlay]; | 515 [draggedController_ removeOverlay]; |
| 489 [targetController_ moveTabView:draggedTabView | 516 [targetController_ moveTabView:draggedTabView |
| 490 fromController:draggedController_]; | 517 fromController:draggedController_]; |
| 491 [targetController_ showWindow:nil]; | 518 [targetController_ showWindow:nil]; |
| 492 } else { | 519 } else { |
| 493 // Tab dragging did move window only. | 520 // Only move the window around on screen. Make sure it's set back to |
| 521 // normal state (fully opaque, has shadow, has key, etc). |
| 494 [draggedController_ removeOverlay]; | 522 [draggedController_ removeOverlay]; |
| 495 // Don't want to re-show the window if it was closed during the drag. | 523 // Don't want to re-show the window if it was closed during the drag. |
| 496 if ([dragWindow_ isVisible]) { | 524 if ([dragWindow_ isVisible]) { |
| 497 [dragWindow_ setAlphaValue:1.0]; | 525 [dragWindow_ setAlphaValue:1.0]; |
| 498 [dragOverlay_ setHasShadow:NO]; | 526 [dragOverlay_ setHasShadow:NO]; |
| 499 [dragWindow_ setHasShadow:YES]; | 527 [dragWindow_ setHasShadow:YES]; |
| 500 [dragWindow_ makeKeyAndOrderFront:nil]; | 528 [dragWindow_ makeKeyAndOrderFront:nil]; |
| 501 } | 529 } |
| 502 [[draggedController_ window] setLevel:NSNormalWindowLevel]; | 530 [[draggedController_ window] setLevel:NSNormalWindowLevel]; |
| 503 [draggedController_ removePlaceholder]; | 531 [draggedController_ removePlaceholder]; |
| 504 } | 532 } |
| 505 [sourceController_ removePlaceholder]; | 533 [sourceController_ removePlaceholder]; |
| 506 chromeIsVisible_ = YES; | 534 chromeIsVisible_ = YES; |
| 535 |
| 536 [self resetDragControllers]; |
| 507 } | 537 } |
| 508 | 538 |
| 509 - (void)otherMouseUp:(NSEvent*)theEvent { | 539 - (void)otherMouseUp:(NSEvent*)theEvent { |
| 510 // Support middle-click-to-close. | 540 // Support middle-click-to-close. |
| 511 if ([theEvent buttonNumber] == 2) { | 541 if ([theEvent buttonNumber] == 2) { |
| 512 [controller_ closeTab:self]; | 542 [controller_ closeTab:self]; |
| 513 } | 543 } |
| 514 } | 544 } |
| 515 | 545 |
| 516 - (NSPoint)patternPhase { | 546 - (NSPoint)patternPhase { |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 673 [[NSGraphicsContext currentContext] restoreGraphicsState]; | 703 [[NSGraphicsContext currentContext] restoreGraphicsState]; |
| 674 } | 704 } |
| 675 | 705 |
| 676 // Called when the user hits the right mouse button (or control-clicks) to | 706 // Called when the user hits the right mouse button (or control-clicks) to |
| 677 // show a context menu. | 707 // show a context menu. |
| 678 - (void)rightMouseDown:(NSEvent*)theEvent { | 708 - (void)rightMouseDown:(NSEvent*)theEvent { |
| 679 [NSMenu popUpContextMenu:[self menu] withEvent:theEvent forView:self]; | 709 [NSMenu popUpContextMenu:[self menu] withEvent:theEvent forView:self]; |
| 680 } | 710 } |
| 681 | 711 |
| 682 @end | 712 @end |
| OLD | NEW |