summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-22 18:05:31 +0000
committerpinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-22 18:05:31 +0000
commit8538894e63ccd5239049484737fb4b819fe49bad (patch)
tree96a646bffcdfa4b3d6ce2c43a30655bf81f798f2
parent2e63183c36ee22e7c3c470cfcf49c9266016dc21 (diff)
downloadchromium_src-8538894e63ccd5239049484737fb4b819fe49bad.zip
chromium_src-8538894e63ccd5239049484737fb4b819fe49bad.tar.gz
chromium_src-8538894e63ccd5239049484737fb4b819fe49bad.tar.bz2
Fix a lot of jankiness during tab dragging. Fix dragging last tab in a different way (overlay view) so we can continue to use the "sprouting up" new tab animation. Improved animation with placeholder so it's smoother. Fixed jank closing and tearing off windows by suppressing window updates. Patch from alcor@google.com
BUG=none TEST=tab dragging w/in a window, between windows. Creating new tabs. Closing window with lots of tabs. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16753 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/cocoa/browser_window_controller.mm20
-rw-r--r--chrome/browser/cocoa/tab_strip_controller.h13
-rw-r--r--chrome/browser/cocoa/tab_strip_controller.mm78
-rw-r--r--chrome/browser/cocoa/tab_view.h25
-rw-r--r--chrome/browser/cocoa/tab_view.mm421
5 files changed, 335 insertions, 222 deletions
diff --git a/chrome/browser/cocoa/browser_window_controller.mm b/chrome/browser/cocoa/browser_window_controller.mm
index 5258fc9..2070be8 100644
--- a/chrome/browser/cocoa/browser_window_controller.mm
+++ b/chrome/browser/cocoa/browser_window_controller.mm
@@ -191,6 +191,8 @@ willPositionSheet:(NSWindow *)sheet
// required to get us to the closing state and (by watching for all the tabs
// going away) will again call to close the window when it's finally ready.
- (BOOL)windowShouldClose:(id)sender {
+ // Disable updates while closing all tabs to avoid flickering.
+ NSDisableScreenUpdates();
// Give beforeunload handlers the chance to cancel the close before we hide
// the window below.
if (!browser_->ShouldCloseWindow())
@@ -209,6 +211,7 @@ willPositionSheet:(NSWindow *)sheet
browser_->OnWindowClosing();
return NO;
}
+ NSEnableScreenUpdates();
// the tab strip is empty, it's ok to close the window
return YES;
@@ -398,6 +401,10 @@ willPositionSheet:(NSWindow *)sheet
- (NSView *)selectedTabView {
return [tabStripController_ selectedTabView];
+}
+
+- (TabStripController *)tabStripController {
+ return tabStripController_;
}
- (void)setIsLoading:(BOOL)isLoading {
@@ -426,6 +433,9 @@ willPositionSheet:(NSWindow *)sheet
}
- (TabWindowController*)detachTabToNewWindow:(TabView*)tabView {
+ // Disable screen updates so that this appears as a single visual change.
+ NSDisableScreenUpdates();
+
// Fetch the tab contents for the tab being dragged
int index = [tabStripController_ indexForTabView:tabView];
TabContents* contents = browser_->tabstrip_model()->GetTabContentsAt(index);
@@ -459,15 +469,17 @@ willPositionSheet:(NSWindow *)sheet
dockInfo);
// Get the new controller by asking the new window for its delegate.
- TabWindowController* controller =
+ BrowserWindowController* controller =
[newBrowser->window()->GetNativeHandle() delegate];
DCHECK(controller && [controller isKindOfClass:[TabWindowController class]]);
- // Force the added tab to the right size (remove stretching)
+ // Force the added tab to the right size (remove stretching.)
tabRect.size.height = [TabStripController defaultTabHeight];
- NSView *newTabView = [controller selectedTabView];
- [newTabView setFrame:tabRect];
+ // And make sure we use the correct frame in the new view.
+ [[controller tabStripController] setFrameOfSelectedTab:tabRect];
+
+ NSEnableScreenUpdates();
return controller;
}
diff --git a/chrome/browser/cocoa/tab_strip_controller.h b/chrome/browser/cocoa/tab_strip_controller.h
index 35fbc42..eeab6b0 100644
--- a/chrome/browser/cocoa/tab_strip_controller.h
+++ b/chrome/browser/cocoa/tab_strip_controller.h
@@ -35,6 +35,7 @@ class ToolbarModel;
TabContents* currentTab_; // weak, tab for which we're showing state
TabStripView* tabView_; // weak
NSView* switchView_; // weak
+ scoped_nsobject<NSView> dragBlockingView_; // avoid bad window server drags
NSButton* newTabButton_; // weak, obtained from the nib.
scoped_ptr<TabStripModelObserverBridge> bridge_;
TabStripModel* tabModel_; // weak
@@ -47,10 +48,15 @@ class ToolbarModel;
// this is kept in the same order as the tab strip model.
scoped_nsobject<NSMutableArray> tabArray_;
- // These values are only used during a drag, and override tab positioning
+ // These values are only used during a drag, and override tab positioning.
TabView* placeholderTab_; // weak. Tab being dragged
NSRect placeholderFrame_; // Frame to use
- CGFloat placeholderStretchiness_; // Vertical force indicated by streching tab
+ CGFloat placeholderStretchiness_; // Vertical force shown by streching tab.
+ // Frame targets for all the current views.
+ // target frames are used because repeated requests to [NSView animator].
+ // aren't coalesced, so we store frames to avoid redundant calls.
+ scoped_nsobject<NSMutableDictionary> targetFrames_;
+ NSRect newTabTargetFrame_;
}
// Initialize the controller with a view and browser that contains
@@ -69,6 +75,9 @@ class ToolbarModel;
// Return the view for the currently selected tab.
- (NSView *)selectedTabView;
+// Set the frame of the selected tab, also updates the internal frame dict.
+- (void)setFrameOfSelectedTab:(NSRect)frame;
+
// Move the given tab at index |from| in this window to the location of the
// current placeholder.
- (void)moveTabFromIndex:(NSInteger)from;
diff --git a/chrome/browser/cocoa/tab_strip_controller.mm b/chrome/browser/cocoa/tab_strip_controller.mm
index 30f7768..6fed79f 100644
--- a/chrome/browser/cocoa/tab_strip_controller.mm
+++ b/chrome/browser/cocoa/tab_strip_controller.mm
@@ -22,6 +22,15 @@
#include "chrome/browser/tabs/tab_strip_model.h"
#include "grit/generated_resources.h"
+// A simple view class that prevents the windowserver from dragging the
+// area behind tabs. Sometimes core animation confuses it.
+@interface TabStripControllerDragBlockingView : NSView
+@end
+@implementation TabStripControllerDragBlockingView
+- (BOOL)mouseDownCanMoveWindow {return NO;}
+- (void)drawRect:(NSRect)rect {}
+@end
+
@implementation TabStripController
- (id)initWithView:(TabStripView*)view
@@ -44,8 +53,11 @@
[newTabButton_ setTarget:nil];
[newTabButton_ setAction:@selector(commandDispatch:)];
[newTabButton_ setTag:IDC_NEW_TAB];
-
+ targetFrames_.reset([[NSMutableDictionary alloc] init]);
[tabView_ setWantsLayer:YES];
+ dragBlockingView_.reset([[TabStripControllerDragBlockingView alloc]
+ initWithFrame:NSZeroRect]);
+ [view addSubview:dragBlockingView_];
}
return self;
}
@@ -167,6 +179,7 @@
const float kMaxTabWidth = [TabController maxTabWidth];
const float kMinTabWidth = [TabController minTabWidth];
+ NSRect enclosingRect = NSZeroRect;
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.2];
@@ -198,9 +211,20 @@
}
if (isPlaceholder) {
+ // Move the current tab to the correct location intantly.
+ // We need a duration or else it doesn't cancel an inflight animation.
+ [NSAnimationContext beginGrouping];
+ [[NSAnimationContext currentContext] setDuration:0.000001];
tabFrame.origin.x = placeholderFrame_.origin.x;
- tabFrame.size.height += 10.0 * placeholderStretchiness_;
- [[tab view] setFrame:tabFrame];
+ // TODO(alcor): reenable this
+ //tabFrame.size.height += 10.0 * placeholderStretchiness_;
+ [[[tab view] animator] setFrame:tabFrame];
+ [NSAnimationContext endGrouping];
+
+ // Store the frame by identifier to aviod redundant calls to animator.
+ NSValue *identifier = [NSValue valueWithPointer:[tab view]];
+ [targetFrames_ setObject:[NSValue valueWithRect:tabFrame]
+ forKey:identifier];
continue;
} else {
// If our left edge is to the left of the placeholder's left, but our mid
@@ -212,20 +236,23 @@
tabFrame.origin.x = offset;
}
-#if 0
// Animate the tab in by putting it below the horizon.
- // TODO(pinkerton/alcor): While this looks nice, it confuses the heck
- // out of the window server and causes the window to think that there's
- // no tab there. The net result is that dragging the tab also drags
- // the window. We need to find another way to do this.
if (newTab && visible) {
[[tab view] setFrame:NSOffsetRect(tabFrame, 0, -NSHeight(tabFrame))];
}
-#endif
id frameTarget = visible ? [[tab view] animator] : [tab view];
tabFrame.size.width = [tab selected] ? kMaxTabWidth : baseTabWidth;
- [frameTarget setFrame:tabFrame];
+
+ // Check the frame by identifier to avoid redundant calls to animator.
+ NSValue *identifier = [NSValue valueWithPointer:[tab view]];
+ NSRect oldTarget = [[targetFrames_ objectForKey:identifier] rectValue];
+ if (!NSEqualRects(oldTarget, tabFrame)) {
+ [frameTarget setFrame:tabFrame];
+ [targetFrames_ setObject:[NSValue valueWithRect:tabFrame]
+ forKey:identifier];
+ }
+ enclosingRect = NSUnionRect(tabFrame, enclosingRect);
}
if (offset < availableWidth) {
@@ -234,12 +261,24 @@
}
i++;
}
+
+ NSRect newTabNewFrame = [newTabButton_ frame];
+ newTabNewFrame.origin =
+ NSMakePoint(MIN(availableWidth, offset + kNewTabButtonOffset), 0);
+ newTabNewFrame.origin.x = MAX(newTabNewFrame.origin.x,
+ NSMaxX(placeholderFrame_));
+ if (i > 0 && [newTabButton_ isHidden]) {
+ [[newTabButton_ animator] setHidden:NO];
+ }
+
+ if (!NSEqualRects(newTabTargetFrame_, newTabNewFrame)) {
+ [newTabButton_ setFrame:newTabNewFrame];
+ newTabTargetFrame_ = newTabNewFrame;
+ // Move the new tab button into place.
+ }
- // Move the new tab button into place
- [[newTabButton_ animator] setFrameOrigin:
- NSMakePoint(MIN(availableWidth, offset + kNewTabButtonOffset), 0)];
- if (i > 0) [[newTabButton_ animator] setHidden:NO];
[NSAnimationContext endGrouping];
+ [dragBlockingView_ setFrame:enclosingRect];
}
// Handles setting the title of the tab based on the given |contents|. Uses
@@ -352,6 +391,9 @@
NSView* tab = [self viewAtIndex:index];
[tab removeFromSuperview];
+ NSValue *identifier = [NSValue valueWithPointer:tab];
+ [targetFrames_ removeObjectForKey:identifier];
+
// Once we're totally done with the tab, delete its controller
[tabArray_ removeObjectAtIndex:index];
@@ -433,6 +475,14 @@
[self layoutTabs];
}
+- (void)setFrameOfSelectedTab:(NSRect)frame {
+ NSView *view = [self selectedTabView];
+ NSValue *identifier = [NSValue valueWithPointer:view];
+ [targetFrames_ setObject:[NSValue valueWithRect:frame]
+ forKey:identifier];
+ [view setFrame:frame];
+}
+
- (NSView *)selectedTabView {
int selectedIndex = tabModel_->selected_index();
return [self viewAtIndex:selectedIndex];
diff --git a/chrome/browser/cocoa/tab_view.h b/chrome/browser/cocoa/tab_view.h
index fda144f..96536d7 100644
--- a/chrome/browser/cocoa/tab_view.h
+++ b/chrome/browser/cocoa/tab_view.h
@@ -7,7 +7,7 @@
#import <Cocoa/Cocoa.h>
-@class TabController;
+@class TabController, TabWindowController;
// A view that handles the event tracking (clicking and dragging) for a tab
// on the tab strip. Relies on an associated TabController to provide a
@@ -19,6 +19,29 @@
// TODO(rohitrao): Add this button to a CoreAnimation layer so we can fade it
// in and out on mouseovers.
IBOutlet NSButton* closeButton_;
+
+ // All following variables are valid for the duration of a drag.
+ // These are released on mouseUp:
+ BOOL isTheOnlyTab_; // Is this the only tab in the window?
+ BOOL tabWasDragged_; // Has the tab been dragged?
+ BOOL draggingWithinTabStrip_; // Did drag stay in the current tab strip?
+ BOOL chromeIsVisible_;
+
+ NSTimeInterval tearTime_; // Time since tear happened
+ NSPoint tearOrigin_; // Origin of the tear rect
+ NSPoint dragOrigin_; // Origin point of the drag
+ // TODO(alcor): these references may need to be strong to avoid crashes
+ // due to JS closing windows
+ TabWindowController* sourceController_; // weak. controller starting the drag
+ NSWindow* sourceWindow_; // weak. The window starting the drag
+ NSRect sourceWindowFrame_;
+ NSRect sourceTabFrame_;
+
+ TabWindowController* draggedController_; // weak. Controller being dragged.
+ NSWindow* dragWindow_; // weak. The window being dragged
+ NSWindow* dragOverlay_; // weak. The overlay being dragged
+
+ TabWindowController* targetController_; // weak. Controller being targeted
}
@end
diff --git a/chrome/browser/cocoa/tab_view.mm b/chrome/browser/cocoa/tab_view.mm
index 1033546..02b9867 100644
--- a/chrome/browser/cocoa/tab_view.mm
+++ b/chrome/browser/cocoa/tab_view.mm
@@ -51,258 +51,277 @@
// TODO(pinkerton/alcor): This routine needs *a lot* of work to marry Cole's
// ideas of dragging cocoa views between windows and how the Browser and
// TabStrip models want to manage tabs.
-- (void)mouseDown:(NSEvent *)theEvent {
- static const CGFloat kTearDistance = 36.0;
- // Make sure the controller doesn't go away while we're doing this.
- // TODO(pinkerton): cole had this, not sure why it's necessary.
- [[controller_ retain] autorelease];
+static const CGFloat kTearDistance = 36.0;
+static const NSTimeInterval kTearDuration = 0.333;
+static const double kDragStartDistance = 3.0;
+- (void)mouseDown:(NSEvent *)theEvent {
// Fire the action to select the tab.
if ([[controller_ target] respondsToSelector:[controller_ action]])
[[controller_ target] performSelector:[controller_ action]
withObject:self];
// Resolve overlay back to original window.
- NSWindow* sourceWindow = [self window];
- if ([sourceWindow isKindOfClass:[NSPanel class]]) {
- sourceWindow = [sourceWindow parentWindow];
+ sourceWindow_ = [self window];
+ if ([sourceWindow_ isKindOfClass:[NSPanel class]]) {
+ sourceWindow_ = [sourceWindow_ parentWindow];
}
- TabWindowController* sourceController = [sourceWindow windowController];
+ sourceWindowFrame_ = [sourceWindow_ frame];
+ sourceTabFrame_ = [self frame];
+ sourceController_ = [sourceWindow_ windowController];
+ draggedController_ = nil;
+ dragWindow_ = nil;
+ dragOverlay_ = nil;
+ targetController_ = nil;
+ tabWasDragged_ = NO;
+ tearTime_ = 0.0;
+ draggingWithinTabStrip_ = YES;
// We don't want to "tear off" a tab if there's only one in the window. Treat
// it like we're dragging around a tab we've already detached. Note that
// unit tests might have |-numberOfTabs| reporting zero since the model
// won't be fully hooked up. We need to be prepared for that and not send
// them into the "magnetic" codepath.
- BOOL isLastRemainingTab = [sourceController numberOfTabs] <= 1;
+ isTheOnlyTab_ = [sourceController_ numberOfTabs] <= 1;
- BOOL dragging = YES;
- BOOL moveBetweenWindows = NO;
+ dragOrigin_ = [NSEvent mouseLocation];
- NSPoint lastPoint =
- [[theEvent window] convertBaseToScreen:[theEvent locationInWindow]];
+ // Because we move views between windows, we need to handle the event loop
+ // ourselves. Ideally we should use the standard event loop.
+ while (1) {
+ theEvent =
+ [NSApp nextEventMatchingMask:NSLeftMouseUpMask | NSLeftMouseDraggedMask
+ untilDate:[NSDate distantFuture]
+ inMode:NSDefaultRunLoopMode dequeue:YES];
+ NSPoint thisPoint = [NSEvent mouseLocation];
- // First, go through the magnetic drag cycle. We break out of this if
- // "stretchiness" ever exceeds the a set amount.
- NSRect frame = [self frame];
- if (!isLastRemainingTab) {
- while (dragging) {
- theEvent =
- [NSApp nextEventMatchingMask:NSLeftMouseUpMask | NSLeftMouseDraggedMask
- untilDate:[NSDate distantFuture]
- inMode:NSDefaultRunLoopMode dequeue:YES];
- NSPoint thisPoint = [NSEvent mouseLocation];
- CGFloat stretchiness = thisPoint.y - lastPoint.y;
- stretchiness = copysign(sqrtf(fabs(stretchiness))/sqrtf(kTearDistance),
- stretchiness);
- [sourceController insertPlaceholderForTab:self
- frame:NSOffsetRect(frame,
- thisPoint.x - lastPoint.x, 0)
- yStretchiness:stretchiness];
-
- CGFloat tearForce = fabs(thisPoint.y - lastPoint.y);
- if (tearForce > kTearDistance) break;
- if ([theEvent type] == NSLeftMouseUp) {
- // Mouse up, break out of the drag event tracking loop
- dragging = NO;
- break;
- }
+ NSEventType type = [theEvent type];
+ if (type == NSLeftMouseDragged) {
+ [self mouseDragged:theEvent];
+ } else { // Mouse Up
+ [self mouseUp:theEvent];
+ break;
}
}
+}
- if (dragging)
- [sourceController removePlaceholder];
+- (void)mouseDragged:(NSEvent *)theEvent {
+ // First, go through the magnetic drag cycle. We break out of this if
+ // "stretchiness" ever exceeds the a set amount.
+ tabWasDragged_ = YES;
- TabWindowController* draggedController = nil;
- TabWindowController* targetController = nil;
+ if (isTheOnlyTab_) draggingWithinTabStrip_ = NO;
+ if (draggingWithinTabStrip_) {
+ NSRect frame = [self frame];
+ NSPoint thisPoint = [NSEvent mouseLocation];
+ CGFloat stretchiness = thisPoint.y - dragOrigin_.y;
+ stretchiness = copysign(sqrtf(fabs(stretchiness))/sqrtf(kTearDistance),
+ stretchiness) / 2.0;
+ CGFloat offset = thisPoint.x - dragOrigin_.x;
+ if (fabsf(offset) > 100) stretchiness = 0;
+ [sourceController_ insertPlaceholderForTab:self
+ frame:NSOffsetRect(sourceTabFrame_,
+ offset, 0)
+ yStretchiness:stretchiness];
+
+ CGFloat tearForce = fabs(thisPoint.y - dragOrigin_.y);
+ if (tearForce > kTearDistance) {
+ draggingWithinTabStrip_ = NO;
+ // When you finally leave the strip, we treat that as the origin.
+ dragOrigin_.x = thisPoint.x;
+ } else {
+ return;
+ }
+ }
- NSWindow* dragWindow = nil;
- NSWindow* dragOverlay = nil;
+ NSPoint lastPoint =
+ [[theEvent window] convertBaseToScreen:[theEvent locationInWindow]];
// Do not start dragging until the user has "torn" the tab off by
// moving more than 3 pixels.
- BOOL torn = NO;
- static const double kDragStartDistance = 3.0;
-
NSDate* targetDwellDate = nil; // The date this target was first chosen
NSMutableArray* targets = [NSMutableArray array];
- while (dragging) {
- theEvent =
- [NSApp nextEventMatchingMask:NSLeftMouseUpMask | NSLeftMouseDraggedMask
- untilDate:[NSDate distantFuture]
- inMode:NSDefaultRunLoopMode dequeue:YES];
- NSPoint thisPoint = [NSEvent mouseLocation];
-
- if (!torn) {
- double dx = thisPoint.x - lastPoint.x;
- double dy = thisPoint.y - lastPoint.y;
-
- if (dx * dx + dy * dy < kDragStartDistance * kDragStartDistance
- && [theEvent type] == NSLeftMouseDragged) {
- continue;
+ NSPoint thisPoint = [NSEvent mouseLocation];
+
+ // Find all the windows that could be a target. It has to be of the
+ // appropriate class, and visible (obviously).
+ if (![targets count]) {
+ for (NSWindow* window in [NSApp windows]) {
+ if (window == sourceWindow_ && isTheOnlyTab_) continue;
+ if (window == dragWindow_) continue;
+ if (![window isVisible]) continue;
+ NSWindowController *controller = [window windowController];
+ if ([controller isKindOfClass:[TabWindowController class]]) {
+ [targets addObject:controller];
}
- torn = YES;
}
+ }
- // Find all the windows that could be a target. It has to be of the
- // appropriate class, and visible (obviously).
- if (![targets count]) {
- for (NSWindow* window in [NSApp windows]) {
- if (window == sourceWindow && isLastRemainingTab) continue;
- if (window == dragWindow) continue;
- if (![window isVisible]) continue;
- NSWindowController *controller = [window windowController];
- if ([controller isKindOfClass:[TabWindowController class]]) {
- [targets addObject:controller];
- }
+ // Iterate over possible targets checking for the one the mouse is in.
+ // The mouse can be in either the tab or window frame.
+ TabWindowController* newTarget = nil;
+ for (TabWindowController* target in targets) {
+ NSRect windowFrame = [[target window] frame];
+ if (NSPointInRect(thisPoint, windowFrame)) {
+ NSRect tabStripFrame = [[target tabStripView] frame];
+ tabStripFrame = [[target tabStripView] convertRectToBase:tabStripFrame];
+ tabStripFrame.origin = [[target window]
+ convertBaseToScreen:tabStripFrame.origin];
+ if (NSPointInRect(thisPoint, tabStripFrame)) {
+ newTarget = target;
}
+ break;
}
+ }
- // Iterate over possible targets checking for the one the mouse is in.
- // The mouse can be in either the tab or window frame.
- TabWindowController* newTarget = nil;
- for (TabWindowController* target in targets) {
- NSRect windowFrame = [[target window] frame];
- if (NSPointInRect(thisPoint, windowFrame)) {
- NSRect tabStripFrame = [[target tabStripView] frame];
- tabStripFrame = [[target tabStripView] convertRectToBase:tabStripFrame];
- tabStripFrame.origin = [[target window]
- convertBaseToScreen:tabStripFrame.origin];
- if (NSPointInRect(thisPoint, tabStripFrame)) {
- newTarget = target;
- }
- break;
- }
+ // If we're now targeting a new window, re-layout the tabs in the old
+ // target and reset how long we've been hovering over this new one.
+ if (targetController_ != newTarget) {
+ targetDwellDate = [NSDate date];
+ [targetController_ removePlaceholder];
+ targetController_ = newTarget;
+ if (!newTarget) {
+ tearTime_ = [NSDate timeIntervalSinceReferenceDate];
+ tearOrigin_ = [dragWindow_ frame].origin;
}
+ }
- // If we're now targeting a new window, re-layout the tabs in the old
- // target and reset how long we've been hovering over this new one.
- if (targetController != newTarget) {
- targetDwellDate = [NSDate date];
- [targetController removePlaceholder];
- targetController = newTarget;
+ // Create or identify the dragged controller.
+ if (!draggedController_) {
+ if (isTheOnlyTab_) {
+ draggedController_ = sourceController_;
+ dragWindow_ = [draggedController_ window];
+ } else {
+ // Detach from the current window and put it in a new window.
+ draggedController_ = [sourceController_ detachTabToNewWindow:self];
+ dragWindow_ = [draggedController_ window];
+ [dragWindow_ setAlphaValue:0.0];
}
- NSEventType type = [theEvent type];
- if (type == NSLeftMouseDragged) {
- moveBetweenWindows = YES;
- if (!draggedController) {
- if (isLastRemainingTab) {
- draggedController = sourceController;
- dragWindow = [draggedController window];
- } else {
- // Detach from the current window and put it in a new window.
- draggedController = [sourceController detachTabToNewWindow:self];
- dragWindow = [draggedController window];
- [dragWindow setAlphaValue:0.0];
- }
-
- // Bring the target window to the front and make sure it has a border.
- [dragWindow setLevel:NSFloatingWindowLevel];
- [dragWindow orderFront:nil];
- [dragWindow makeMainWindow];
- [draggedController showOverlay];
- dragOverlay = [draggedController overlayWindow];
-
- NSPoint origin = [sourceWindow frame].origin;
-
- origin.y += thisPoint.y - lastPoint.y;
- [dragWindow setFrameOrigin:NSMakePoint(origin.x, origin.y)];
- //if (![targets count])
- // [dragOverlay setHasShadow:NO];
- } else {
- NSPoint origin = [dragWindow frame].origin;
- origin.x += thisPoint.x - lastPoint.x;
- origin.y += thisPoint.y - lastPoint.y;
- [dragWindow setFrameOrigin:NSMakePoint(origin.x, origin.y)];
- }
+ // Bring the target window to the front and make sure it has a border.
+ [dragWindow_ setLevel:NSFloatingWindowLevel];
+ [dragWindow_ orderFront:nil];
+ [dragWindow_ makeMainWindow];
+ [draggedController_ showOverlay];
+ dragOverlay_ = [draggedController_ overlayWindow];
+ //if (![targets count])
+ // [dragOverlay_ setHasShadow:NO];
+ if (!isTheOnlyTab_) {
+ tearTime_ = [NSDate timeIntervalSinceReferenceDate];
+ tearOrigin_ = sourceWindowFrame_.origin;
+ }
+ }
- // If we're not hovering over any window, make the window is fully
- // opaque. Otherwise, find where the tab might be dropped and insert
- // a placeholder so it appears like it's part of that window.
- if (targetController) {
- if (![[targetController window] isKeyWindow]) {
- // && ([targetDwellDate timeIntervalSinceNow] < -REQUIRED_DWELL)) {
- [[targetController window] makeKeyAndOrderFront:nil];
- [targets removeAllObjects];
- targetDwellDate = nil;
- }
-
- // Compute where placeholder should go and insert it into the
- // destination tab strip.
- NSRect dropTabFrame = [[targetController tabStripView] frame];
- NSView *draggedTabView = [draggedController selectedTabView];
- NSRect tabFrame = [draggedTabView frame];
- tabFrame = [draggedTabView convertRectToBase:[self bounds]];
- tabFrame.origin = [dragWindow
- convertBaseToScreen:tabFrame.origin];
- tabFrame.origin = [[targetController window]
- convertScreenToBase:tabFrame.origin];
- tabFrame = [[targetController tabStripView]
- convertRectFromBase:tabFrame];
- NSPoint point =
- [sourceWindow convertBaseToScreen:
- [self convertPointToBase:NSZeroPoint]];
- [targetController insertPlaceholderForTab:self
- frame:tabFrame
- yStretchiness:0];
- [targetController layoutTabs];
- } else {
- [dragWindow makeKeyAndOrderFront:nil];
- }
+ float tearProgress = [NSDate timeIntervalSinceReferenceDate] - tearTime_;
+ tearProgress /= kTearDuration;
+ tearProgress = sqrtf(MAX(MIN(tearProgress, 1.0), 0.0));
+
+ // Move the dragged window to the right place on the screen.
+ NSPoint origin = sourceWindowFrame_.origin;
+ origin.x += (thisPoint.x - dragOrigin_.x);
+ origin.y += (thisPoint.y - dragOrigin_.y);
+
+ if (tearProgress < 1) {
+ // If the tear animation is not complete, call back to ourself with the
+ // same event to animate even if the mouse isn't moving.
+ [NSObject cancelPreviousPerformRequestsWithTarget:self];
+ [self performSelector:@selector(mouseDragged:)
+ withObject:theEvent
+ afterDelay:1.0f/30.0f];
+
+ origin.x = (1 - tearProgress) * tearOrigin_.x + tearProgress * origin.x;
+ origin.y = (1 - tearProgress) * tearOrigin_.y + tearProgress * origin.y;
+ }
- [dragWindow setHasShadow:NO];
- [dragWindow setAlphaValue:targetController ? 0.1 : 0.5];
- [[draggedController overlayWindow]
- setAlphaValue:targetController ? 0.85 : 1.0];
- } else if (type == NSLeftMouseUp) {
- // Mouse up, break out of the drag event tracking loop
- dragging = NO;
+ if (targetController_)
+ origin.y = [[targetController_ window] frame].origin.y;
+ [dragWindow_ setFrameOrigin:NSMakePoint(origin.x, origin.y)];
+
+ // If we're not hovering over any window, make the window is fully
+ // opaque. Otherwise, find where the tab might be dropped and insert
+ // a placeholder so it appears like it's part of that window.
+ if (targetController_) {
+ if (![[targetController_ window] isKeyWindow]) {
+ // && ([targetDwellDate timeIntervalSinceNow] < -REQUIRED_DWELL)) {
+ [[targetController_ window] orderFront:nil];
+ [targets removeAllObjects];
+ targetDwellDate = nil;
}
- lastPoint = thisPoint;
- } // while tracking mouse
- // The drag/click is done. If the user dragged the mouse, finalize the drag
- // and clean up.
- if (moveBetweenWindows) {
- // Move between windows. If |targetController| is nil, we're not dropping
- // into any existing window.
- TabWindowController* dropController = targetController;
- if (dropController) {
- NSView* draggedTabView = [draggedController selectedTabView];
- [draggedController removeOverlay];
- [dropController moveTabView:draggedTabView
- fromController:draggedController];
- [dropController showWindow:nil];
+ // Compute where placeholder should go and insert it into the
+ // destination tab strip.
+ NSRect dropTabFrame = [[targetController_ tabStripView] frame];
+ TabView *draggedTabView = (TabView *)[draggedController_ selectedTabView];
+ NSRect tabFrame = [draggedTabView frame];
+ tabFrame.origin = [dragWindow_ convertBaseToScreen:tabFrame.origin];
+ tabFrame.origin = [[targetController_ window]
+ convertScreenToBase:tabFrame.origin];
+ tabFrame = [[targetController_ tabStripView]
+ convertRectFromBase:tabFrame];
+ NSPoint point =
+ [sourceWindow_ convertBaseToScreen:
+ [draggedTabView convertPointToBase:NSZeroPoint]];
+ [targetController_ insertPlaceholderForTab:self
+ frame:tabFrame
+ yStretchiness:0];
+ [targetController_ layoutTabs];
+ } else {
+ [dragWindow_ makeKeyAndOrderFront:nil];
+ }
+ BOOL chromeShouldBeVisible = targetController_ == nil;
+
+ if (chromeIsVisible_ != chromeShouldBeVisible) {
+ [dragWindow_ setHasShadow:YES];
+ if (targetController_) {
+ [NSAnimationContext beginGrouping];
+ [[NSAnimationContext currentContext] setDuration:0.00001];
+ [[dragWindow_ animator] setAlphaValue:0.0];
+ [NSAnimationContext endGrouping];
+ [[draggedController_ overlayWindow] setHasShadow:YES];
+ [[[draggedController_ overlayWindow] animator] setAlphaValue:1.0];
} else {
- [targetController removePlaceholder];
- [[dragWindow animator] setAlphaValue:1.0];
- [dragOverlay setHasShadow:NO];
- [dragWindow setHasShadow:YES];
- [draggedController removeOverlay];
- [dragWindow makeKeyAndOrderFront:nil];
-
- [[draggedController window] setLevel:NSNormalWindowLevel];
- [draggedController layoutTabs];
+ [[draggedController_ overlayWindow] setAlphaValue:1.0];
+ [[draggedController_ overlayWindow] setHasShadow:NO];
+ [[dragWindow_ animator] setAlphaValue:0.5];
}
- [sourceController layoutTabs];
- } else {
- // Move or click within a window. We need to differentiate between a
- // click on the tab and a drag by checking against the initial x position.
- NSPoint currentPoint = [NSEvent mouseLocation];
- BOOL wasDrag = fabs(currentPoint.x - lastPoint.x) > kDragStartDistance;
- if (wasDrag) {
+ chromeIsVisible_ = chromeShouldBeVisible;
+ }
+}
+
+- (void)mouseUp:(NSEvent *)theEvent {
+ // The drag/click is done. If the user dragged the mouse, finalize the drag
+ // and clean up.
+
+ if (draggingWithinTabStrip_) {
+ if (tabWasDragged_) {
// Move tab to new location.
- TabWindowController* dropController = sourceController;
+ TabWindowController* dropController = sourceController_;
[dropController moveTabView:[dropController selectedTabView]
fromController:nil];
}
+ } else if (targetController_) {
+ // Move between windows. If |targetController_| is nil, we're not dropping
+ // into any existing window.
+ NSView* draggedTabView = [draggedController_ selectedTabView];
+ [draggedController_ removeOverlay];
+ [targetController_ moveTabView:draggedTabView
+ fromController:draggedController_];
+ [targetController_ showWindow:nil];
+ } else {
+ [dragWindow_ setAlphaValue:1.0];
+ [dragOverlay_ setHasShadow:NO];
+ [dragWindow_ setHasShadow:YES];
+ [draggedController_ removeOverlay];
+ [dragWindow_ makeKeyAndOrderFront:nil];
+
+ [[draggedController_ window] setLevel:NSNormalWindowLevel];
+ [draggedController_ removePlaceholder];
+ [draggedController_ layoutTabs];
}
-
- [sourceController removePlaceholder];
+ [sourceController_ removePlaceholder];
}
- (void)otherMouseUp:(NSEvent*) theEvent {