1 // Copyright (c) 2012 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.
5 #include "chrome/browser/ui/cocoa/panels/mouse_drag_controller.h"
7 #include <Carbon/Carbon.h> // kVK_Escape
8 #import <Cocoa/Cocoa.h>
10 #include "base/logging.h"
11 #include "base/mac/scoped_nsautorelease_pool.h"
13 // The distance the user has to move the mouse while keeping the left button
14 // down before panel resizing operation actually starts.
15 const double kDragThreshold = 3.0;
17 @implementation MouseDragController
19 - (NSView<MouseDragControllerClient>*)client {
23 - (NSPoint)initialMouseLocation {
24 return initialMouseLocation_;
27 - (BOOL)exceedsDragThreshold:(NSPoint)mouseLocation {
28 float deltaX = fabs(initialMouseLocation_.x - mouseLocation.x);
29 float deltaY = fabs(initialMouseLocation_.y - mouseLocation.y);
30 return deltaX > kDragThreshold || deltaY > kDragThreshold;
33 - (BOOL)tryStartDrag:(NSEvent*)event {
34 DCHECK(dragState_ == PANEL_DRAG_CAN_START);
35 NSPoint mouseLocation = [event locationInWindow];
36 if (![self exceedsDragThreshold:mouseLocation])
39 // Mouse moved over threshold, start drag.
40 dragState_ = PANEL_DRAG_IN_PROGRESS;
41 [client_ dragStarted:initialMouseLocation_];
45 - (void)cleanupAfterDrag {
46 if (dragState_ == PANEL_DRAG_SUPPRESSED)
48 dragState_ = PANEL_DRAG_SUPPRESSED;
49 initialMouseLocation_ = NSZeroPoint;
50 [client_ cleanupAfterDrag];
53 - (MouseDragController*)initWithClient:
54 (NSView<MouseDragControllerClient>*)client {
56 dragState_ = PANEL_DRAG_SUPPRESSED;
60 - (void)mouseDown:(NSEvent*)event {
61 DCHECK(dragState_ == PANEL_DRAG_SUPPRESSED);
62 dragState_ = PANEL_DRAG_CAN_START;
63 initialMouseLocation_ = [event locationInWindow];
64 [client_ prepareForDrag];
67 - (void)mouseDragged:(NSEvent*)event {
68 if (dragState_ == PANEL_DRAG_SUPPRESSED)
71 // In addition to events needed to control the drag operation, fetch the right
72 // mouse click events and key down events and ignore them, to prevent their
73 // accumulation in the queue and "playing out" when the mouse is released.
74 const NSUInteger mask =
75 NSLeftMouseUpMask | NSLeftMouseDraggedMask | NSKeyUpMask |
76 NSRightMouseDownMask | NSKeyDownMask ;
79 base::mac::ScopedNSAutoreleasePool autorelease_pool;
82 switch ([event type]) {
83 case NSLeftMouseDragged: {
84 // If drag didn't start yet, see if mouse moved far enough to start it.
85 if (dragState_ == PANEL_DRAG_CAN_START && ![self tryStartDrag:event])
88 DCHECK(dragState_ == PANEL_DRAG_IN_PROGRESS);
89 [client_ dragProgress:[event locationInWindow]];
94 if ([event keyCode] == kVK_Escape) {
95 // The drag might not be started yet because of threshold, so check.
96 if (dragState_ == PANEL_DRAG_IN_PROGRESS)
97 [client_ dragEnded:YES];
103 // The drag might not be started yet because of threshold, so check.
104 if (dragState_ == PANEL_DRAG_IN_PROGRESS)
105 [client_ dragEnded:NO];
109 case NSRightMouseDownMask:
113 // Dequeue and ignore other mouse and key events so the Chrome context
114 // menu does not come after right click on a page during Panel
115 // resize, or the keystrokes are not 'accumulated' and entered
116 // at once when the drag ends.
123 autorelease_pool.Recycle();
125 event = [NSApp nextEventMatchingMask:mask
126 untilDate:[NSDate distantFuture]
127 inMode:NSDefaultRunLoopMode
131 [self cleanupAfterDrag];
135 - (void)mouseUp:(NSEvent*)event {
136 if (dragState_ == PANEL_DRAG_SUPPRESSED)
138 // The mouseUp while in drag should be processed by nested message loop
139 // in mouseDragged: method.
140 DCHECK(dragState_ != PANEL_DRAG_IN_PROGRESS);
141 // Do cleanup in case the actual drag was not started (because of threshold).
142 [self cleanupAfterDrag];