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