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

Unified Diff: chrome/browser/ui/cocoa/tab_view.mm

Issue 6362007: [Mac] Organize all tab/tab strip files into chrome/browser/ui/cocoa/tabs/.... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 11 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/ui/cocoa/tab_view.h ('k') | chrome/browser/ui/cocoa/tab_view_unittest.mm » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/ui/cocoa/tab_view.mm
===================================================================
--- chrome/browser/ui/cocoa/tab_view.mm (revision 71805)
+++ chrome/browser/ui/cocoa/tab_view.mm (working copy)
@@ -1,1056 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "chrome/browser/ui/cocoa/tab_view.h"
-
-#include "base/logging.h"
-#import "base/mac/mac_util.h"
-#include "base/mac/scoped_cftyperef.h"
-#include "chrome/browser/accessibility/browser_accessibility_state.h"
-#include "chrome/browser/themes/browser_theme_provider.h"
-#import "chrome/browser/ui/cocoa/tab_controller.h"
-#import "chrome/browser/ui/cocoa/tab_window_controller.h"
-#import "chrome/browser/ui/cocoa/themed_window.h"
-#import "chrome/browser/ui/cocoa/view_id_util.h"
-#include "grit/theme_resources.h"
-
-namespace {
-
-// Constants for inset and control points for tab shape.
-const CGFloat kInsetMultiplier = 2.0/3.0;
-const CGFloat kControlPoint1Multiplier = 1.0/3.0;
-const CGFloat kControlPoint2Multiplier = 3.0/8.0;
-
-// The amount of time in seconds during which each type of glow increases, holds
-// steady, and decreases, respectively.
-const NSTimeInterval kHoverShowDuration = 0.2;
-const NSTimeInterval kHoverHoldDuration = 0.02;
-const NSTimeInterval kHoverHideDuration = 0.4;
-const NSTimeInterval kAlertShowDuration = 0.4;
-const NSTimeInterval kAlertHoldDuration = 0.4;
-const NSTimeInterval kAlertHideDuration = 0.4;
-
-// The default time interval in seconds between glow updates (when
-// increasing/decreasing).
-const NSTimeInterval kGlowUpdateInterval = 0.025;
-
-const CGFloat kTearDistance = 36.0;
-const NSTimeInterval kTearDuration = 0.333;
-
-// This is used to judge whether the mouse has moved during rapid closure; if it
-// has moved less than the threshold, we want to close the tab.
-const CGFloat kRapidCloseDist = 2.5;
-
-} // namespace
-
-@interface TabView(Private)
-
-- (void)resetLastGlowUpdateTime;
-- (NSTimeInterval)timeElapsedSinceLastGlowUpdate;
-- (void)adjustGlowValue;
-// TODO(davidben): When we stop supporting 10.5, this can be removed.
-- (int)getWorkspaceID:(NSWindow*)window useCache:(BOOL)useCache;
-- (NSBezierPath*)bezierPathForRect:(NSRect)rect;
-
-@end // TabView(Private)
-
-@implementation TabView
-
-@synthesize state = state_;
-@synthesize hoverAlpha = hoverAlpha_;
-@synthesize alertAlpha = alertAlpha_;
-@synthesize closing = closing_;
-
-- (id)initWithFrame:(NSRect)frame {
- self = [super initWithFrame:frame];
- if (self) {
- [self setShowsDivider:NO];
- // TODO(alcor): register for theming
- }
- return self;
-}
-
-- (void)awakeFromNib {
- [self setShowsDivider:NO];
-
- // It is desirable for us to remove the close button from the cocoa hierarchy,
- // so that VoiceOver does not encounter it.
- // TODO(dtseng): crbug.com/59978.
- // Retain in case we remove it from its superview.
- closeButtonRetainer_.reset([closeButton_ retain]);
- if (BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser()) {
- // The superview gives up ownership of the closeButton here.
- [closeButton_ removeFromSuperview];
- }
-}
-
-- (void)dealloc {
- // Cancel any delayed requests that may still be pending (drags or hover).
- [NSObject cancelPreviousPerformRequestsWithTarget:self];
- [super dealloc];
-}
-
-// Called to obtain the context menu for when the user hits the right mouse
-// button (or control-clicks). (Note that -rightMouseDown: is *not* called for
-// control-click.)
-- (NSMenu*)menu {
- if ([self isClosing])
- return nil;
-
- // Sheets, being window-modal, should block contextual menus. For some reason
- // they do not. Disallow them ourselves.
- if ([[self window] attachedSheet])
- return nil;
-
- return [controller_ menu];
-}
-
-// Overridden so that mouse clicks come to this view (the parent of the
-// hierarchy) first. We want to handle clicks and drags in this class and
-// leave the background button for display purposes only.
-- (BOOL)acceptsFirstMouse:(NSEvent*)theEvent {
- return YES;
-}
-
-- (void)mouseEntered:(NSEvent*)theEvent {
- isMouseInside_ = YES;
- [self resetLastGlowUpdateTime];
- [self adjustGlowValue];
-}
-
-- (void)mouseMoved:(NSEvent*)theEvent {
- hoverPoint_ = [self convertPoint:[theEvent locationInWindow]
- fromView:nil];
- [self setNeedsDisplay:YES];
-}
-
-- (void)mouseExited:(NSEvent*)theEvent {
- isMouseInside_ = NO;
- hoverHoldEndTime_ =
- [NSDate timeIntervalSinceReferenceDate] + kHoverHoldDuration;
- [self resetLastGlowUpdateTime];
- [self adjustGlowValue];
-}
-
-- (void)setTrackingEnabled:(BOOL)enabled {
- [closeButton_ setTrackingEnabled:enabled];
-}
-
-// Determines which view a click in our frame actually hit. It's either this
-// view or our child close button.
-- (NSView*)hitTest:(NSPoint)aPoint {
- NSPoint viewPoint = [self convertPoint:aPoint fromView:[self superview]];
- NSRect frame = [self frame];
-
- // Reduce the width of the hit rect slightly to remove the overlap
- // between adjacent tabs. The drawing code in TabCell has the top
- // corners of the tab inset by height*2/3, so we inset by half of
- // that here. This doesn't completely eliminate the overlap, but it
- // works well enough.
- NSRect hitRect = NSInsetRect(frame, frame.size.height / 3.0f, 0);
- if (![closeButton_ isHidden])
- if (NSPointInRect(viewPoint, [closeButton_ frame])) return closeButton_;
- if (NSPointInRect(aPoint, hitRect)) return self;
- return nil;
-}
-
-// Returns |YES| if this tab can be torn away into a new window.
-- (BOOL)canBeDragged {
- if ([self isClosing])
- return NO;
- NSWindowController* controller = [sourceWindow_ windowController];
- if ([controller isKindOfClass:[TabWindowController class]]) {
- TabWindowController* realController =
- static_cast<TabWindowController*>(controller);
- return [realController isTabDraggable:self];
- }
- return YES;
-}
-
-// Returns an array of controllers that could be a drop target, ordered front to
-// back. It has to be of the appropriate class, and visible (obviously). Note
-// that the window cannot be a target for itself.
-- (NSArray*)dropTargetsForController:(TabWindowController*)dragController {
- NSMutableArray* targets = [NSMutableArray array];
- NSWindow* dragWindow = [dragController window];
- for (NSWindow* window in [NSApp orderedWindows]) {
- if (window == dragWindow) continue;
- if (![window isVisible]) continue;
- // Skip windows on the wrong space.
- if ([window respondsToSelector:@selector(isOnActiveSpace)]) {
- if (![window performSelector:@selector(isOnActiveSpace)])
- continue;
- } else {
- // TODO(davidben): When we stop supporting 10.5, this can be
- // removed.
- //
- // We don't cache the workspace of |dragWindow| because it may
- // move around spaces.
- if ([self getWorkspaceID:dragWindow useCache:NO] !=
- [self getWorkspaceID:window useCache:YES])
- continue;
- }
- NSWindowController* controller = [window windowController];
- if ([controller isKindOfClass:[TabWindowController class]]) {
- TabWindowController* realController =
- static_cast<TabWindowController*>(controller);
- if ([realController canReceiveFrom:dragController])
- [targets addObject:controller];
- }
- }
- return targets;
-}
-
-// Call to clear out transient weak references we hold during drags.
-- (void)resetDragControllers {
- draggedController_ = nil;
- dragWindow_ = nil;
- dragOverlay_ = nil;
- sourceController_ = nil;
- sourceWindow_ = nil;
- targetController_ = nil;
- workspaceIDCache_.clear();
-}
-
-// Sets whether the window background should be visible or invisible when
-// dragging a tab. The background should be invisible when the mouse is over a
-// potential drop target for the tab (the tab strip). It should be visible when
-// there's no drop target so the window looks more fully realized and ready to
-// become a stand-alone window.
-- (void)setWindowBackgroundVisibility:(BOOL)shouldBeVisible {
- if (chromeIsVisible_ == shouldBeVisible)
- return;
-
- // There appears to be a race-condition in CoreAnimation where if we use
- // animators to set the alpha values, we can't guarantee that we cancel them.
- // This has the side effect of sometimes leaving the dragged window
- // translucent or invisible. As a result, don't animate the alpha change.
- [[draggedController_ overlayWindow] setAlphaValue:1.0];
- if (targetController_) {
- [dragWindow_ setAlphaValue:0.0];
- [[draggedController_ overlayWindow] setHasShadow:YES];
- [[targetController_ window] makeMainWindow];
- } else {
- [dragWindow_ setAlphaValue:0.5];
- [[draggedController_ overlayWindow] setHasShadow:NO];
- [[draggedController_ window] makeMainWindow];
- }
- chromeIsVisible_ = shouldBeVisible;
-}
-
-// Handle clicks and drags in this button. We get here because we have
-// overridden acceptsFirstMouse: and the click is within our bounds.
-- (void)mouseDown:(NSEvent*)theEvent {
- if ([self isClosing])
- return;
-
- NSPoint downLocation = [theEvent locationInWindow];
-
- // Record the state of the close button here, because selecting the tab will
- // unhide it.
- BOOL closeButtonActive = [closeButton_ isHidden] ? NO : YES;
-
- // During the tab closure animation (in particular, during rapid tab closure),
- // we may get incorrectly hit with a mouse down. If it should have gone to the
- // close button, we send it there -- it should then track the mouse, so we
- // don't have to worry about mouse ups.
- if (closeButtonActive && [controller_ inRapidClosureMode]) {
- NSPoint hitLocation = [[self superview] convertPoint:downLocation
- fromView:nil];
- if ([self hitTest:hitLocation] == closeButton_) {
- [closeButton_ mouseDown:theEvent];
- return;
- }
- }
-
- // Fire the action to select the tab.
- if ([[controller_ target] respondsToSelector:[controller_ action]])
- [[controller_ target] performSelector:[controller_ action]
- withObject:self];
-
- [self resetDragControllers];
-
- // Resolve overlay back to original window.
- sourceWindow_ = [self window];
- if ([sourceWindow_ isKindOfClass:[NSPanel class]]) {
- sourceWindow_ = [sourceWindow_ parentWindow];
- }
-
- sourceWindowFrame_ = [sourceWindow_ frame];
- sourceTabFrame_ = [self frame];
- sourceController_ = [sourceWindow_ windowController];
- tabWasDragged_ = NO;
- tearTime_ = 0.0;
- draggingWithinTabStrip_ = YES;
- chromeIsVisible_ = NO;
-
- // If there's more than one potential window to be a drop target, we want to
- // treat a drag of a tab just like dragging around a tab that's 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.
- NSArray* targets = [self dropTargetsForController:sourceController_];
- moveWindowOnDrag_ =
- ([sourceController_ numberOfTabs] < 2 && ![targets count]) ||
- ![self canBeDragged] ||
- ![sourceController_ tabDraggingAllowed];
- // If we are dragging a tab, a window with a single tab should immediately
- // snap off and not drag within the tab strip.
- if (!moveWindowOnDrag_)
- draggingWithinTabStrip_ = [sourceController_ numberOfTabs] > 1;
-
- dragOrigin_ = [NSEvent mouseLocation];
-
- // If the tab gets torn off, the tab controller will be removed from the tab
- // strip and then deallocated. This will also result in *us* being
- // deallocated. Both these are bad, so we prevent this by retaining the
- // controller.
- scoped_nsobject<TabController> controller([controller_ retain]);
-
- // 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];
- NSEventType type = [theEvent type];
- if (type == NSLeftMouseDragged) {
- [self mouseDragged:theEvent];
- } else if (type == NSLeftMouseUp) {
- NSPoint upLocation = [theEvent locationInWindow];
- CGFloat dx = upLocation.x - downLocation.x;
- CGFloat dy = upLocation.y - downLocation.y;
-
- // During rapid tab closure (mashing tab close buttons), we may get hit
- // with a mouse down. As long as the mouse up is over the close button,
- // and the mouse hasn't moved too much, we close the tab.
- if (closeButtonActive &&
- (dx*dx + dy*dy) <= kRapidCloseDist*kRapidCloseDist &&
- [controller inRapidClosureMode]) {
- NSPoint hitLocation =
- [[self superview] convertPoint:[theEvent locationInWindow]
- fromView:nil];
- if ([self hitTest:hitLocation] == closeButton_) {
- [controller closeTab:self];
- break;
- }
- }
-
- [self mouseUp:theEvent];
- break;
- } else {
- // TODO(viettrungluu): [crbug.com/23830] We can receive right-mouse-ups
- // (and maybe even others?) for reasons I don't understand. So we
- // explicitly check for both events we're expecting, and log others. We
- // should figure out what's going on.
- LOG(WARNING) << "Spurious event received of type " << type << ".";
- }
- }
-}
-
-- (void)mouseDragged:(NSEvent*)theEvent {
- // Special-case this to keep the logic below simpler.
- if (moveWindowOnDrag_) {
- if ([sourceController_ windowMovementAllowed]) {
- NSPoint thisPoint = [NSEvent mouseLocation];
- NSPoint origin = sourceWindowFrame_.origin;
- origin.x += (thisPoint.x - dragOrigin_.x);
- origin.y += (thisPoint.y - dragOrigin_.y);
- [sourceWindow_ setFrameOrigin:NSMakePoint(origin.x, origin.y)];
- } // else do nothing.
- return;
- }
-
- // First, go through the magnetic drag cycle. We break out of this if
- // "stretchiness" ever exceeds a set amount.
- tabWasDragged_ = YES;
-
- if (draggingWithinTabStrip_) {
- 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];
- // Check that we haven't pulled the tab too far to start a drag. This
- // can include either pulling it too far down, or off the side of the tab
- // strip that would cause it to no longer be fully visible.
- BOOL stillVisible = [sourceController_ isTabFullyVisible:self];
- CGFloat tearForce = fabs(thisPoint.y - dragOrigin_.y);
- if ([sourceController_ tabTearingAllowed] &&
- (tearForce > kTearDistance || !stillVisible)) {
- draggingWithinTabStrip_ = NO;
- // When you finally leave the strip, we treat that as the origin.
- dragOrigin_.x = thisPoint.x;
- } else {
- // Still dragging within the tab strip, wait for the next drag event.
- return;
- }
- }
-
- // Do not start dragging until the user has "torn" the tab off by
- // moving more than 3 pixels.
- NSDate* targetDwellDate = nil; // The date this target was first chosen.
-
- NSPoint thisPoint = [NSEvent mouseLocation];
-
- // Iterate over possible targets checking for the one the mouse is in.
- // If the tab is just in the frame, bring the window forward to make it
- // easier to drop something there. If it's in the tab strip, set the new
- // target so that it pops into that window. We can't cache this because we
- // need the z-order to be correct.
- NSArray* targets = [self dropTargetsForController:draggedController_];
- TabWindowController* newTarget = nil;
- for (TabWindowController* target in targets) {
- NSRect windowFrame = [[target window] frame];
- if (NSPointInRect(thisPoint, windowFrame)) {
- [[target window] orderFront:self];
- NSRect tabStripFrame = [[target tabStripView] frame];
- 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;
- }
- }
-
- // Create or identify the dragged controller.
- if (!draggedController_) {
- // Get rid of any placeholder remaining in the original source window.
- [sourceController_ removePlaceholder];
-
- // Detach from the current window and put it in a new window. If there are
- // no more tabs remaining after detaching, the source window is about to
- // go away (it's been autoreleased) so we need to ensure we don't reference
- // it any more. In that case the new controller becomes our source
- // controller.
- draggedController_ = [sourceController_ detachTabToNewWindow:self];
- dragWindow_ = [draggedController_ window];
- [dragWindow_ setAlphaValue:0.0];
- if (![sourceController_ hasLiveTabs]) {
- sourceController_ = draggedController_;
- sourceWindow_ = dragWindow_;
- }
-
- // If dragging the tab only moves the current window, do not show overlay
- // so that sheets stay on top of the window.
- // Bring the target window to the front and make sure it has a border.
- [dragWindow_ setLevel:NSFloatingWindowLevel];
- [dragWindow_ setHasShadow:YES];
- [dragWindow_ orderFront:nil];
- [dragWindow_ makeMainWindow];
- [draggedController_ showOverlay];
- dragOverlay_ = [draggedController_ overlayWindow];
- // Force the new tab button to be hidden. We'll reset it on mouse up.
- [draggedController_ showNewTabButton:NO];
- tearTime_ = [NSDate timeIntervalSinceReferenceDate];
- tearOrigin_ = sourceWindowFrame_.origin;
- }
-
- // TODO(pinkerton): http://crbug.com/25682 demonstrates a way to get here by
- // some weird circumstance that doesn't first go through mouseDown:. We
- // really shouldn't go any farther.
- if (!draggedController_ || !sourceController_)
- return;
-
- // When the user first tears off the window, we want slide the window to
- // the current mouse location (to reduce the jarring appearance). We do this
- // by calling ourselves back with additional mouseDragged calls (not actual
- // events). |tearProgress| is a normalized measure of how far through this
- // tear "animation" (of length kTearDuration) we are and has values [0..1].
- // We use sqrt() so the animation is non-linear (slow down near the end
- // point).
- NSTimeInterval tearProgress =
- [NSDate timeIntervalSinceReferenceDate] - tearTime_;
- tearProgress /= kTearDuration; // Normalize.
- 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. We need to make
- // sure these get cancelled in mouseUp:.
- [NSObject cancelPreviousPerformRequestsWithTarget:self];
- [self performSelector:@selector(mouseDragged:)
- withObject:theEvent
- afterDelay:1.0f/30.0f];
-
- // Set the current window origin based on how far we've progressed through
- // the tear animation.
- origin.x = (1 - tearProgress) * tearOrigin_.x + tearProgress * origin.x;
- origin.y = (1 - tearProgress) * tearOrigin_.y + tearProgress * origin.y;
- }
-
- if (targetController_) {
- // In order to "snap" two windows of different sizes together at their
- // toolbar, we can't just use the origin of the target frame. We also have
- // to take into consideration the difference in height.
- NSRect targetFrame = [[targetController_ window] frame];
- NSRect sourceFrame = [dragWindow_ frame];
- origin.y = NSMinY(targetFrame) +
- (NSHeight(targetFrame) - NSHeight(sourceFrame));
- }
- [dragWindow_ setFrameOrigin:NSMakePoint(origin.x, origin.y)];
-
- // If we're not hovering over any window, make the window 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];
- targetDwellDate = nil;
- }
-
- // Compute where placeholder should go and insert it into the
- // destination tab strip.
- 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]
- convertRect:tabFrame fromView:nil];
- [targetController_ insertPlaceholderForTab:self
- frame:tabFrame
- yStretchiness:0];
- [targetController_ layoutTabs];
- } else {
- [dragWindow_ makeKeyAndOrderFront:nil];
- }
-
- // Adjust the visibility of the window background. If there is a drop target,
- // we want to hide the window background so the tab stands out for
- // positioning. If not, we want to show it so it looks like a new window will
- // be realized.
- BOOL chromeShouldBeVisible = targetController_ == nil;
- [self setWindowBackgroundVisibility:chromeShouldBeVisible];
-}
-
-- (void)mouseUp:(NSEvent*)theEvent {
- // The drag/click is done. If the user dragged the mouse, finalize the drag
- // and clean up.
-
- // Special-case this to keep the logic below simpler.
- if (moveWindowOnDrag_)
- return;
-
- // Cancel any delayed -mouseDragged: requests that may still be pending.
- [NSObject cancelPreviousPerformRequestsWithTarget:self];
-
- // TODO(pinkerton): http://crbug.com/25682 demonstrates a way to get here by
- // some weird circumstance that doesn't first go through mouseDown:. We
- // really shouldn't go any farther.
- if (!sourceController_)
- return;
-
- // We are now free to re-display the new tab button in the window we're
- // dragging. It will show when the next call to -layoutTabs (which happens
- // indrectly by several of the calls below, such as removing the placeholder).
- [draggedController_ showNewTabButton:YES];
-
- if (draggingWithinTabStrip_) {
- if (tabWasDragged_) {
- // Move tab to new location.
- DCHECK([sourceController_ numberOfTabs]);
- 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];
- [targetController_ moveTabView:draggedTabView
- fromController:draggedController_];
- // Force redraw to avoid flashes of old content before returning to event
- // loop.
- [[targetController_ window] display];
- [targetController_ showWindow:nil];
- [draggedController_ removeOverlay];
- } else {
- // Only move the window around on screen. Make sure it's set back to
- // normal state (fully opaque, has shadow, has key, etc).
- [draggedController_ removeOverlay];
- // Don't want to re-show the window if it was closed during the drag.
- if ([dragWindow_ isVisible]) {
- [dragWindow_ setAlphaValue:1.0];
- [dragOverlay_ setHasShadow:NO];
- [dragWindow_ setHasShadow:YES];
- [dragWindow_ makeKeyAndOrderFront:nil];
- }
- [[draggedController_ window] setLevel:NSNormalWindowLevel];
- [draggedController_ removePlaceholder];
- }
- [sourceController_ removePlaceholder];
- chromeIsVisible_ = YES;
-
- [self resetDragControllers];
-}
-
-- (void)otherMouseUp:(NSEvent*)theEvent {
- if ([self isClosing])
- return;
-
- // Support middle-click-to-close.
- if ([theEvent buttonNumber] == 2) {
- // |-hitTest:| takes a location in the superview's coordinates.
- NSPoint upLocation =
- [[self superview] convertPoint:[theEvent locationInWindow]
- fromView:nil];
- // If the mouse up occurred in our view or over the close button, then
- // close.
- if ([self hitTest:upLocation])
- [controller_ closeTab:self];
- }
-}
-
-- (void)drawRect:(NSRect)dirtyRect {
- NSGraphicsContext* context = [NSGraphicsContext currentContext];
- [context saveGraphicsState];
-
- BrowserThemeProvider* themeProvider =
- static_cast<BrowserThemeProvider*>([[self window] themeProvider]);
- [context setPatternPhase:[[self window] themePatternPhase]];
-
- NSRect rect = [self bounds];
- NSBezierPath* path = [self bezierPathForRect:rect];
-
- BOOL selected = [self state];
- // Don't draw the window/tab bar background when selected, since the tab
- // background overlay drawn over it (see below) will be fully opaque.
- BOOL hasBackgroundImage = NO;
- if (!selected) {
- // ThemeProvider::HasCustomImage is true only if the theme provides the
- // image. However, even if the theme doesn't provide a tab background, the
- // theme machinery will make one if given a frame image. See
- // BrowserThemePack::GenerateTabBackgroundImages for details.
- hasBackgroundImage = themeProvider &&
- (themeProvider->HasCustomImage(IDR_THEME_TAB_BACKGROUND) ||
- themeProvider->HasCustomImage(IDR_THEME_FRAME));
-
- NSColor* backgroundImageColor = hasBackgroundImage ?
- themeProvider->GetNSImageColorNamed(IDR_THEME_TAB_BACKGROUND, true) :
- nil;
-
- if (backgroundImageColor) {
- [backgroundImageColor set];
- [path fill];
- } else {
- // Use the window's background color rather than |[NSColor
- // windowBackgroundColor]|, which gets confused by the fullscreen window.
- // (The result is the same for normal, non-fullscreen windows.)
- [[[self window] backgroundColor] set];
- [path fill];
- [[NSColor colorWithCalibratedWhite:1.0 alpha:0.3] set];
- [path fill];
- }
- }
-
- [context saveGraphicsState];
- [path addClip];
-
- // Use the same overlay for the selected state and for hover and alert glows;
- // for the selected state, it's fully opaque.
- CGFloat hoverAlpha = [self hoverAlpha];
- CGFloat alertAlpha = [self alertAlpha];
- if (selected || hoverAlpha > 0 || alertAlpha > 0) {
- // Draw the selected background / glow overlay.
- [context saveGraphicsState];
- CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]);
- CGContextBeginTransparencyLayer(cgContext, 0);
- if (!selected) {
- // The alert glow overlay is like the selected state but at most at most
- // 80% opaque. The hover glow brings up the overlay's opacity at most 50%.
- CGFloat backgroundAlpha = 0.8 * alertAlpha;
- backgroundAlpha += (1 - backgroundAlpha) * 0.5 * hoverAlpha;
- CGContextSetAlpha(cgContext, backgroundAlpha);
- }
- [path addClip];
- [context saveGraphicsState];
- [super drawBackground];
- [context restoreGraphicsState];
-
- // Draw a mouse hover gradient for the default themes.
- if (!selected && hoverAlpha > 0) {
- if (themeProvider && !hasBackgroundImage) {
- scoped_nsobject<NSGradient> glow([NSGradient alloc]);
- [glow initWithStartingColor:[NSColor colorWithCalibratedWhite:1.0
- alpha:1.0 * hoverAlpha]
- endingColor:[NSColor colorWithCalibratedWhite:1.0
- alpha:0.0]];
-
- NSPoint point = hoverPoint_;
- point.y = NSHeight(rect);
- [glow drawFromCenter:point
- radius:0.0
- toCenter:point
- radius:NSWidth(rect) / 3.0
- options:NSGradientDrawsBeforeStartingLocation];
-
- [glow drawInBezierPath:path relativeCenterPosition:hoverPoint_];
- }
- }
-
- CGContextEndTransparencyLayer(cgContext);
- [context restoreGraphicsState];
- }
-
- BOOL active = [[self window] isKeyWindow] || [[self window] isMainWindow];
- CGFloat borderAlpha = selected ? (active ? 0.3 : 0.2) : 0.2;
- NSColor* borderColor = [NSColor colorWithDeviceWhite:0.0 alpha:borderAlpha];
- NSColor* highlightColor = themeProvider ? themeProvider->GetNSColor(
- themeProvider->UsingDefaultTheme() ?
- BrowserThemeProvider::COLOR_TOOLBAR_BEZEL :
- BrowserThemeProvider::COLOR_TOOLBAR, true) : nil;
-
- // Draw the top inner highlight within the currently selected tab if using
- // the default theme.
- if (selected && themeProvider && themeProvider->UsingDefaultTheme()) {
- NSAffineTransform* highlightTransform = [NSAffineTransform transform];
- [highlightTransform translateXBy:1.0 yBy:-1.0];
- scoped_nsobject<NSBezierPath> highlightPath([path copy]);
- [highlightPath transformUsingAffineTransform:highlightTransform];
- [highlightColor setStroke];
- [highlightPath setLineWidth:1.0];
- [highlightPath stroke];
- highlightTransform = [NSAffineTransform transform];
- [highlightTransform translateXBy:-2.0 yBy:0.0];
- [highlightPath transformUsingAffineTransform:highlightTransform];
- [highlightPath stroke];
- }
-
- [context restoreGraphicsState];
-
- // Draw the top stroke.
- [context saveGraphicsState];
- [borderColor set];
- [path setLineWidth:1.0];
- [path stroke];
- [context restoreGraphicsState];
-
- // Mimic the tab strip's bottom border, which consists of a dark border
- // and light highlight.
- if (!selected) {
- [path addClip];
- NSRect borderRect = rect;
- borderRect.origin.y = 1;
- borderRect.size.height = 1;
- [borderColor set];
- NSRectFillUsingOperation(borderRect, NSCompositeSourceOver);
-
- borderRect.origin.y = 0;
- [highlightColor set];
- NSRectFillUsingOperation(borderRect, NSCompositeSourceOver);
- }
-
- [context restoreGraphicsState];
-}
-
-- (void)viewDidMoveToWindow {
- [super viewDidMoveToWindow];
- if ([self window]) {
- [controller_ updateTitleColor];
- }
-}
-
-- (void)setClosing:(BOOL)closing {
- closing_ = closing; // Safe because the property is nonatomic.
- // When closing, ensure clicks to the close button go nowhere.
- if (closing) {
- [closeButton_ setTarget:nil];
- [closeButton_ setAction:nil];
- }
-}
-
-- (void)startAlert {
- // Do not start a new alert while already alerting or while in a decay cycle.
- if (alertState_ == tabs::kAlertNone) {
- alertState_ = tabs::kAlertRising;
- [self resetLastGlowUpdateTime];
- [self adjustGlowValue];
- }
-}
-
-- (void)cancelAlert {
- if (alertState_ != tabs::kAlertNone) {
- alertState_ = tabs::kAlertFalling;
- alertHoldEndTime_ =
- [NSDate timeIntervalSinceReferenceDate] + kGlowUpdateInterval;
- [self resetLastGlowUpdateTime];
- [self adjustGlowValue];
- }
-}
-
-- (BOOL)accessibilityIsIgnored {
- return NO;
-}
-
-- (NSArray*)accessibilityActionNames {
- NSArray* parentActions = [super accessibilityActionNames];
-
- return [parentActions arrayByAddingObject:NSAccessibilityPressAction];
-}
-
-- (NSArray*)accessibilityAttributeNames {
- NSMutableArray* attributes =
- [[super accessibilityAttributeNames] mutableCopy];
- [attributes addObject:NSAccessibilityTitleAttribute];
- [attributes addObject:NSAccessibilityEnabledAttribute];
-
- return attributes;
-}
-
-- (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
- if ([attribute isEqual:NSAccessibilityTitleAttribute])
- return NO;
-
- if ([attribute isEqual:NSAccessibilityEnabledAttribute])
- return NO;
-
- return [super accessibilityIsAttributeSettable:attribute];
-}
-
-- (id)accessibilityAttributeValue:(NSString*)attribute {
- if ([attribute isEqual:NSAccessibilityRoleAttribute])
- return NSAccessibilityButtonRole;
-
- if ([attribute isEqual:NSAccessibilityTitleAttribute])
- return [controller_ title];
-
- if ([attribute isEqual:NSAccessibilityEnabledAttribute])
- return [NSNumber numberWithBool:YES];
-
- if ([attribute isEqual:NSAccessibilityChildrenAttribute]) {
- // The subviews (icon and text) are clutter; filter out everything but
- // useful controls.
- NSArray* children = [super accessibilityAttributeValue:attribute];
- NSMutableArray* okChildren = [NSMutableArray array];
- for (id child in children) {
- if ([child isKindOfClass:[NSButtonCell class]])
- [okChildren addObject:child];
- }
-
- return okChildren;
- }
-
- return [super accessibilityAttributeValue:attribute];
-}
-
-- (ViewID)viewID {
- return VIEW_ID_TAB;
-}
-
-@end // @implementation TabView
-
-@implementation TabView (TabControllerInterface)
-
-- (void)setController:(TabController*)controller {
- controller_ = controller;
-}
-
-@end // @implementation TabView (TabControllerInterface)
-
-@implementation TabView(Private)
-
-- (void)resetLastGlowUpdateTime {
- lastGlowUpdate_ = [NSDate timeIntervalSinceReferenceDate];
-}
-
-- (NSTimeInterval)timeElapsedSinceLastGlowUpdate {
- return [NSDate timeIntervalSinceReferenceDate] - lastGlowUpdate_;
-}
-
-- (void)adjustGlowValue {
- // A time interval long enough to represent no update.
- const NSTimeInterval kNoUpdate = 1000000;
-
- // Time until next update for either glow.
- NSTimeInterval nextUpdate = kNoUpdate;
-
- NSTimeInterval elapsed = [self timeElapsedSinceLastGlowUpdate];
- NSTimeInterval currentTime = [NSDate timeIntervalSinceReferenceDate];
-
- // TODO(viettrungluu): <http://crbug.com/30617> -- split off the stuff below
- // into a pure function and add a unit test.
-
- CGFloat hoverAlpha = [self hoverAlpha];
- if (isMouseInside_) {
- // Increase hover glow until it's 1.
- if (hoverAlpha < 1) {
- hoverAlpha = MIN(hoverAlpha + elapsed / kHoverShowDuration, 1);
- [self setHoverAlpha:hoverAlpha];
- nextUpdate = MIN(kGlowUpdateInterval, nextUpdate);
- } // Else already 1 (no update needed).
- } else {
- if (currentTime >= hoverHoldEndTime_) {
- // No longer holding, so decrease hover glow until it's 0.
- if (hoverAlpha > 0) {
- hoverAlpha = MAX(hoverAlpha - elapsed / kHoverHideDuration, 0);
- [self setHoverAlpha:hoverAlpha];
- nextUpdate = MIN(kGlowUpdateInterval, nextUpdate);
- } // Else already 0 (no update needed).
- } else {
- // Schedule update for end of hold time.
- nextUpdate = MIN(hoverHoldEndTime_ - currentTime, nextUpdate);
- }
- }
-
- CGFloat alertAlpha = [self alertAlpha];
- if (alertState_ == tabs::kAlertRising) {
- // Increase alert glow until it's 1 ...
- alertAlpha = MIN(alertAlpha + elapsed / kAlertShowDuration, 1);
- [self setAlertAlpha:alertAlpha];
-
- // ... and having reached 1, switch to holding.
- if (alertAlpha >= 1) {
- alertState_ = tabs::kAlertHolding;
- alertHoldEndTime_ = currentTime + kAlertHoldDuration;
- nextUpdate = MIN(kAlertHoldDuration, nextUpdate);
- } else {
- nextUpdate = MIN(kGlowUpdateInterval, nextUpdate);
- }
- } else if (alertState_ != tabs::kAlertNone) {
- if (alertAlpha > 0) {
- if (currentTime >= alertHoldEndTime_) {
- // Stop holding, then decrease alert glow (until it's 0).
- if (alertState_ == tabs::kAlertHolding) {
- alertState_ = tabs::kAlertFalling;
- nextUpdate = MIN(kGlowUpdateInterval, nextUpdate);
- } else {
- DCHECK_EQ(tabs::kAlertFalling, alertState_);
- alertAlpha = MAX(alertAlpha - elapsed / kAlertHideDuration, 0);
- [self setAlertAlpha:alertAlpha];
- nextUpdate = MIN(kGlowUpdateInterval, nextUpdate);
- }
- } else {
- // Schedule update for end of hold time.
- nextUpdate = MIN(alertHoldEndTime_ - currentTime, nextUpdate);
- }
- } else {
- // Done the alert decay cycle.
- alertState_ = tabs::kAlertNone;
- }
- }
-
- if (nextUpdate < kNoUpdate)
- [self performSelector:_cmd withObject:nil afterDelay:nextUpdate];
-
- [self resetLastGlowUpdateTime];
- [self setNeedsDisplay:YES];
-}
-
-// Returns the workspace id of |window|. If |useCache|, then lookup
-// and remember the value in |workspaceIDCache_| until the end of the
-// current drag.
-- (int)getWorkspaceID:(NSWindow*)window useCache:(BOOL)useCache {
- CGWindowID windowID = [window windowNumber];
- if (useCache) {
- std::map<CGWindowID, int>::iterator iter =
- workspaceIDCache_.find(windowID);
- if (iter != workspaceIDCache_.end())
- return iter->second;
- }
-
- int workspace = -1;
- // It's possible to query in bulk, but probably not necessary.
- base::mac::ScopedCFTypeRef<CFArrayRef> windowIDs(CFArrayCreate(
- NULL, reinterpret_cast<const void **>(&windowID), 1, NULL));
- base::mac::ScopedCFTypeRef<CFArrayRef> descriptions(
- CGWindowListCreateDescriptionFromArray(windowIDs));
- DCHECK(CFArrayGetCount(descriptions.get()) <= 1);
- if (CFArrayGetCount(descriptions.get()) > 0) {
- CFDictionaryRef dict = static_cast<CFDictionaryRef>(
- CFArrayGetValueAtIndex(descriptions.get(), 0));
- DCHECK(CFGetTypeID(dict) == CFDictionaryGetTypeID());
-
- // Sanity check the ID.
- CFNumberRef otherIDRef = (CFNumberRef)base::mac::GetValueFromDictionary(
- dict, kCGWindowNumber, CFNumberGetTypeID());
- CGWindowID otherID;
- if (otherIDRef &&
- CFNumberGetValue(otherIDRef, kCGWindowIDCFNumberType, &otherID) &&
- otherID == windowID) {
- // And then get the workspace.
- CFNumberRef workspaceRef = (CFNumberRef)base::mac::GetValueFromDictionary(
- dict, kCGWindowWorkspace, CFNumberGetTypeID());
- if (!workspaceRef ||
- !CFNumberGetValue(workspaceRef, kCFNumberIntType, &workspace)) {
- workspace = -1;
- }
- } else {
- NOTREACHED();
- }
- }
- if (useCache) {
- workspaceIDCache_[windowID] = workspace;
- }
- return workspace;
-}
-
-// Returns the bezier path used to draw the tab given the bounds to draw it in.
-- (NSBezierPath*)bezierPathForRect:(NSRect)rect {
- // Outset by 0.5 in order to draw on pixels rather than on borders (which
- // would cause blurry pixels). Subtract 1px of height to compensate, otherwise
- // clipping will occur.
- rect = NSInsetRect(rect, -0.5, -0.5);
- rect.size.height -= 1.0;
-
- NSPoint bottomLeft = NSMakePoint(NSMinX(rect), NSMinY(rect) + 2);
- NSPoint bottomRight = NSMakePoint(NSMaxX(rect), NSMinY(rect) + 2);
- NSPoint topRight =
- NSMakePoint(NSMaxX(rect) - kInsetMultiplier * NSHeight(rect),
- NSMaxY(rect));
- NSPoint topLeft =
- NSMakePoint(NSMinX(rect) + kInsetMultiplier * NSHeight(rect),
- NSMaxY(rect));
-
- CGFloat baseControlPointOutset = NSHeight(rect) * kControlPoint1Multiplier;
- CGFloat bottomControlPointInset = NSHeight(rect) * kControlPoint2Multiplier;
-
- // Outset many of these values by 1 to cause the fill to bleed outside the
- // clip area.
- NSBezierPath* path = [NSBezierPath bezierPath];
- [path moveToPoint:NSMakePoint(bottomLeft.x - 1, bottomLeft.y - 2)];
- [path lineToPoint:NSMakePoint(bottomLeft.x - 1, bottomLeft.y)];
- [path lineToPoint:bottomLeft];
- [path curveToPoint:topLeft
- controlPoint1:NSMakePoint(bottomLeft.x + baseControlPointOutset,
- bottomLeft.y)
- controlPoint2:NSMakePoint(topLeft.x - bottomControlPointInset,
- topLeft.y)];
- [path lineToPoint:topRight];
- [path curveToPoint:bottomRight
- controlPoint1:NSMakePoint(topRight.x + bottomControlPointInset,
- topRight.y)
- controlPoint2:NSMakePoint(bottomRight.x - baseControlPointOutset,
- bottomRight.y)];
- [path lineToPoint:NSMakePoint(bottomRight.x + 1, bottomRight.y)];
- [path lineToPoint:NSMakePoint(bottomRight.x + 1, bottomRight.y - 2)];
- return path;
-}
-
-@end // @implementation TabView(Private)
« no previous file with comments | « chrome/browser/ui/cocoa/tab_view.h ('k') | chrome/browser/ui/cocoa/tab_view_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698