blob: 8eb7f9867342a54c83c1a4e49d388ba3ad164916 (
plain)
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
146
147
148
149
150
|
// Copyright (c) 2010 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.
#import "chrome/browser/cocoa/draggable_button.h"
#include "base/logging.h"
#import "base/scoped_nsobject.h"
namespace {
// Code taken from <http://codereview.chromium.org/180036/diff/3001/3004>.
// TODO(viettrungluu): Do we want common, standard code for drag hysteresis?
const CGFloat kWebDragStartHysteresisX = 5.0;
const CGFloat kWebDragStartHysteresisY = 5.0;
const CGFloat kDragExpirationTimeout = 1.0;
}
@implementation DraggableButton
@synthesize draggable = draggable_;
- (id)initWithFrame:(NSRect)frame {
if ((self = [super initWithFrame:frame])) {
draggable_ = YES;
}
return self;
}
- (id)initWithCoder:(NSCoder*)coder {
if ((self = [super initWithCoder:coder])) {
draggable_ = YES;
}
return self;
}
// Determine whether a mouse down should turn into a drag; started as copy of
// NSTableView code.
- (BOOL)dragShouldBeginFromMouseDown:(NSEvent*)mouseDownEvent
withExpiration:(NSDate*)expiration
xHysteresis:(float)xHysteresis
yHysteresis:(float)yHysteresis {
if ([mouseDownEvent type] != NSLeftMouseDown) {
return NO;
}
NSEvent* nextEvent = nil;
NSEvent* firstEvent = nil;
NSEvent* dragEvent = nil;
NSEvent* mouseUp = nil;
BOOL dragIt = NO;
while ((nextEvent = [[self window]
nextEventMatchingMask:(NSLeftMouseUpMask | NSLeftMouseDraggedMask)
untilDate:expiration
inMode:NSEventTrackingRunLoopMode
dequeue:YES]) != nil) {
if (firstEvent == nil) {
firstEvent = nextEvent;
}
if ([nextEvent type] == NSLeftMouseDragged) {
float deltax = ABS([nextEvent locationInWindow].x -
[mouseDownEvent locationInWindow].x);
float deltay = ABS([nextEvent locationInWindow].y -
[mouseDownEvent locationInWindow].y);
dragEvent = nextEvent;
if (deltax >= xHysteresis) {
dragIt = YES;
break;
}
if (deltay >= yHysteresis) {
dragIt = YES;
break;
}
} else if ([nextEvent type] == NSLeftMouseUp) {
mouseUp = nextEvent;
break;
}
}
// Since we've been dequeuing the events (If we don't, we'll never see
// the mouse up...), we need to push some of the events back on.
// It makes sense to put the first and last drag events and the mouse
// up if there was one.
if (mouseUp != nil) {
[NSApp postEvent:mouseUp atStart:YES];
}
if (dragEvent != nil) {
[NSApp postEvent:dragEvent atStart:YES];
}
if (firstEvent != mouseUp && firstEvent != dragEvent) {
[NSApp postEvent:firstEvent atStart:YES];
}
return dragIt;
}
- (BOOL)dragShouldBeginFromMouseDown:(NSEvent*)mouseDownEvent
withExpiration:(NSDate*)expiration {
return [self dragShouldBeginFromMouseDown:mouseDownEvent
withExpiration:expiration
xHysteresis:kWebDragStartHysteresisX
yHysteresis:kWebDragStartHysteresisY];
}
- (void)mouseUp:(NSEvent*)theEvent {
if (!draggable_) {
[super mouseUp:theEvent];
return;
}
// There are non-drag cases where a mouseUp: may happen
// (e.g. mouse-down, cmd-tab to another application, move mouse,
// mouse-up). So we check.
NSPoint viewLocal = [self convertPoint:[theEvent locationInWindow]
fromView:[[self window] contentView]];
if (NSPointInRect(viewLocal, [self bounds])) {
[self performClick:self];
}
}
// Mimic "begin a click" operation visually. Do NOT follow through
// with normal button event handling.
- (void)mouseDown:(NSEvent*)theEvent {
if (draggable_) {
[[self cell] setHighlighted:YES];
NSDate* date = [NSDate dateWithTimeIntervalSinceNow:kDragExpirationTimeout];
if ([self dragShouldBeginFromMouseDown:theEvent
withExpiration:date]) {
[self beginDrag:theEvent];
[self endDrag];
} else {
[super mouseDown:theEvent];
}
} else {
[super mouseDown:theEvent];
}
}
- (void)beginDrag:(NSEvent*)dragEvent {
// Must be overridden by subclasses.
NOTREACHED();
}
- (void)endDrag {
[[self cell] setHighlighted:NO];
}
@end // @interface DraggableButton
|