diff options
author | rowanbeentje <rowan@beent.je> | 2010-06-30 23:44:55 +0000 |
---|---|---|
committer | rowanbeentje <rowan@beent.je> | 2010-06-30 23:44:55 +0000 |
commit | 219afc56c02b508fbb6d706dfed4e1a1ccc949d4 (patch) | |
tree | de4e473de2a5fd1063c671733a5f7552e6080ba0 /Frameworks/PSMTabBar/PSMTabDragAssistant.m | |
parent | ba332e64c29622e0b69412cb2fea639182ac1d1c (diff) | |
download | sequelpro-219afc56c02b508fbb6d706dfed4e1a1ccc949d4.tar.gz sequelpro-219afc56c02b508fbb6d706dfed4e1a1ccc949d4.tar.bz2 sequelpro-219afc56c02b508fbb6d706dfed4e1a1ccc949d4.zip |
Improve tab functionality, behaviour and interaction.
PSMTabBar general improvements:
- Fix a phantom tab appearing in windows a tab was just dragged out of
- Add support for a new control usersSafariStyleDragging property; this causes tabs being dragged inside a tab bar to snap to the tab bar, be drawn at full transparency (also no longer darkening the placeholder position), and alters tab ordering within the tab bar to be based on the tab position rather than the mouse position, for a more Mac-like reordering feel.
- Add support for dragging items onto the menubar to cancel the drag
- Alter the image of the dragged tab to use a tab drawn onto a transparent background instead of snapshotting a rectangle around the tab, improving drag appearance
- Allow tabs to be dragged partially off screen and keep their position instead of snapping back fully onto the screen
- Improve behaviour when dragging tabs out of and back into tab bars, or into new windows, resizing the tabs in the target tab bar to improve display and no longer intermittently showing close buttons while dragging
- Pull windows to the front as tabs are dragged onto their tab bars
- Abstract Custom Query Editor text snippet code, adding a delegate method so code could be moved to the SP window manager.
Sequel Pro tab styling improvements:
- Improve and clean up tab drawing code
- Draw background tabs with shadows as appropriate
- Improve logic for how background tab edges are drawn, handling edges cases better (active tab in overflow menu etc) and vastly improving drawing when a tab is being dragged (respect placeholder position when stacking tabs, draw edges on either side)
Sequel Pro tab behaviour improvements:
- Improve show/hide tab bar interaction
- Improve window positioning after creating new windows via a drag
- Alter tab dragging out of the tab bar to use an image based on the full window appearance - looks better, and fixes issues like the tab bar background not being drawn while dragging
Diffstat (limited to 'Frameworks/PSMTabBar/PSMTabDragAssistant.m')
-rw-r--r-- | Frameworks/PSMTabBar/PSMTabDragAssistant.m | 191 |
1 files changed, 157 insertions, 34 deletions
diff --git a/Frameworks/PSMTabBar/PSMTabDragAssistant.m b/Frameworks/PSMTabBar/PSMTabDragAssistant.m index 9c9eb2a8..29fa036c 100644 --- a/Frameworks/PSMTabBar/PSMTabDragAssistant.m +++ b/Frameworks/PSMTabBar/PSMTabDragAssistant.m @@ -153,6 +153,10 @@ static PSMTabDragAssistant *sharedDragAssistant = nil; - (void)startDraggingCell:(PSMTabBarCell *)cell fromTabBar:(PSMTabBarControl *)control withMouseDownEvent:(NSEvent *)event { + + // Ensure the window is frontmost + [[control window] makeKeyAndOrderFront:self]; + [self setIsDragging:YES]; [self setSourceTabBar:control]; [self setDestinationTabBar:control]; @@ -161,7 +165,8 @@ static PSMTabDragAssistant *sharedDragAssistant = nil; [self setDraggedCellIndex:[[control cells] indexOfObject:cell]]; NSRect cellFrame = [cell frame]; - // list of widths for animation + + // Generate a list of widths for animation NSInteger i; CGFloat cellStepSize = ([control orientation] == PSMTabBarHorizontalOrientation) ? (cellFrame.size.width + 6) : (cellFrame.size.height + 1); for (i = 0; i < kPSMTabDragAnimationSteps - 1; i++) { @@ -198,7 +203,7 @@ static PSMTabDragAssistant *sharedDragAssistant = nil; if ([control delegate] && [[control delegate] respondsToSelector:@selector(tabView:shouldDropTabViewItem:inTabBar:)] && [[control delegate] tabView:[control tabView] shouldDropTabViewItem:[[self draggedCell] representedObject] inTabBar:nil]) { _currentTearOffStyle = [control tearOffStyle]; - _draggedTab = [[PSMTabDragWindowController alloc] initWithImage:dragImage styleMask:NSBorderlessWindowMask tearOffStyle:_currentTearOffStyle]; + _draggedTab = [[PSMTabDragWindowController alloc] initWithImage:dragImage styleMask:NSBorderlessWindowMask tearOffStyle:_currentTearOffStyle initialAlpha:[control usesSafariStyleDragging]?1:kPSMTabDragWindowAlpha]; cellFrame.origin.y -= cellFrame.size.height; [control dragImage:[[[NSImage alloc] initWithSize:NSMakeSize(1, 1)] autorelease] at:cellFrame.origin offset:offset event:event pasteboard:pboard source:control slideBack:NO]; @@ -211,20 +216,47 @@ static PSMTabDragAssistant *sharedDragAssistant = nil; - (void)draggingEnteredTabBar:(PSMTabBarControl *)control atPoint:(NSPoint)mouseLoc { + + // Bring the new tab window to the front + [[control window] makeKeyAndOrderFront:self]; + if (_currentTearOffStyle == PSMTabBarTearOffMiniwindow && ![self destinationTabBar]) { [_draggedTab switchImages]; } + + // If this is not the starting drag bar... + if ([self sourceTabBar] != [self destinationTabBar] && control != [self destinationTabBar]) { + + // Add a single placeholder to the tab bar and tell the new tab bar to update. + // The placeholder is later removed by distributePlaceholdersInTabBar:. + PSMTabBarCell *pc = [[[PSMTabBarCell alloc] initPlaceholderWithFrame:[[self draggedCell] frame] expanded:NO inControlView:control] autorelease]; + [[control cells] addObject:pc]; + [control update:NO]; + + // Deselect any currently selected tabs after the update + for (PSMTabBarCell *aCell in [control cells]) { + if ([aCell tabState] & PSMTab_SelectedMask) { + [aCell setState:NSOffState]; + [aCell setTabState:PSMTab_PositionMiddleMask]; + break; + } + } + } [self setDestinationTabBar:control]; [self setCurrentMouseLoc:mouseLoc]; - // hide UI buttons - [[control overflowPopUpButton] setHidden:YES]; - [[control addTabButton] setHidden:YES]; + + [_participatingTabBars addObject:control]; + + // Add placeholders if necessary if ([[control cells] count] == 0 || ![[[control cells] objectAtIndex:0] isPlaceholder]) { [self distributePlaceholdersInTabBar:control]; } - [_participatingTabBars addObject:control]; + // hide UI buttons + [[control overflowPopUpButton] setHidden:YES]; + [[control addTabButton] setHidden:YES]; + //tell the drag window to display only the header if there is one if (_currentTearOffStyle == PSMTabBarTearOffAlphaWindow && _draggedView) { if (_fadeTimer) { @@ -264,8 +296,13 @@ static PSMTabDragAssistant *sharedDragAssistant = nil; NSUInteger styleMask; NSImage *viewImage = [self _imageForViewOfCell:[self draggedCell] styleMask:&styleMask]; - _draggedView = [[PSMTabDragWindowController alloc] initWithImage:viewImage styleMask:styleMask tearOffStyle:PSMTabBarTearOffAlphaWindow]; + _draggedView = [[PSMTabDragWindowController alloc] initWithImage:viewImage styleMask:styleMask tearOffStyle:PSMTabBarTearOffAlphaWindow initialAlpha:[control usesSafariStyleDragging]?1:kPSMTabDragWindowAlpha]; [[_draggedView window] setAlphaValue:0.0]; + + // Inform the delegate a new drag window was created to allow any changes + if ([control delegate] && [[control delegate] respondsToSelector:@selector(tabViewDragWindowCreated:)]) { + [[control delegate] tabViewDragWindowCreated:[_draggedView window]]; + } } NSPoint windowOrigin = [[control window] frame].origin; @@ -303,7 +340,7 @@ static PSMTabDragAssistant *sharedDragAssistant = nil; [[[self sourceTabBar] window] setAlphaValue:0.0]; if ([_sourceTabBar tearOffStyle] == PSMTabBarTearOffAlphaWindow) { - [[_draggedView window] setAlphaValue:kPSMTabDragWindowAlpha]; + [[_draggedView window] setAlphaValue:kPSMTabDragWindowAlpha]; } else { #warning fix me - what should we do when the last tab is dragged as a miniwindow? } @@ -333,6 +370,7 @@ static PSMTabDragAssistant *sharedDragAssistant = nil; // move actual NSTabViewItem if ([self sourceTabBar] != [self destinationTabBar]) { + //remove the tracking rects and bindings registered on the old tab [[self sourceTabBar] removeTrackingRect:[[self draggedCell] closeButtonTrackingTag]]; [[self sourceTabBar] removeTrackingRect:[[self draggedCell] cellTrackingTag]]; @@ -404,12 +442,22 @@ static PSMTabDragAssistant *sharedDragAssistant = nil; { if ([self isDragging]) { // means there was not a successful drop (performDragOperation) id sourceDelegate = [[self sourceTabBar] delegate]; - - //split off the dragged tab into a new window - if ([self destinationTabBar] == nil && - sourceDelegate && [sourceDelegate respondsToSelector:@selector(tabView:shouldDropTabViewItem:inTabBar:)] && - [sourceDelegate tabView:[[self sourceTabBar] tabView] shouldDropTabViewItem:[[self draggedCell] representedObject] inTabBar:nil] && - [sourceDelegate respondsToSelector:@selector(tabView:newTabBarForDraggedTabViewItem:atPoint:)]) { + + // Extract the menu bar rect + NSScreen *menuBarScreen = [[NSScreen screens] objectAtIndex:0]; + NSRect menuBarRect = [menuBarScreen frame]; + menuBarRect.origin.y = menuBarRect.size.height; + menuBarRect.size.height = 22; + + // Split off the dragged tab into a new window. + // Do this if there's no destination tab bar, the delegate approves it, and the delegate supports it - and + // not if the drag ended in the menu bar (which acts as a cancel) + if ([self destinationTabBar] == nil + && sourceDelegate && [sourceDelegate respondsToSelector:@selector(tabView:shouldDropTabViewItem:inTabBar:)] && + [sourceDelegate tabView:[[self sourceTabBar] tabView] shouldDropTabViewItem:[[self draggedCell] representedObject] inTabBar:nil] + && [sourceDelegate respondsToSelector:@selector(tabView:newTabBarForDraggedTabViewItem:atPoint:)] + && !NSPointInRect(aPoint, menuBarRect)) + { PSMTabBarControl *control = [sourceDelegate tabView:[[self sourceTabBar] tabView] newTabBarForDraggedTabViewItem:[[self draggedCell] representedObject] atPoint:aPoint]; if (control) { @@ -432,7 +480,11 @@ static PSMTabDragAssistant *sharedDragAssistant = nil; [control update:NO]; //make sure the new tab is set in the correct position if (_currentTearOffStyle == PSMTabBarTearOffAlphaWindow) { + + // Grab the window frame, and show - which moves it fully onto screen - before restoring + NSRect draggedWindowFrame = [[control window] frame]; [[control window] makeKeyAndOrderFront:nil]; + [[control window] setFrame:draggedWindowFrame display:YES]; } else { //center the window over where we ended dragging [self _expandWindow:[control window] atPoint:[NSEvent mouseLocation]]; @@ -447,8 +499,15 @@ static PSMTabDragAssistant *sharedDragAssistant = nil; } } else { + // put cell back [[[self sourceTabBar] cells] insertObject:[self draggedCell] atIndex:[self draggedCellIndex]]; + [[[self sourceTabBar] window] makeKeyAndOrderFront:self]; + + // Restore the window alpha if appropriate + if ([[[self sourceTabBar] tabView] numberOfTabViewItems]) { + [[[self sourceTabBar] window] setAlphaValue:1.0]; + } } [[NSNotificationCenter defaultCenter] postNotificationName:PSMTabDragDidEndNotification object:nil]; @@ -477,21 +536,24 @@ static PSMTabDragAssistant *sharedDragAssistant = nil; _centersDragWindows = NO; - [self setIsDragging:NO]; + [_animationTimer invalidate]; + _animationTimer = nil; + [self removeAllPlaceholdersFromTabBar:[self sourceTabBar]]; [self setSourceTabBar:nil]; [self setDestinationTabBar:nil]; + NSEnumerator *e = [_participatingTabBars objectEnumerator]; PSMTabBarControl *tabBar; while ( (tabBar = [e nextObject]) ) { [self removeAllPlaceholdersFromTabBar:tabBar]; } [_participatingTabBars removeAllObjects]; - [self setDraggedCell:nil]; - [_animationTimer invalidate]; - _animationTimer = nil; + + [self setDraggedCell:nil]; [_sineCurveWidths removeAllObjects]; [self setTargetCell:nil]; + [self setIsDragging:NO]; } - (void)draggingBeganAt:(NSPoint)aPoint @@ -522,6 +584,22 @@ static PSMTabDragAssistant *sharedDragAssistant = nil; frame.origin.y -= frame.size.height / 2; [[_draggedTab window] setFrame:frame display:NO]; } else { + + // If there is a destination tab bar set to snapping, snap the tab to it. + if ([self destinationTabBar] + && [[self destinationTabBar] usesSafariStyleDragging] + && [[self destinationTabBar] orientation] == PSMTabBarHorizontalOrientation) + { + NSRect windowFrame = [[[self destinationTabBar] window] frame]; + NSPoint dragPointInWindow = [[self destinationTabBar] convertPoint:aPoint fromView:nil]; + + // Vertical snapping + aPoint.y += windowFrame.origin.y + dragPointInWindow.y; + + // Horizontal constraining/snapping + if (dragPointInWindow.x - windowFrame.origin.x < 0) aPoint.x = windowFrame.origin.x; + if (dragPointInWindow.x - windowFrame.origin.x + [[_draggedTab window] frame].size.width > windowFrame.size.width) aPoint.x = windowFrame.origin.x + windowFrame.size.width - [[_draggedTab window] frame].size.width; + } [[_draggedTab window] setFrameTopLeftPoint:aPoint]; } @@ -554,15 +632,16 @@ static PSMTabDragAssistant *sharedDragAssistant = nil; { CGFloat value = [[_draggedView window] alphaValue]; NSWindow *tabWindow = [_draggedTab window], *viewWindow = [_draggedView window]; + float tabWindowAlphaValue = [[self destinationTabBar] usesSafariStyleDragging]?1:kPSMTabDragWindowAlpha; if (value <= 0.0) { [viewWindow setAlphaValue:0.0]; - [tabWindow setAlphaValue:kPSMTabDragWindowAlpha]; + [tabWindow setAlphaValue:tabWindowAlphaValue]; [timer invalidate]; _fadeTimer = nil; } else { - if ([tabWindow alphaValue] < kPSMTabDragWindowAlpha) { + if ([tabWindow alphaValue] < tabWindowAlphaValue) { [tabWindow setAlphaValue:[tabWindow alphaValue] + kPSMTabDragAlphaInterval]; } [viewWindow setAlphaValue:value - kPSMTabDragAlphaInterval]; @@ -693,33 +772,76 @@ static PSMTabDragAssistant *sharedDragAssistant = nil; NSMutableArray *cells = [control cells]; NSInteger i, cellCount = [cells count]; CGFloat position = [control orientation] == PSMTabBarHorizontalOrientation ? [[control style] leftMarginForTabBarControl] : [[control style] topMarginForTabBarControl]; - - // identify target cell - // mouse at beginning of tabs - NSPoint mouseLoc = [self currentMouseLoc]; + if ([self destinationTabBar] == control) { removeFlag = NO; - if (mouseLoc.x < [[control style] leftMarginForTabBarControl]) { + + // Determine the location of the point to use. + NSPoint targetPoint; + if ([control usesSafariStyleDragging]) { + NSRect draggedTabWindowFrame = [[_draggedTab window] contentRectForFrameRect:[[_draggedTab window] frame]]; + NSRect controlWindowFrame = [[control window] contentRectForFrameRect:[[control window] frame]]; + NSPoint tabTopLeftInWindowCoords = NSMakePoint(draggedTabWindowFrame.origin.x - controlWindowFrame.origin.x, controlWindowFrame.origin.y + (2*controlWindowFrame.size.height) - draggedTabWindowFrame.origin.y); + targetPoint = [control convertPoint:tabTopLeftInWindowCoords fromView:nil]; + targetPoint.x += (draggedTabWindowFrame.size.width / 2.0); + targetPoint.y = 0 - (draggedTabWindowFrame.size.height / 2.0) - targetPoint.y; + } else { + targetPoint = [self currentMouseLoc]; + } + + if (targetPoint.x < [[control style] leftMarginForTabBarControl]) { [self setTargetCell:[cells objectAtIndex:0]]; } else { + + // Identify which cell the mouse is over NSRect overCellRect; - PSMTabBarCell *overCell = [control cellForPoint:mouseLoc cellFrame:&overCellRect]; + PSMTabBarCell *overCell = [control cellForPoint:targetPoint cellFrame:&overCellRect]; if (overCell) { - // mouse among cells - placeholder + + // Mouse is among cells - placeholder if ([overCell isPlaceholder]) { [self setTargetCell:overCell]; + + // Non-placeholder cells - horizontal orientation } else if ([control orientation] == PSMTabBarHorizontalOrientation) { - // non-placeholders - horizontal orientation - if (mouseLoc.x < (overCellRect.origin.x + (overCellRect.size.width / 2.0))) { - // mouse on left side of cell - [self setTargetCell:[cells objectAtIndex:([cells indexOfObject:overCell] - 1)]]; + + // Handle Safari-style dragging + if ([control usesSafariStyleDragging]) { + + // Determine the index of the tab the dragged tab is over + NSUInteger overCellIndex = [cells indexOfObject:overCell]; + + // Ensure that drag changes aren't as a result of an animation + NSInteger currentCellStep = [[cells objectAtIndex:(overCellIndex - 1)] currentStep]; + if (!currentCellStep || currentCellStep == kPSMTabDragAnimationSteps - 1) { + + // Center of the tab is past the edge of the tab to the left + if (targetPoint.x < (overCellRect.origin.x + overCellRect.size.width) + && targetPoint.x > (overCellRect.origin.x + overCellRect.size.width/2.0)) + { + [self setTargetCell:[cells objectAtIndex:(overCellIndex - 1)]]; + + // Center of the tab is past the edge of the tab to the right + } else if (targetPoint.x > overCellRect.origin.x) { + [self setTargetCell:[cells objectAtIndex:(overCellIndex + 1)]]; + } + } + + // Handle old-style dragging based on mouse position } else { - // mouse on right side of cell - [self setTargetCell:[cells objectAtIndex:([cells indexOfObject:overCell] + 1)]]; + + // Mouse is over the left side of the cell + if (targetPoint.x < (overCellRect.origin.x + (overCellRect.size.width / 2.0))) { + [self setTargetCell:[cells objectAtIndex:([cells indexOfObject:overCell] - 1)]]; + + // Otherwise the mouse is over the right side of the cell + } else { + [self setTargetCell:[cells objectAtIndex:([cells indexOfObject:overCell] + 1)]]; + } } } else { // non-placeholders - vertical orientation - if (mouseLoc.y < (overCellRect.origin.y + (overCellRect.size.height / 2.0))) { + if (targetPoint.y < (overCellRect.origin.y + (overCellRect.size.height / 2.0))) { // mouse on top of cell [self setTargetCell:[cells objectAtIndex:([cells indexOfObject:overCell] - 1)]]; } else { @@ -823,6 +945,7 @@ static PSMTabDragAssistant *sharedDragAssistant = nil; for (i = (cellCount - 1); i >= 0; i--) { PSMTabBarCell *cell = [[control cells] objectAtIndex:i]; if ([cell isPlaceholder]) { + [NSObject cancelPreviousPerformRequestsWithTarget:cell]; [control removeTabForCell:cell]; } } |