• 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/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