Home | History | Annotate | Download | only in extensions
      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/extensions/browser_actions_container_view.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/basictypes.h"
     10 #import "base/memory/scoped_nsobject.h"
     11 #import "chrome/browser/ui/cocoa/view_id_util.h"
     12 
     13 NSString* const kBrowserActionGrippyDragStartedNotification =
     14     @"BrowserActionGrippyDragStartedNotification";
     15 NSString* const kBrowserActionGrippyDraggingNotification =
     16     @"BrowserActionGrippyDraggingNotification";
     17 NSString* const kBrowserActionGrippyDragFinishedNotification =
     18     @"BrowserActionGrippyDragFinishedNotification";
     19 
     20 namespace {
     21 const CGFloat kAnimationDuration = 0.2;
     22 const CGFloat kGrippyWidth = 4.0;
     23 const CGFloat kMinimumContainerWidth = 10.0;
     24 }  // namespace
     25 
     26 @interface BrowserActionsContainerView(Private)
     27 // Returns the cursor that should be shown when hovering over the grippy based
     28 // on |canDragLeft_| and |canDragRight_|.
     29 - (NSCursor*)appropriateCursorForGrippy;
     30 @end
     31 
     32 @implementation BrowserActionsContainerView
     33 
     34 @synthesize animationEndFrame = animationEndFrame_;
     35 @synthesize canDragLeft = canDragLeft_;
     36 @synthesize canDragRight = canDragRight_;
     37 @synthesize grippyPinned = grippyPinned_;
     38 @synthesize maxWidth = maxWidth_;
     39 @synthesize userIsResizing = userIsResizing_;
     40 
     41 #pragma mark -
     42 #pragma mark Overridden Class Functions
     43 
     44 - (id)initWithFrame:(NSRect)frameRect {
     45   if ((self = [super initWithFrame:frameRect])) {
     46     grippyRect_ = NSMakeRect(0.0, 0.0, kGrippyWidth, NSHeight([self bounds]));
     47     canDragLeft_ = YES;
     48     canDragRight_ = YES;
     49     resizable_ = YES;
     50     [self setHidden:YES];
     51   }
     52   return self;
     53 }
     54 
     55 - (void)setResizable:(BOOL)resizable {
     56   if (resizable == resizable_)
     57     return;
     58   resizable_ = resizable;
     59   [self setNeedsDisplay:YES];
     60 }
     61 
     62 - (BOOL)isResizable {
     63   return resizable_;
     64 }
     65 
     66 - (void)resetCursorRects {
     67   [self discardCursorRects];
     68   [self addCursorRect:grippyRect_ cursor:[self appropriateCursorForGrippy]];
     69 }
     70 
     71 - (BOOL)acceptsFirstResponder {
     72   return YES;
     73 }
     74 
     75 - (void)mouseDown:(NSEvent*)theEvent {
     76   initialDragPoint_ = [self convertPoint:[theEvent locationInWindow]
     77                                 fromView:nil];
     78   if (!resizable_ ||
     79       !NSMouseInRect(initialDragPoint_, grippyRect_, [self isFlipped]))
     80     return;
     81 
     82   lastXPos_ = [self frame].origin.x;
     83   userIsResizing_ = YES;
     84 
     85   [[self appropriateCursorForGrippy] push];
     86   // Disable cursor rects so that the Omnibox and other UI elements don't push
     87   // cursors while the user is dragging. The cursor should be grippy until
     88   // the |-mouseUp:| message is received.
     89   [[self window] disableCursorRects];
     90 
     91   [[NSNotificationCenter defaultCenter]
     92       postNotificationName:kBrowserActionGrippyDragStartedNotification
     93                     object:self];
     94 }
     95 
     96 - (void)mouseUp:(NSEvent*)theEvent {
     97   if (!userIsResizing_)
     98     return;
     99 
    100   [NSCursor pop];
    101   [[self window] enableCursorRects];
    102 
    103   userIsResizing_ = NO;
    104   [[NSNotificationCenter defaultCenter]
    105       postNotificationName:kBrowserActionGrippyDragFinishedNotification
    106                     object:self];
    107 }
    108 
    109 - (void)mouseDragged:(NSEvent*)theEvent {
    110   if (!userIsResizing_)
    111     return;
    112 
    113   NSPoint location = [self convertPoint:[theEvent locationInWindow]
    114                                fromView:nil];
    115   NSRect containerFrame = [self frame];
    116   CGFloat dX = [theEvent deltaX];
    117   CGFloat withDelta = location.x - dX;
    118   canDragRight_ = (withDelta >= initialDragPoint_.x) &&
    119       (NSWidth(containerFrame) > kMinimumContainerWidth);
    120   canDragLeft_ = (withDelta <= initialDragPoint_.x) &&
    121       (NSWidth(containerFrame) < maxWidth_);
    122   if ((dX < 0.0 && !canDragLeft_) || (dX > 0.0 && !canDragRight_))
    123     return;
    124 
    125   containerFrame.size.width =
    126       std::max(NSWidth(containerFrame) - dX, kMinimumContainerWidth);
    127 
    128   if (NSWidth(containerFrame) == kMinimumContainerWidth)
    129     return;
    130 
    131   containerFrame.origin.x += dX;
    132 
    133   [self setFrame:containerFrame];
    134   [self setNeedsDisplay:YES];
    135 
    136   [[NSNotificationCenter defaultCenter]
    137       postNotificationName:kBrowserActionGrippyDraggingNotification
    138                     object:self];
    139 
    140   lastXPos_ += dX;
    141 }
    142 
    143 - (ViewID)viewID {
    144   return VIEW_ID_BROWSER_ACTION_TOOLBAR;
    145 }
    146 
    147 #pragma mark -
    148 #pragma mark Public Methods
    149 
    150 - (void)resizeToWidth:(CGFloat)width animate:(BOOL)animate {
    151   width = std::max(width, kMinimumContainerWidth);
    152   NSRect frame = [self frame];
    153   lastXPos_ = frame.origin.x;
    154   CGFloat dX = frame.size.width - width;
    155   frame.size.width = width;
    156   NSRect newFrame = NSOffsetRect(frame, dX, 0);
    157   if (animate) {
    158     [NSAnimationContext beginGrouping];
    159     [[NSAnimationContext currentContext] setDuration:kAnimationDuration];
    160     [[self animator] setFrame:newFrame];
    161     [NSAnimationContext endGrouping];
    162     animationEndFrame_ = newFrame;
    163   } else {
    164     [self setFrame:newFrame];
    165     [self setNeedsDisplay:YES];
    166   }
    167 }
    168 
    169 - (CGFloat)resizeDeltaX {
    170   return [self frame].origin.x - lastXPos_;
    171 }
    172 
    173 #pragma mark -
    174 #pragma mark Private Methods
    175 
    176 // Returns the cursor to display over the grippy hover region depending on the
    177 // current drag state.
    178 - (NSCursor*)appropriateCursorForGrippy {
    179   NSCursor* retVal;
    180   if (!resizable_ || (!canDragLeft_ && !canDragRight_)) {
    181     retVal = [NSCursor arrowCursor];
    182   } else if (!canDragLeft_) {
    183     retVal = [NSCursor resizeRightCursor];
    184   } else if (!canDragRight_) {
    185     retVal = [NSCursor resizeLeftCursor];
    186   } else {
    187     retVal = [NSCursor resizeLeftRightCursor];
    188   }
    189   return retVal;
    190 }
    191 
    192 @end
    193