Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(136)

Side by Side Diff: chrome/browser/cocoa/tab_view.mm

Issue 243080: Fix several issues with dragging tabs and quickly letting go, including crash... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 11 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698