1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
// 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
|