diff options
author | pinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-28 19:03:39 +0000 |
---|---|---|
committer | pinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-28 19:03:39 +0000 |
commit | 2feb19b125ef3fd6bebef1b5eefd08e92523d897 (patch) | |
tree | 13d5f0ce125e06b367fc1170fdc3556655b1ba6e | |
parent | c6b5b4f6a3f379e01299292f36b626d491912366 (diff) | |
download | chromium_src-2feb19b125ef3fd6bebef1b5eefd08e92523d897.zip chromium_src-2feb19b125ef3fd6bebef1b5eefd08e92523d897.tar.gz chromium_src-2feb19b125ef3fd6bebef1b5eefd08e92523d897.tar.bz2 |
Support for tabs reaching a threshold before tearing off (magnetism), showing position of where the tab will go in the destination tab strip. Use new close box icon. Don't draw the tab title over the close box. Patch from alcor@google.com
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@14758 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/app/nibs/en.lproj/BrowserWindow.xib | 24 | ||||
-rw-r--r-- | chrome/app/nibs/en.lproj/TabView.xib | 31 | ||||
-rw-r--r-- | chrome/browser/cocoa/browser_window_controller.mm | 36 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_cell.mm | 20 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_strip_controller.h | 26 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_strip_controller.mm | 92 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_strip_view.mm | 2 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_view.mm | 143 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_window_controller.h | 13 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_window_controller.mm | 18 |
10 files changed, 294 insertions, 111 deletions
diff --git a/chrome/app/nibs/en.lproj/BrowserWindow.xib b/chrome/app/nibs/en.lproj/BrowserWindow.xib index d9a64e8..be26e4d 100644 --- a/chrome/app/nibs/en.lproj/BrowserWindow.xib +++ b/chrome/app/nibs/en.lproj/BrowserWindow.xib @@ -2,13 +2,13 @@ <archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.03"> <data> <int key="IBDocument.SystemTarget">1050</int> - <string key="IBDocument.SystemVersion">9F33</string> + <string key="IBDocument.SystemVersion">9G55</string> <string key="IBDocument.InterfaceBuilderVersion">677</string> - <string key="IBDocument.AppKitVersion">949.34</string> - <string key="IBDocument.HIToolboxVersion">352.00</string> + <string key="IBDocument.AppKitVersion">949.43</string> + <string key="IBDocument.HIToolboxVersion">353.00</string> <object class="NSMutableArray" key="IBDocument.EditedObjectIDs"> <bool key="EncodedWithXMLCoder">YES</bool> - <integer value="2"/> + <integer value="56"/> </object> <object class="NSArray" key="IBDocument.PluginDependencies"> <bool key="EncodedWithXMLCoder">YES</bool> @@ -44,7 +44,7 @@ <nil key="NSViewClass"/> <string key="NSWindowContentMaxSize">{3.40282e+38, 3.40282e+38}</string> <object class="NSView" key="NSWindowView" id="1006"> - <reference key="NSNextResponder"/> + <nil key="NSNextResponder"/> <int key="NSvFlags">256</int> <object class="NSMutableArray" key="NSSubviews"> <bool key="EncodedWithXMLCoder">YES</bool> @@ -57,19 +57,18 @@ </object> </object> <string key="NSFrameSize">{750, 600}</string> - <reference key="NSSuperview"/> </object> <string key="NSScreenRect">{{0, 0}, {1440, 878}}</string> <string key="NSMaxSize">{3.40282e+38, 3.40282e+38}</string> </object> <object class="NSCustomView" id="1029219716"> - <nil key="NSNextResponder"/> + <reference key="NSNextResponder"/> <int key="NSvFlags">266</int> <object class="NSMutableArray" key="NSSubviews"> <bool key="EncodedWithXMLCoder">YES</bool> <object class="NSButton" id="131944810"> <reference key="NSNextResponder" ref="1029219716"/> - <int key="NSvFlags">300</int> + <int key="NSvFlags">-2147483356</int> <string key="NSFrame">{{295, 0}, {40, 27}}</string> <reference key="NSSuperview" ref="1029219716"/> <bool key="NSEnabled">YES</bool> @@ -96,7 +95,8 @@ </object> </object> </object> - <string key="NSFrameSize">{483, 25}</string> + <string key="NSFrameSize">{483, 36}</string> + <reference key="NSSuperview"/> <string key="NSClassName">TabStripView</string> </object> </object> @@ -244,9 +244,9 @@ <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> - <string>{{136, 256}, {750, 600}}</string> + <string>{{267, 167}, {750, 600}}</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> - <string>{{136, 256}, {750, 600}}</string> + <string>{{267, 167}, {750, 600}}</string> <boolean value="NO"/> <string>{196, 240}</string> <string>{{357, 418}, {480, 270}}</string> @@ -258,7 +258,7 @@ </object> <real value="0.000000e+00"/> <real value="0.000000e+00"/> - <string>{{160, 204}, {483, 25}}</string> + <string>{{138, 199}, {483, 36}}</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> diff --git a/chrome/app/nibs/en.lproj/TabView.xib b/chrome/app/nibs/en.lproj/TabView.xib index ff144f7..5cc215f 100644 --- a/chrome/app/nibs/en.lproj/TabView.xib +++ b/chrome/app/nibs/en.lproj/TabView.xib @@ -102,7 +102,7 @@ </object> <object class="NSProgressIndicator" id="58797509"> <reference key="NSNextResponder" ref="1005"/> - <int key="NSvFlags">1321</int> + <int key="NSvFlags">-2147482327</int> <object class="NSPSMatrix" key="NSDrawMatrix"/> <string key="NSFrame">{{131, 4}, {16, 16}}</string> <reference key="NSSuperview" ref="1005"/> @@ -113,24 +113,28 @@ <object class="NSButton" id="1054640993"> <reference key="NSNextResponder" ref="1005"/> <int key="NSvFlags">297</int> - <string key="NSFrame">{{134, 7}, {10, 10}}</string> + <string key="NSFrame">{{131, 5}, {15, 15}}</string> <reference key="NSSuperview" ref="1005"/> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="348599947"> <int key="NSCellFlags">67239424</int> <int key="NSCellFlags2">134217728</int> - <string key="NSContents">Close</string> + <string key="NSContents"/> <object class="NSFont" key="NSSupport"> <string key="NSName">LucidaGrande</string> <double key="NSSize">1.300000e+01</double> <int key="NSfFlags">1044</int> </object> <reference key="NSControlView" ref="1054640993"/> - <int key="NSButtonFlags">-2042347265</int> - <int key="NSButtonFlags2">134</int> + <int key="NSButtonFlags">139215103</int> + <int key="NSButtonFlags2">6</int> <object class="NSCustomResource" key="NSNormalImage"> <string key="NSClassName">NSImage</string> - <string key="NSResourceName">NSStopProgressTemplate</string> + <string key="NSResourceName">close_bar</string> + </object> + <object class="NSCustomResource" key="NSAlternateImage"> + <string key="NSClassName">NSImage</string> + <string key="NSResourceName">close_bar_p</string> </object> <string key="NSAlternateContents"/> <string key="NSKeyEquivalent"/> @@ -396,7 +400,7 @@ <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilderKit</string> <string>com.apple.InterfaceBuilderKit</string> - <string>{{831, 498}, {160, 25}}</string> + <string>{{799, 498}, {160, 25}}</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <boolean value="NO"/> <string>{628, 654}</string> @@ -436,6 +440,13 @@ <object class="NSMutableArray" key="referencedPartialClassDescriptions"> <bool key="EncodedWithXMLCoder">YES</bool> <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> + <string key="minorKey">browser/cocoa/tab_strip_model_observer_bridge.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> <string key="className">TabCell</string> <string key="superclassName">NSButtonCell</string> <object class="IBClassDescriptionSource" key="sourceIdentifier"> @@ -455,14 +466,12 @@ <object class="NSMutableArray" key="dict.sortedKeys"> <bool key="EncodedWithXMLCoder">YES</bool> <string>backgroundButton_</string> - <string>closeButton_</string> <string>progressIndicator_</string> <string>target_</string> </object> <object class="NSMutableArray" key="dict.values"> <bool key="EncodedWithXMLCoder">YES</bool> <string>NSButton</string> - <string>NSButton</string> <string>NSProgressIndicator</string> <string>id</string> </object> @@ -475,10 +484,6 @@ <object class="IBPartialClassDescription"> <string key="className">TabView</string> <string key="superclassName">NSView</string> - <object class="NSMutableDictionary" key="actions"> - <string key="NS.key.0">closeTab:</string> - <string key="NS.object.0">id</string> - </object> <object class="NSMutableDictionary" key="outlets"> <bool key="EncodedWithXMLCoder">YES</bool> <object class="NSMutableArray" key="dict.sortedKeys"> diff --git a/chrome/browser/cocoa/browser_window_controller.mm b/chrome/browser/cocoa/browser_window_controller.mm index 048b641..c9d325f 100644 --- a/chrome/browser/cocoa/browser_window_controller.mm +++ b/chrome/browser/cocoa/browser_window_controller.mm @@ -275,6 +275,18 @@ willPositionSheet:(NSWindow *)sheet return [tabStripController_ selectedTabGrowBoxRect]; } +- (void)dropTabView:(NSView *)view atIndex:(NSUInteger)index { + [tabStripController_ dropTabView:view atIndex:index]; +} + +- (NSView *)selectedTabView { + return [tabStripController_ selectedTabView]; +} + +- (TabStripController *)tabStripController { + return tabStripController_; +} + - (void)setIsLoading:(BOOL)isLoading { [toolbarController_ setIsLoading:isLoading]; } @@ -296,8 +308,8 @@ willPositionSheet:(NSWindow *)sheet [toolbarController_ focusLocationBar]; } -- (void)arrangeTabs { - NOTIMPLEMENTED(); +- (void)layoutTabs { + [tabStripController_ layoutTabs]; } - (TabWindowController*)detachTabToNewWindow:(TabView*)tabView { @@ -317,6 +329,8 @@ willPositionSheet:(NSWindow *)sheet gfx::Rect browserRect(windowRect.origin.x, windowRect.origin.y, windowRect.size.width, windowRect.size.height); + NSRect tabRect = [tabView frame]; + // Detach it from the source window, which just updates the model without // deleting the tab contents. This needs to come before creating the new // Browser because it clears the TabContents' delegate, which gets hooked @@ -336,15 +350,27 @@ willPositionSheet:(NSWindow *)sheet [newBrowser->window()->GetNativeHandle() delegate]; DCHECK(controller && [controller isKindOfClass:[TabWindowController class]]); + // Force the added tab to the right size (remove stretching) + tabRect.size.height = [TabStripController defaultTabHeight]; + NSView *newTabView = [controller selectedTabView]; + [newTabView setFrame:tabRect]; + return controller; } -- (void)insertPlaceholderForTab:(TabView*)tab atLocation:(NSInteger)xLocation { - NOTIMPLEMENTED(); + +- (void)insertPlaceholderForTab:(TabView*)tab + frame:(NSRect)frame + yStretchiness:(CGFloat)yStretchiness { + [tabStripController_ insertPlaceholderForTab:tab + frame:frame + yStretchiness:yStretchiness]; } - (void)removePlaceholder { - NOTIMPLEMENTED(); + [tabStripController_ insertPlaceholderForTab:nil + frame:NSZeroRect + yStretchiness:0]; } - (BOOL)isBookmarkBarVisible { diff --git a/chrome/browser/cocoa/tab_cell.mm b/chrome/browser/cocoa/tab_cell.mm index 0600c21..088f848 100644 --- a/chrome/browser/cocoa/tab_cell.mm +++ b/chrome/browser/cocoa/tab_cell.mm @@ -21,9 +21,6 @@ } - (NSBackgroundStyle)interiorBackgroundStyle { - // GTMThemeStyle style = - // [self isHighlighted] ? GTMThemeStyleTabBarSelected - // : GTMThemeStyleTabBarDeselected; return [[GTMTheme defaultTheme] interiorBackgroundStyleForStyle:GTMThemeStyleTabBarSelected active:YES]; @@ -97,7 +94,6 @@ active:active]; // Stroke with a translucent black [[NSColor colorWithCalibratedWhite:0.0 alpha:active ? 0.3 : 0.1] set]; - [path fill]; } [[NSGraphicsContext currentContext] saveGraphicsState]; @@ -105,17 +101,27 @@ [[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set]; [path setLineWidth:selected ? 2.0 : 1.0]; [path stroke]; + [[NSGraphicsContext currentContext] restoreGraphicsState]; [gradient drawInBezierPath:path angle:90.0]; - [[NSColor darkGrayColor] set]; + + if (!selected) { + [path addClip]; + NSRect borderRect, contentRect; + NSDivideRect(rect, &borderRect, &contentRect, 1, NSMaxYEdge); + [[NSColor colorWithCalibratedWhite:0.0 alpha:0.4] set]; + NSRectFillUsingOperation(borderRect, NSCompositeSourceOver); + } [[NSGraphicsContext currentContext] restoreGraphicsState]; // Inset where the text and favicon are drawn to keep them away from the - // sloping edges of the tab. + // sloping edges of the tab and the close box. int kInteriorInset = cellFrame.size.height / 2.0; - [self drawInteriorWithFrame:NSInsetRect(cellFrame, kInteriorInset, 0) + NSRect frame = NSInsetRect(cellFrame, kInteriorInset, 0); + frame.size.width -= 16; // Inset for close box + [self drawInteriorWithFrame:frame inView:controlView]; } diff --git a/chrome/browser/cocoa/tab_strip_controller.h b/chrome/browser/cocoa/tab_strip_controller.h index d4bd199..28e5ff0 100644 --- a/chrome/browser/cocoa/tab_strip_controller.h +++ b/chrome/browser/cocoa/tab_strip_controller.h @@ -11,6 +11,7 @@ #include "base/scoped_ptr.h" #import "chrome/browser/cocoa/tab_controller_target.h" +@class TabView; @class TabStripView; class TabStripModelObserverBridge; @@ -45,6 +46,11 @@ class ToolbarModel; // an array of TabControllers which manage the actual tab views. As above, // 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 + TabView* placeholderTab_; // weak. Tab being dragged + NSRect placeholderFrame_; // Frame to use + CGFloat placeholderStretchiness_; // Vertical force indicated by streching tab } // Initialize the controller with a view and browser that contains @@ -60,9 +66,29 @@ class ToolbarModel; // in the coordinate system of the content area of the currently selected tab. - (NSRect)selectedTabGrowBoxRect; +// Return the view for the currently selected tab. +- (NSView *)selectedTabView; + +// Drop a tab view at a new index in the array. +- (void)dropTabView:(NSView *)view atIndex:(NSUInteger)index; + // Given a tab view in the strip, return its index. Returns -1 if not present. - (NSInteger)indexForTabView:(NSView*)view; +// return the view at a given index +- (NSView*)viewAtIndex:(NSUInteger)index; + +// Set the placeholder for a dragged tab, allowing the |frame| and |strechiness| +// to be specified. This causes this tab to be rendered in an arbitrary position +- (void)insertPlaceholderForTab:(TabView*)tab + frame:(NSRect)frame + yStretchiness:(CGFloat)yStretchiness; + +// Force the tabs to rearrange themselves to reflect the current model +- (void)layoutTabs; + +// Default height for tabs. ++ (CGFloat)defaultTabHeight; @end #endif // CHROME_BROWSER_COCOA_TAB_STRIP_CONTROLLER_H_ diff --git a/chrome/browser/cocoa/tab_strip_controller.mm b/chrome/browser/cocoa/tab_strip_controller.mm index 524d11c..5d33a32 100644 --- a/chrome/browser/cocoa/tab_strip_controller.mm +++ b/chrome/browser/cocoa/tab_strip_controller.mm @@ -47,6 +47,10 @@ return self; } ++ (CGFloat)defaultTabHeight { + return 24.0; +} + // Finds the associated TabContentsController at the given |index| and swaps // out the sole child of the contentArea to display its contents. - (void)swapInTabAtIndex:(NSInteger)index { @@ -119,11 +123,29 @@ // Called when the user closes a tab. Asks the model to close the tab. - (void)closeTab:(id)sender { - int index = [self indexForTabView:sender]; - if (index >= 0 && tabModel_->ContainsIndex(index)) - tabModel_->CloseTabContentsAt(index); + if ([self numberOfTabViews] > 1) { + int index = [self indexForTabView:sender]; + if (index >= 0 && tabModel_->ContainsIndex(index)) + tabModel_->CloseTabContentsAt(index); + } else { + // Use the standard window close if this is the last tab + // this prevents the tab from being removed from the model until after + // the window dissapears + [[tabView_ window] performClose:nil]; + } +} + + +- (void)insertPlaceholderForTab:(TabView*)tab + frame:(NSRect)frame + yStretchiness:(CGFloat)yStretchiness { + placeholderTab_ = tab; + placeholderFrame_ = frame; + placeholderStretchiness_ = yStretchiness; + [self layoutTabs]; } + // Lay out all tabs in the order of their TabContentsControllers, which matches // the ordering in the TabStripModel. This call isn't that expensive, though // it is O(n) in the number of tabs. Tabs will animate to their new position @@ -152,24 +174,45 @@ kMaxTabWidth), kMinTabWidth); + CGFloat minX = NSMinX(placeholderFrame_); + + NSUInteger i = 0; + NSInteger gap = -1; for (TabController* tab in tabArray_.get()) { - // BOOL isPlaceholder = ![[[tab view] superview] isEqual:tabView_]; - BOOL isPlaceholder = NO; + BOOL isPlaceholder = [[tab view] isEqual:placeholderTab_]; NSRect tabFrame = [[tab view] frame]; - // If the tab is all the way on the left, we consider it a new tab. We - // need to show it, but not animate the movement. We do however want to - // animate the display. - BOOL newTab = NSMinX(tabFrame) == 0; + tabFrame.size.height = [[self class] defaultTabHeight]; + tabFrame.origin.y = 0; + tabFrame.origin.x = offset; + + // If the tab is hidden, we consider it a new tab. We make it visible + // and animate it in. + BOOL newTab = [[tab view] isHidden]; if (newTab) { - id visibilityTarget = visible ? [[tab view] animator] : [tab view]; - [visibilityTarget setHidden:NO]; + [[tab view] setHidden:NO]; } - tabFrame.origin = NSMakePoint(offset, 0); - if (!isPlaceholder) { - // Set the tab's new frame and animate the tab to its new location. Don't - // animate if the window isn't visible or if the tab is new. - BOOL animate = visible && !newTab; - id frameTarget = animate ? [[tab view] animator] : [tab view]; + + if (isPlaceholder) { + tabFrame.origin.x = placeholderFrame_.origin.x; + tabFrame.size.height += 10.0 * placeholderStretchiness_; + [[tab view] setFrame:tabFrame]; + continue; + } else { + // If our left edge is to the left of the placeholder's left, but our mid + // is to the right of it we should slide over to make space for it. + if (placeholderTab_ && gap < 0 && NSMidX(tabFrame) > minX) { + gap = i; + offset += NSWidth(tabFrame); + offset -= kTabOverlap; + tabFrame.origin.x = offset; + } + + // Animate the tab in by putting it below the horizon. + if (newTab && visible) { + [[tab view] setFrame:NSOffsetRect(tabFrame, 0, -NSHeight(tabFrame))]; + } + + id frameTarget = visible ? [[tab view] animator] : [tab view]; tabFrame.size.width = [tab selected] ? kMaxTabWidth : baseTabWidth; [frameTarget setFrame:tabFrame]; } @@ -178,11 +221,13 @@ offset += NSWidth(tabFrame); offset -= kTabOverlap; } + i++; } // 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]; } @@ -222,6 +267,9 @@ TabController* newController = [self newTab]; [tabArray_ insertObject:newController atIndex:index]; NSView* newView = [newController view]; + [newView setFrame:NSOffsetRect([newView frame], + 0, [[self class] defaultTabHeight])]; + [tabView_ addSubview:newView positioned:inForeground ? NSWindowAbove : NSWindowBelow relativeTo:nil]; @@ -301,6 +349,16 @@ [updatedController tabDidChange:contents]; } +- (NSView *)selectedTabView { + int selectedIndex = tabModel_->selected_index(); + return [self viewAtIndex:selectedIndex]; +} + +- (void)dropTabView:(NSView *)view atIndex:(NSUInteger)index { + // TODO(pinkerton): implement drop + NOTIMPLEMENTED(); +} + // Return the rect, in WebKit coordinates (flipped), of the window's grow box // in the coordinate system of the content area of the currently selected tab. - (NSRect)selectedTabGrowBoxRect { diff --git a/chrome/browser/cocoa/tab_strip_view.mm b/chrome/browser/cocoa/tab_strip_view.mm index d63d6ef..fd61766 100644 --- a/chrome/browser/cocoa/tab_strip_view.mm +++ b/chrome/browser/cocoa/tab_strip_view.mm @@ -18,7 +18,7 @@ NSRect boundsRect = [self bounds]; NSRect borderRect, contentRect; NSDivideRect(boundsRect, &borderRect, &contentRect, 1, NSMinYEdge); - [[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set]; + [[NSColor colorWithCalibratedWhite:0.0 alpha:0.3] set]; NSRectFillUsingOperation(borderRect, NSCompositeSourceOver); } diff --git a/chrome/browser/cocoa/tab_view.mm b/chrome/browser/cocoa/tab_view.mm index 2aef856..fd31652 100644 --- a/chrome/browser/cocoa/tab_view.mm +++ b/chrome/browser/cocoa/tab_view.mm @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "chrome/browser/cocoa/tab_view.h" - #include "chrome/browser/cocoa/tab_window_controller.h" @implementation TabView @@ -44,6 +43,8 @@ // 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]; @@ -53,26 +54,63 @@ [[controller_ target] performSelector:[controller_ action] withObject:self]; - // TODO(pinkerton): necessary to pre-arrange the tabs here? - // Resolve overlay back to original window. NSWindow* sourceWindow = [self window]; if ([sourceWindow isKindOfClass:[NSPanel class]]) { sourceWindow = [sourceWindow parentWindow]; } + TabWindowController* sourceController = [sourceWindow windowController]; - TabWindowController* draggedController = nil; - TabWindowController* targetController = nil; // 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. - BOOL isLastRemainingTab = [sourceController numberOfTabs] == 1; + // 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; - NSWindow* dragWindow = nil; - NSWindow* dragOverlay = nil; BOOL dragging = YES; BOOL moved = NO; + NSPoint lastPoint = + [[theEvent window] convertBaseToScreen:[theEvent locationInWindow]]; + + // 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; + } + } + } + + [sourceController removePlaceholder]; + + TabWindowController* draggedController = nil; + TabWindowController* targetController = nil; + + NSWindow* dragWindow = nil; + NSWindow* dragOverlay = nil; + // Do not start dragging until the user has "torn" the tab off by // moving more than 3 pixels. BOOL torn = NO; @@ -81,9 +119,6 @@ NSDate* targetDwellDate = nil; // The date this target was first chosen NSMutableArray* targets = [NSMutableArray array]; - NSPoint lastPoint = - [[theEvent window] convertBaseToScreen:[theEvent locationInWindow]]; - while (dragging) { theEvent = [NSApp nextEventMatchingMask:NSLeftMouseUpMask | NSLeftMouseDraggedMask @@ -91,16 +126,13 @@ inMode:NSDefaultRunLoopMode dequeue:YES]; NSPoint thisPoint = [NSEvent mouseLocation]; - // TODO(alcor): Pinkerton indicated that cole is adding a more - // formal concept of "magnetism" to tab detachment, which would - // be an alternative solution. 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; + && [theEvent type] == NSLeftMouseDragged) { + continue; } torn = YES; } @@ -125,7 +157,11 @@ for (TabWindowController* target in targets) { NSRect windowFrame = [[target window] frame]; if (NSPointInRect(thisPoint, windowFrame)) { - if (NSPointInRect(thisPoint, [[target tabStripView] frame])) { + NSRect tabStripFrame = [[target tabStripView] frame]; + tabStripFrame = [[target tabStripView] convertRectToBase:tabStripFrame]; + tabStripFrame.origin = [[target window] + convertBaseToScreen:tabStripFrame.origin]; + if (NSPointInRect(thisPoint, tabStripFrame)) { newTarget = target; } break; @@ -136,7 +172,7 @@ // target and reset how long we've been hovering over this new one. if (targetController != newTarget) { targetDwellDate = [NSDate date]; - [targetController arrangeTabs]; + [targetController removePlaceholder]; targetController = newTarget; } @@ -161,8 +197,12 @@ [draggedController showOverlay]; dragOverlay = [draggedController overlayWindow]; - if (![targets count]) - [dragOverlay setHasShadow:NO]; + 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; @@ -173,9 +213,7 @@ // 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) { - [[dragWindow animator] setAlphaValue:1.0]; - } else { + if (targetController) { if (![[targetController window] isKeyWindow]) { // && ([targetDwellDate timeIntervalSinceNow] < -REQUIRED_DWELL)) { [[targetController window] makeKeyAndOrderFront:nil]; @@ -186,21 +224,30 @@ // 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]]; - int x = NSWidth([self bounds]) / 2 + point.x - dropTabFrame.origin.x; - [targetController insertPlaceholderForTab:self atLocation:x]; - [targetController arrangeTabs]; - - if (!targetController) - [dragWindow makeKeyAndOrderFront:nil]; - [[dragWindow animator] setAlphaValue:targetController ? 0.0 : 0.333]; - - [[[draggedController overlayWindow] animator] - setAlphaValue:targetController ? 0.85 : 1.0]; - // [setAlphaValue:targetController ? 0.0 : 0.6]; + [targetController insertPlaceholderForTab:self + frame:tabFrame + yStretchiness:0]; + [targetController layoutTabs]; + } else { + [dragWindow makeKeyAndOrderFront:nil]; } + + [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; @@ -212,38 +259,38 @@ // and clean up. if (moved) { TabWindowController *dropController = targetController; +#if 1 + dropController = nil; // Don't allow drops on other windows for now +#endif if (dropController) { -#if 0 -// TODO(alcor/pinkerton): hookup drops on existing windows + // TODO(alcor/pinkerton): hookup drops on existing windows NSRect adjustedFrame = [self bounds]; NSRect dropTabFrame = [[dropController tabStripView] frame]; adjustedFrame.origin = [self convertPointToBase:NSZeroPoint]; adjustedFrame.origin = - [sourceWindow convertBaseToScreen:adjustedFrame.origin]; + [sourceWindow convertBaseToScreen:adjustedFrame.origin]; adjustedFrame.origin.x = adjustedFrame.origin.x - dropTabFrame.origin.x; -// adjustedFrame.origin.y = adjustedFrame.origin.y - dropTabFrame.origin.y; -// adjustedFrame.size.height += adjustedFrame.origin.y; + //adjustedFrame.origin.y = adjustedFrame.origin.y - dropTabFrame.origin.y; + //adjustedFrame.size.height += adjustedFrame.origin.y; adjustedFrame.origin.y = 0; - // TODO(alcor): get add tab stuff working // [dropController addTab:tab_]; - [self setFrame:adjustedFrame]; - [dropController arrangeTabs]; + [dropController layoutTabs]; [draggedController close]; [dropController showWindow:nil]; -#endif } else { + [targetController removePlaceholder]; [[dragWindow animator] setAlphaValue:1.0]; [dragOverlay setHasShadow:NO]; - [draggedController removeOverlayAfterDelay: - [[NSAnimationContext currentContext] duration]]; + [dragWindow setHasShadow:YES]; + [draggedController removeOverlay]; [dragWindow makeKeyAndOrderFront:nil]; [[draggedController window] setLevel:NSNormalWindowLevel]; - [draggedController arrangeTabs]; + [draggedController layoutTabs]; } - [sourceController arrangeTabs]; + [sourceController layoutTabs]; } } diff --git a/chrome/browser/cocoa/tab_window_controller.h b/chrome/browser/cocoa/tab_window_controller.h index 60d13de..e86c829 100644 --- a/chrome/browser/cocoa/tab_window_controller.h +++ b/chrome/browser/cocoa/tab_window_controller.h @@ -47,7 +47,7 @@ // appropriate. // Layout the tabs based on the current ordering of the model. -- (void)arrangeTabs; +- (void)layoutTabs; // Creates a new window by pulling the given tab out and placing it in // the new window. Returns the controller for the new window. The size of the @@ -55,8 +55,9 @@ - (TabWindowController*)detachTabToNewWindow:(TabView*)tabView; // Make room in the tab strip for |tab| at the given x coordinate. -// TODO(pink): is |tab| a necessary parameter? -- (void)insertPlaceholderForTab:(TabView*)tab atLocation:(NSInteger)xLocation; +- (void)insertPlaceholderForTab:(TabView*)tab + frame:(NSRect)frame + yStretchiness:(CGFloat)yStretchiness; // Removes the placeholder installed by |-insertPlaceholderForTab:atLocation:|. - (void)removePlaceholder; @@ -65,6 +66,12 @@ // dragging the only tab in the window. - (NSInteger)numberOfTabs; +// Return the view of the selected tab. +- (NSView *)selectedTabView; + +// Drop a given tab view at a new index. +- (void)dropTabView:(NSView *)view atIndex:(NSUInteger)index; + // The title of the selected tab. - (NSString*)selectedTabTitle; diff --git a/chrome/browser/cocoa/tab_window_controller.mm b/chrome/browser/cocoa/tab_window_controller.mm index 89ae67f..f7e6079 100644 --- a/chrome/browser/cocoa/tab_window_controller.mm +++ b/chrome/browser/cocoa/tab_window_controller.mm @@ -79,13 +79,10 @@ [contentView addSubview:[self tabStripView]]; cachedContentView_ = [[self window] contentView]; [self moveViewsBetweenWindowAndOverlay:useOverlay]; - [overlayWindow_ setHasShadow:YES]; [[self window] addChildWindow:overlayWindow_ ordered:NSWindowAbove]; [overlayWindow_ orderFront:nil]; - [[self window] setHasShadow:NO]; } else if (!useOverlay && overlayWindow_) { DCHECK(cachedContentView_); - [[self window] setHasShadow:YES]; [[self window] setContentView:cachedContentView_]; [self moveViewsBetweenWindowAndOverlay:useOverlay]; [[self window] makeFirstResponder:cachedContentView_]; @@ -102,7 +99,16 @@ return overlayWindow_; } -- (void)arrangeTabs { +- (void)dropTabView:(NSView *)view atIndex:(NSUInteger)index { + NOTIMPLEMENTED(); +} + +- (NSView *)selectedTabView { + NOTIMPLEMENTED(); + return nil; +} + +- (void)layoutTabs { // subclass must implement NOTIMPLEMENTED(); } @@ -113,7 +119,9 @@ return NULL; } -- (void)insertPlaceholderForTab:(TabView*)tab atLocation:(NSInteger)xLocation { +- (void)insertPlaceholderForTab:(TabView*)tab + frame:(NSRect)frame + yStretchiness:(CGFloat)yStretchiness { // subclass must implement NOTIMPLEMENTED(); } |