Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: chrome/browser/ui/cocoa/draggable_button_mixin.mm

Issue 7462018: [Mac] "Refactor" DraggableButton into a mixin and impl. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "chrome/browser/ui/cocoa/draggable_button.h" 5 #import "chrome/browser/ui/cocoa/draggable_button.h"
6 6
7 #include <complex>
Mark Mentovai 2011/08/11 00:51:17 <complex> is for std::abs on complex<> values. You
8
7 #include "base/logging.h" 9 #include "base/logging.h"
8 #import "base/memory/scoped_nsobject.h" 10 #import "base/memory/scoped_nsobject.h"
9 11
10 namespace { 12 namespace {
11 13
12 // Code taken from <http://codereview.chromium.org/180036/diff/3001/3004>. 14 // Code taken from <http://codereview.chromium.org/180036/diff/3001/3004>.
13 // TODO(viettrungluu): Do we want common, standard code for drag hysteresis? 15 // TODO(viettrungluu): Do we want common, standard code for drag hysteresis?
14 const CGFloat kWebDragStartHysteresisX = 5.0; 16 const CGFloat kWebDragStartHysteresisX = 5.0;
15 const CGFloat kWebDragStartHysteresisY = 5.0; 17 const CGFloat kWebDragStartHysteresisY = 5.0;
16 const CGFloat kDragExpirationTimeout = 0.45; 18 const CGFloat kDragExpirationTimeout = 0.45;
17 19
18 } 20 }
19 21
20 @implementation DraggableButton 22 // Private /////////////////////////////////////////////////////////////////////
23
24 @interface DraggableButtonImpl (Private)
25
26 - (BOOL)deltaIndicatesDragStartWithXDelta:(float)xDelta
27 yDelta:(float)yDelta
28 xHysteresis:(float)xHysteresis
29 yHysteresis:(float)yHysteresis;
30 - (BOOL)deltaIndicatesConclusionReachedWithXDelta:(float)xDelta
31 yDelta:(float)yDelta
32 xHysteresis:(float)xHysteresis
33 yHysteresis:(float)yHysteresis;
34 - (void)performMouseDownAction:(NSEvent*)theEvent;
35 - (void)endDrag;
36 - (BOOL)dragShouldBeginFromMouseDown:(NSEvent*)mouseDownEvent
37 withExpiration:(NSDate*)expiration;
38 - (BOOL)dragShouldBeginFromMouseDown:(NSEvent*)mouseDownEvent
39 withExpiration:(NSDate*)expiration
40 xHysteresis:(float)xHysteresis
41 yHysteresis:(float)yHysteresis;
42
43 @end
44
45 // Implementation //////////////////////////////////////////////////////////////
46
47 @implementation DraggableButtonImpl
21 48
22 @synthesize draggable = draggable_; 49 @synthesize draggable = draggable_;
23 @synthesize actsOnMouseDown = actsOnMouseDown_; 50 @synthesize actsOnMouseDown = actsOnMouseDown_;
24 @synthesize durationMouseWasDown = durationMouseWasDown_; 51 @synthesize durationMouseWasDown = durationMouseWasDown_;
25 @synthesize actionHasFired = actionHasFired_; 52 @synthesize actionHasFired = actionHasFired_;
26 @synthesize whenMouseDown = whenMouseDown_; 53 @synthesize whenMouseDown = whenMouseDown_;
27 54
28 55 - (id)initWithButton:(NSButton<DraggableButtonMixin>*)button {
29 - (id)initWithFrame:(NSRect)frame { 56 if ((self = [super init])) {
30 if ((self = [super initWithFrame:frame])) { 57 button_ = button;
31 draggable_ = YES; 58 draggable_ = YES;
32 actsOnMouseDown_ = NO; 59 actsOnMouseDown_ = NO;
33 actionHasFired_ = NO; 60 actionHasFired_ = NO;
34 } 61 }
35 return self; 62 return self;
36 } 63 }
37 64
38 - (id)initWithCoder:(NSCoder*)coder { 65 // NSButton/NSResponder Implementations ////////////////////////////////////////
39 if ((self = [super initWithCoder:coder])) { 66
40 draggable_ = YES; 67 - (DraggableButtonResult)mouseUp:(NSEvent*)theEvent {
41 actsOnMouseDown_ = NO; 68 durationMouseWasDown_ = [theEvent timestamp] - whenMouseDown_;
42 actionHasFired_ = NO; 69
70 if (actionHasFired_)
Mark Mentovai 2011/08/11 00:51:17 Here’s where the turd pile begins, but it’s actual
71 return kDraggableButtonImplDidWork;
72
73 if (!draggable_)
74 return kDraggableButtonMixinCallSuper;
75
76 // There are non-drag cases where a |-mouseUp:| may happen (e.g. mouse-down,
77 // cmd-tab to another application, move mouse, mouse-up), so check.
78 NSPoint viewLocal = [button_ convertPoint:[theEvent locationInWindow]
79 fromView:[[button_ window] contentView]];
80 if (NSPointInRect(viewLocal, [button_ bounds]))
81 [button_ performClick:self];
82
83 return kDraggableButtonImplDidWork;
84 }
85
86 // Mimic "begin a click" operation visually. Do NOT follow through with normal
87 // button event handling.
88 - (DraggableButtonResult)mouseDown:(NSEvent*)theEvent {
89 [[NSCursor arrowCursor] set];
90
91 whenMouseDown_ = [theEvent timestamp];
92 actionHasFired_ = NO;
93
94 if (draggable_) {
95 NSDate* date = [NSDate dateWithTimeIntervalSinceNow:kDragExpirationTimeout];
96 if ([self dragShouldBeginFromMouseDown:theEvent
97 withExpiration:date]) {
98 [button_ beginDrag:theEvent];
99 [self endDrag];
100 } else {
101 if (actsOnMouseDown_) {
102 [self performMouseDownAction:theEvent];
103 return kDraggableButtonImplDidWork;
104 } else {
105 return kDraggableButtonMixinCallSuper;
106 }
107 }
108 } else {
109 if (actsOnMouseDown_) {
110 [self performMouseDownAction:theEvent];
111 return kDraggableButtonImplDidWork;
112 } else {
113 return kDraggableButtonMixinCallSuper;
114 }
43 } 115 }
44 return self; 116
117 return kDraggableButtonImplDidWork;
45 } 118 }
46 119
120 // Idempotent Helpers //////////////////////////////////////////////////////////
121
47 - (BOOL)deltaIndicatesDragStartWithXDelta:(float)xDelta 122 - (BOOL)deltaIndicatesDragStartWithXDelta:(float)xDelta
48 yDelta:(float)yDelta 123 yDelta:(float)yDelta
49 xHysteresis:(float)xHysteresis 124 xHysteresis:(float)xHysteresis
50 yHysteresis:(float)yHysteresis { 125 yHysteresis:(float)yHysteresis {
51 return (ABS(xDelta) >= xHysteresis) || (ABS(yDelta) >= yHysteresis); 126 if ([button_ respondsToSelector:@selector(deltaIndicatesDragStartWithXDelta:
127 yDelta:
128 xHysteresis:
129 yHysteresis:
130 indicates:)]) {
131 BOOL indicates = NO;
132 DraggableButtonResult result = [button_
133 deltaIndicatesDragStartWithXDelta:xDelta
134 yDelta:yDelta
135 xHysteresis:xHysteresis
136 yHysteresis:yHysteresis
137 indicates:&indicates];
138 if (result != kDraggableButtonImplUseBase)
139 return indicates;
140 }
141 return (std::abs(xDelta) >= xHysteresis) || (std::abs(yDelta) >= yHysteresis);
52 } 142 }
53 143
54 - (BOOL)deltaIndicatesConclusionReachedWithXDelta:(float)xDelta 144 - (BOOL)deltaIndicatesConclusionReachedWithXDelta:(float)xDelta
55 yDelta:(float)yDelta 145 yDelta:(float)yDelta
56 xHysteresis:(float)xHysteresis 146 xHysteresis:(float)xHysteresis
57 yHysteresis:(float)yHysteresis { 147 yHysteresis:(float)yHysteresis {
58 return (ABS(xDelta) >= xHysteresis) || (ABS(yDelta) >= yHysteresis); 148 if ([button_ respondsToSelector:
149 @selector(deltaIndicatesConclusionReachedWithXDelta:
150 yDelta:
151 xHysteresis:
152 yHysteresis:
153 indicates:)]) {
154 BOOL indicates = NO;
155 DraggableButtonResult result = [button_
156 deltaIndicatesConclusionReachedWithXDelta:xDelta
157 yDelta:yDelta
158 xHysteresis:xHysteresis
159 yHysteresis:yHysteresis
160 indicates:&indicates];
161 if (result != kDraggableButtonImplUseBase)
162 return indicates;
163 }
164 return (std::abs(xDelta) >= xHysteresis) || (std::abs(yDelta) >= yHysteresis);
59 } 165 }
60 166
167 - (BOOL)dragShouldBeginFromMouseDown:(NSEvent*)mouseDownEvent
168 withExpiration:(NSDate*)expiration {
169 return [self dragShouldBeginFromMouseDown:mouseDownEvent
170 withExpiration:expiration
171 xHysteresis:kWebDragStartHysteresisX
172 yHysteresis:kWebDragStartHysteresisY];
173 }
174
175 // Implementation Details //////////////////////////////////////////////////////
61 176
62 // Determine whether a mouse down should turn into a drag; started as copy of 177 // Determine whether a mouse down should turn into a drag; started as copy of
63 // NSTableView code. 178 // NSTableView code.
64 - (BOOL)dragShouldBeginFromMouseDown:(NSEvent*)mouseDownEvent 179 - (BOOL)dragShouldBeginFromMouseDown:(NSEvent*)mouseDownEvent
65 withExpiration:(NSDate*)expiration 180 withExpiration:(NSDate*)expiration
66 xHysteresis:(float)xHysteresis 181 xHysteresis:(float)xHysteresis
67 yHysteresis:(float)yHysteresis { 182 yHysteresis:(float)yHysteresis {
68 if ([mouseDownEvent type] != NSLeftMouseDown) { 183 if ([mouseDownEvent type] != NSLeftMouseDown) {
69 return NO; 184 return NO;
70 } 185 }
71 186
72 NSEvent* nextEvent = nil; 187 NSEvent* nextEvent = nil;
73 NSEvent* firstEvent = nil; 188 NSEvent* firstEvent = nil;
74 NSEvent* dragEvent = nil; 189 NSEvent* dragEvent = nil;
75 NSEvent* mouseUp = nil; 190 NSEvent* mouseUp = nil;
76 BOOL dragIt = NO; 191 BOOL dragIt = NO;
77 192
78 while ((nextEvent = [[self window] 193 while ((nextEvent = [[button_ window]
79 nextEventMatchingMask:(NSLeftMouseUpMask | NSLeftMouseDraggedMask) 194 nextEventMatchingMask:NSLeftMouseUpMask | NSLeftMouseDraggedMask
80 untilDate:expiration 195 untilDate:expiration
81 inMode:NSEventTrackingRunLoopMode 196 inMode:NSEventTrackingRunLoopMode
82 dequeue:YES]) != nil) { 197 dequeue:YES]) != nil) {
83 if (firstEvent == nil) { 198 if (firstEvent == nil) {
84 firstEvent = nextEvent; 199 firstEvent = nextEvent;
85 } 200 }
86 if ([nextEvent type] == NSLeftMouseDragged) { 201 if ([nextEvent type] == NSLeftMouseDragged) {
87 float deltax = [nextEvent locationInWindow].x - 202 float deltax = [nextEvent locationInWindow].x -
88 [mouseDownEvent locationInWindow].x; 203 [mouseDownEvent locationInWindow].x;
89 float deltay = [nextEvent locationInWindow].y - 204 float deltay = [nextEvent locationInWindow].y -
(...skipping 25 matching lines...) Expand all
115 if (dragEvent != nil) { 230 if (dragEvent != nil) {
116 [NSApp postEvent:dragEvent atStart:YES]; 231 [NSApp postEvent:dragEvent atStart:YES];
117 } 232 }
118 if (firstEvent != mouseUp && firstEvent != dragEvent) { 233 if (firstEvent != mouseUp && firstEvent != dragEvent) {
119 [NSApp postEvent:firstEvent atStart:YES]; 234 [NSApp postEvent:firstEvent atStart:YES];
120 } 235 }
121 236
122 return dragIt; 237 return dragIt;
123 } 238 }
124 239
125 - (BOOL)dragShouldBeginFromMouseDown:(NSEvent*)mouseDownEvent 240 - (void)secondaryMouseUpAction:(BOOL)wasInside {
126 withExpiration:(NSDate*)expiration { 241 if ([button_ respondsToSelector:_cmd])
127 return [self dragShouldBeginFromMouseDown:mouseDownEvent 242 [button_ secondaryMouseUpAction:wasInside];
128 withExpiration:expiration 243
129 xHysteresis:kWebDragStartHysteresisX 244 // No actual implementation yet.
130 yHysteresis:kWebDragStartHysteresisY];
131 } 245 }
132 246
133 - (void)mouseUp:(NSEvent*)theEvent { 247 - (void)performMouseDownAction:(NSEvent*)event {
134 durationMouseWasDown_ = [theEvent timestamp] - whenMouseDown_; 248 if ([button_ respondsToSelector:_cmd] &&
135 249 [button_ performMouseDownAction:event] != kDraggableButtonImplUseBase) {
136 if (actionHasFired_) 250 return;
137 return;
138
139 if (!draggable_) {
140 [super mouseUp:theEvent];
141 return;
142 } 251 }
143 252
144 // There are non-drag cases where a mouseUp: may happen
145 // (e.g. mouse-down, cmd-tab to another application, move mouse,
146 // mouse-up). So we check.
147 NSPoint viewLocal = [self convertPoint:[theEvent locationInWindow]
148 fromView:[[self window] contentView]];
149 if (NSPointInRect(viewLocal, [self bounds])) {
150 [self performClick:self];
151 }
152 }
153
154 - (void)secondaryMouseUpAction:(BOOL)wasInside {
155 // Override if you want to do any extra work on mouseUp, after a mouseDown
156 // action has already fired.
157 }
158
159 - (void)performMouseDownAction:(NSEvent*)theEvent {
160 int eventMask = NSLeftMouseUpMask; 253 int eventMask = NSLeftMouseUpMask;
161 254
162 [[self target] performSelector:[self action] withObject:self]; 255 [[button_ target] performSelector:[button_ action] withObject:self];
163 actionHasFired_ = YES; 256 actionHasFired_ = YES;
164 257
165 while (1) { 258 while (1) {
166 theEvent = [[self window] nextEventMatchingMask:eventMask]; 259 event = [[button_ window] nextEventMatchingMask:eventMask];
167 if (!theEvent) 260 if (!event)
168 continue; 261 continue;
169 NSPoint mouseLoc = [self convertPoint:[theEvent locationInWindow] 262 NSPoint mouseLoc = [button_ convertPoint:[event locationInWindow]
170 fromView:nil]; 263 fromView:nil];
171 BOOL isInside = [self mouse:mouseLoc inRect:[self bounds]]; 264 BOOL isInside = [button_ mouse:mouseLoc inRect:[button_ bounds]];
172 [self highlight:isInside]; 265 [button_ highlight:isInside];
173 266
174 switch ([theEvent type]) { 267 switch ([event type]) {
175 case NSLeftMouseUp: 268 case NSLeftMouseUp:
176 durationMouseWasDown_ = [theEvent timestamp] - whenMouseDown_; 269 durationMouseWasDown_ = [event timestamp] - whenMouseDown_;
177 [self secondaryMouseUpAction:isInside]; 270 [self secondaryMouseUpAction:isInside];
178 break; 271 break;
179 default: 272 default:
180 /* Ignore any other kind of event. */ 273 // Ignore any other kind of event.
181 break; 274 break;
182 } 275 }
183 } 276 }
184 277
185 [self highlight:NO]; 278 [button_ highlight:NO];
186 }
187
188 // Mimic "begin a click" operation visually. Do NOT follow through
189 // with normal button event handling.
190 - (void)mouseDown:(NSEvent*)theEvent {
191 [[NSCursor arrowCursor] set];
192
193 whenMouseDown_ = [theEvent timestamp];
194 actionHasFired_ = NO;
195
196 if (draggable_) {
197 NSDate* date = [NSDate dateWithTimeIntervalSinceNow:kDragExpirationTimeout];
198 if ([self dragShouldBeginFromMouseDown:theEvent
199 withExpiration:date]) {
200 [self beginDrag:theEvent];
201 [self endDrag];
202 } else {
203 if (actsOnMouseDown_) {
204 [self performMouseDownAction:theEvent];
205 } else {
206 [super mouseDown:theEvent];
207 }
208
209 }
210 } else {
211 if (actsOnMouseDown_) {
212 [self performMouseDownAction:theEvent];
213 } else {
214 [super mouseDown:theEvent];
215 }
216 }
217 }
218
219 - (void)beginDrag:(NSEvent*)dragEvent {
220 // Must be overridden by subclasses.
221 NOTREACHED();
222 } 279 }
223 280
224 - (void)endDrag { 281 - (void)endDrag {
225 [self highlight:NO]; 282 if ([button_ respondsToSelector:_cmd] &&
283 [button_ endDrag] != kDraggableButtonImplUseBase) {
284 return;
285 }
286 [button_ highlight:NO];
226 } 287 }
227 288
228 @end // @interface DraggableButton 289 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698