• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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