Home | History | Annotate | Download | only in tabs
      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