• 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 "base/memory/scoped_nsobject.h"
11#import "chrome/browser/ui/cocoa/view_id_util.h"
12
13NSString* const kBrowserActionGrippyDragStartedNotification =
14    @"BrowserActionGrippyDragStartedNotification";
15NSString* const kBrowserActionGrippyDraggingNotification =
16    @"BrowserActionGrippyDraggingNotification";
17NSString* const kBrowserActionGrippyDragFinishedNotification =
18    @"BrowserActionGrippyDragFinishedNotification";
19
20namespace {
21const CGFloat kAnimationDuration = 0.2;
22const CGFloat kGrippyWidth = 4.0;
23const 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