1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h" 6 7 #include "base/logging.h" 8 #include "base/mac/mac_util.h" 9 #include "chrome/browser/themes/theme_service.h" 10 #import "chrome/browser/ui/cocoa/nsview_additions.h" 11 #import "chrome/browser/ui/cocoa/browser_window_controller.h" 12 #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h" 13 #import "chrome/browser/ui/cocoa/view_id_util.h" 14 15 @implementation TabStripView 16 17 @synthesize newTabButton = newTabButton_; 18 @synthesize profileMenuButton = profileMenuButton_; 19 @synthesize dropArrowShown = dropArrowShown_; 20 @synthesize dropArrowPosition = dropArrowPosition_; 21 22 - (id)initWithFrame:(NSRect)frame { 23 self = [super initWithFrame:frame]; 24 if (self) { 25 // Set lastMouseUp_ = -1000.0 so that timestamp-lastMouseUp_ is big unless 26 // lastMouseUp_ has been reset. 27 lastMouseUp_ = -1000.0; 28 29 // Register to be an URL drop target. 30 dropHandler_.reset([[URLDropTargetHandler alloc] initWithView:self]); 31 } 32 return self; 33 } 34 35 // Draw bottom border (a dark border and light highlight). Each tab is 36 // responsible for mimicking this bottom border, unless it's the selected 37 // tab. 38 - (void)drawBorder:(NSRect)bounds { 39 const CGFloat lineWidth = [self cr_lineWidth]; 40 NSRect borderRect, contentRect; 41 42 borderRect = bounds; 43 borderRect.origin.y = lineWidth; 44 borderRect.size.height = lineWidth; 45 [[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set]; 46 NSRectFillUsingOperation(borderRect, NSCompositeSourceOver); 47 NSDivideRect(bounds, &borderRect, &contentRect, lineWidth, NSMinYEdge); 48 49 ThemeService* themeProvider = 50 static_cast<ThemeService*>([[self window] themeProvider]); 51 if (!themeProvider) 52 return; 53 54 NSColor* bezelColor = themeProvider->GetNSColor( 55 themeProvider->UsingDefaultTheme() ? 56 ThemeService::COLOR_TOOLBAR_BEZEL : 57 ThemeService::COLOR_TOOLBAR, true); 58 [bezelColor set]; 59 NSRectFill(borderRect); 60 NSRectFillUsingOperation(borderRect, NSCompositeSourceOver); 61 } 62 63 - (void)drawRect:(NSRect)rect { 64 NSRect boundsRect = [self bounds]; 65 66 [self drawBorder:boundsRect]; 67 68 // Draw drop-indicator arrow (if appropriate). 69 // TODO(viettrungluu): this is all a stop-gap measure. 70 if ([self dropArrowShown]) { 71 // Programmer art: an arrow parametrized by many knobs. Note that the arrow 72 // points downwards (so understand "width" and "height" accordingly). 73 74 // How many (pixels) to inset on the top/bottom. 75 const CGFloat kArrowTopInset = 1.5; 76 const CGFloat kArrowBottomInset = 1; 77 78 // What proportion of the vertical space is dedicated to the arrow tip, 79 // i.e., (arrow tip height)/(amount of vertical space). 80 const CGFloat kArrowTipProportion = 0.5; 81 82 // This is a slope, i.e., (arrow tip height)/(0.5 * arrow tip width). 83 const CGFloat kArrowTipSlope = 1.2; 84 85 // What proportion of the arrow tip width is the stem, i.e., (stem 86 // width)/(arrow tip width). 87 const CGFloat kArrowStemProportion = 0.33; 88 89 NSPoint arrowTipPos = [self dropArrowPosition]; 90 arrowTipPos.y += kArrowBottomInset; // Inset on the bottom. 91 92 // Height we have to work with (insetting on the top). 93 CGFloat availableHeight = 94 NSMaxY(boundsRect) - arrowTipPos.y - kArrowTopInset; 95 DCHECK(availableHeight >= 5); 96 97 // Based on the knobs above, calculate actual dimensions which we'll need 98 // for drawing. 99 CGFloat arrowTipHeight = kArrowTipProportion * availableHeight; 100 CGFloat arrowTipWidth = 2 * arrowTipHeight / kArrowTipSlope; 101 CGFloat arrowStemHeight = availableHeight - arrowTipHeight; 102 CGFloat arrowStemWidth = kArrowStemProportion * arrowTipWidth; 103 CGFloat arrowStemInset = (arrowTipWidth - arrowStemWidth) / 2; 104 105 // The line width is arbitrary, but our path really should be mitered. 106 NSBezierPath* arrow = [NSBezierPath bezierPath]; 107 [arrow setLineJoinStyle:NSMiterLineJoinStyle]; 108 [arrow setLineWidth:1]; 109 110 // Define the arrow's shape! We start from the tip and go clockwise. 111 [arrow moveToPoint:arrowTipPos]; 112 [arrow relativeLineToPoint:NSMakePoint(-arrowTipWidth / 2, arrowTipHeight)]; 113 [arrow relativeLineToPoint:NSMakePoint(arrowStemInset, 0)]; 114 [arrow relativeLineToPoint:NSMakePoint(0, arrowStemHeight)]; 115 [arrow relativeLineToPoint:NSMakePoint(arrowStemWidth, 0)]; 116 [arrow relativeLineToPoint:NSMakePoint(0, -arrowStemHeight)]; 117 [arrow relativeLineToPoint:NSMakePoint(arrowStemInset, 0)]; 118 [arrow closePath]; 119 120 // Draw and fill the arrow. 121 [[NSColor colorWithCalibratedWhite:0 alpha:0.67] set]; 122 [arrow stroke]; 123 [[NSColor colorWithCalibratedWhite:1 alpha:0.67] setFill]; 124 [arrow fill]; 125 } 126 } 127 128 // YES if a double-click in the background of the tab strip minimizes the 129 // window. 130 - (BOOL)doubleClickMinimizesWindow { 131 return YES; 132 } 133 134 // We accept first mouse so clicks onto close/zoom/miniaturize buttons and 135 // title bar double-clicks are properly detected even when the window is in the 136 // background. 137 - (BOOL)acceptsFirstMouse:(NSEvent*)event { 138 return YES; 139 } 140 141 // Trap double-clicks and make them miniaturize the browser window. 142 - (void)mouseUp:(NSEvent*)event { 143 // Bail early if double-clicks are disabled. 144 if (![self doubleClickMinimizesWindow]) { 145 [super mouseUp:event]; 146 return; 147 } 148 149 NSInteger clickCount = [event clickCount]; 150 NSTimeInterval timestamp = [event timestamp]; 151 152 // Double-clicks on Zoom/Close/Mininiaturize buttons shouldn't cause 153 // miniaturization. For those, we miss the first click but get the second 154 // (with clickCount == 2!). We thus check that we got a first click shortly 155 // before (measured up-to-up) a double-click. Cocoa doesn't have a documented 156 // way of getting the proper interval (= (double-click-threshold) + 157 // (drag-threshold); the former is Carbon GetDblTime()/60.0 or 158 // com.apple.mouse.doubleClickThreshold [undocumented]). So we hard-code 159 // "short" as 0.8 seconds. (Measuring up-to-up isn't enough to properly 160 // detect double-clicks, but we're actually using Cocoa for that.) 161 if (clickCount == 2 && (timestamp - lastMouseUp_) < 0.8) { 162 if (base::mac::ShouldWindowsMiniaturizeOnDoubleClick()) 163 [[self window] performMiniaturize:self]; 164 } else { 165 [super mouseUp:event]; 166 } 167 168 // If clickCount is 0, the drag threshold was passed. 169 lastMouseUp_ = (clickCount == 1) ? timestamp : -1000.0; 170 } 171 172 // (URLDropTarget protocol) 173 - (id<URLDropTargetController>)urlDropController { 174 BrowserWindowController* windowController = [[self window] windowController]; 175 DCHECK([windowController isKindOfClass:[BrowserWindowController class]]); 176 return [windowController tabStripController]; 177 } 178 179 // (URLDropTarget protocol) 180 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender { 181 return [dropHandler_ draggingEntered:sender]; 182 } 183 184 // (URLDropTarget protocol) 185 - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender { 186 return [dropHandler_ draggingUpdated:sender]; 187 } 188 189 // (URLDropTarget protocol) 190 - (void)draggingExited:(id<NSDraggingInfo>)sender { 191 return [dropHandler_ draggingExited:sender]; 192 } 193 194 // (URLDropTarget protocol) 195 - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender { 196 return [dropHandler_ performDragOperation:sender]; 197 } 198 199 - (BOOL)accessibilityIsIgnored { 200 return NO; 201 } 202 203 - (id)accessibilityAttributeValue:(NSString*)attribute { 204 if ([attribute isEqual:NSAccessibilityRoleAttribute]) 205 return NSAccessibilityGroupRole; 206 207 return [super accessibilityAttributeValue:attribute]; 208 } 209 210 - (ViewID)viewID { 211 return VIEW_ID_TAB_STRIP; 212 } 213 214 @end 215