blob: fdc00e21f70f5d98c94abe7b6613cab75db37f09 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/cocoa/panels/mouse_drag_controller.h"
#include <Carbon/Carbon.h> // kVK_Escape
#import <Cocoa/Cocoa.h>
#include "base/logging.h"
#include "base/mac/scoped_nsautorelease_pool.h"
// The distance the user has to move the mouse while keeping the left button
// down before panel resizing operation actually starts.
const double kDragThreshold = 3.0;
@implementation MouseDragController
- (NSView<MouseDragControllerClient>*)client {
return client_;
}
- (NSPoint)initialMouseLocation {
return initialMouseLocation_;
}
- (BOOL)exceedsDragThreshold:(NSPoint)mouseLocation {
float deltaX = fabs(initialMouseLocation_.x - mouseLocation.x);
float deltaY = fabs(initialMouseLocation_.y - mouseLocation.y);
return deltaX > kDragThreshold || deltaY > kDragThreshold;
}
- (BOOL)tryStartDrag:(NSEvent*)event {
DCHECK(dragState_ == PANEL_DRAG_CAN_START);
NSPoint mouseLocation = [event locationInWindow];
if (![self exceedsDragThreshold:mouseLocation])
return NO;
// Mouse moved over threshold, start drag.
dragState_ = PANEL_DRAG_IN_PROGRESS;
[client_ dragStarted:initialMouseLocation_];
return YES;
}
- (void)cleanupAfterDrag {
if (dragState_ == PANEL_DRAG_SUPPRESSED)
return;
dragState_ = PANEL_DRAG_SUPPRESSED;
initialMouseLocation_ = NSZeroPoint;
[client_ cleanupAfterDrag];
}
- (MouseDragController*)initWithClient:
(NSView<MouseDragControllerClient>*)client {
client_ = client;
dragState_ = PANEL_DRAG_SUPPRESSED;
return self;
}
- (void)mouseDown:(NSEvent*)event {
DCHECK(dragState_ == PANEL_DRAG_SUPPRESSED);
dragState_ = PANEL_DRAG_CAN_START;
initialMouseLocation_ = [event locationInWindow];
[client_ prepareForDrag];
}
- (void)mouseDragged:(NSEvent*)event {
if (dragState_ == PANEL_DRAG_SUPPRESSED)
return;
// In addition to events needed to control the drag operation, fetch the right
// mouse click events and key down events and ignore them, to prevent their
// accumulation in the queue and "playing out" when the mouse is released.
const NSUInteger mask =
NSLeftMouseUpMask | NSLeftMouseDraggedMask | NSKeyUpMask |
NSRightMouseDownMask | NSKeyDownMask ;
while (true) {
base::mac::ScopedNSAutoreleasePool autorelease_pool;
BOOL keepGoing = YES;
switch ([event type]) {
case NSLeftMouseDragged: {
// If drag didn't start yet, see if mouse moved far enough to start it.
if (dragState_ == PANEL_DRAG_CAN_START && ![self tryStartDrag:event])
return;
DCHECK(dragState_ == PANEL_DRAG_IN_PROGRESS);
[client_ dragProgress:[event locationInWindow]];
break;
}
case NSKeyUp:
if ([event keyCode] == kVK_Escape) {
// The drag might not be started yet because of threshold, so check.
if (dragState_ == PANEL_DRAG_IN_PROGRESS)
[client_ dragEnded:YES];
keepGoing = NO;
}
break;
case NSLeftMouseUp:
// The drag might not be started yet because of threshold, so check.
if (dragState_ == PANEL_DRAG_IN_PROGRESS)
[client_ dragEnded:NO];
keepGoing = NO;
break;
case NSRightMouseDownMask:
break;
default:
// Dequeue and ignore other mouse and key events so the Chrome context
// menu does not come after right click on a page during Panel
// resize, or the keystrokes are not 'accumulated' and entered
// at once when the drag ends.
break;
}
if (!keepGoing)
break;
autorelease_pool.Recycle();
event = [NSApp nextEventMatchingMask:mask
untilDate:[NSDate distantFuture]
inMode:NSDefaultRunLoopMode
dequeue:YES];
}
[self cleanupAfterDrag];
}
- (void)mouseUp:(NSEvent*)event {
if (dragState_ == PANEL_DRAG_SUPPRESSED)
return;
// The mouseUp while in drag should be processed by nested message loop
// in mouseDragged: method.
DCHECK(dragState_ != PANEL_DRAG_IN_PROGRESS);
// Do cleanup in case the actual drag was not started (because of threshold).
[self cleanupAfterDrag];
}
@end