Index: chrome/browser/ui/cocoa/draggable_button_mixin.mm |
diff --git a/chrome/browser/ui/cocoa/draggable_button.mm b/chrome/browser/ui/cocoa/draggable_button_mixin.mm |
similarity index 50% |
copy from chrome/browser/ui/cocoa/draggable_button.mm |
copy to chrome/browser/ui/cocoa/draggable_button_mixin.mm |
index 4d03b2790ec78127546126e082bdf5dd4854b47c..a005a8a05df587470c43d5a2da7c0a84044675dc 100644 |
--- a/chrome/browser/ui/cocoa/draggable_button.mm |
+++ b/chrome/browser/ui/cocoa/draggable_button_mixin.mm |
@@ -4,6 +4,8 @@ |
#import "chrome/browser/ui/cocoa/draggable_button.h" |
+#include <complex> |
Mark Mentovai
2011/08/11 00:51:17
<complex> is for std::abs on complex<> values. You
|
+ |
#include "base/logging.h" |
#import "base/memory/scoped_nsobject.h" |
@@ -17,7 +19,32 @@ const CGFloat kDragExpirationTimeout = 0.45; |
} |
-@implementation DraggableButton |
+// Private ///////////////////////////////////////////////////////////////////// |
+ |
+@interface DraggableButtonImpl (Private) |
+ |
+- (BOOL)deltaIndicatesDragStartWithXDelta:(float)xDelta |
+ yDelta:(float)yDelta |
+ xHysteresis:(float)xHysteresis |
+ yHysteresis:(float)yHysteresis; |
+- (BOOL)deltaIndicatesConclusionReachedWithXDelta:(float)xDelta |
+ yDelta:(float)yDelta |
+ xHysteresis:(float)xHysteresis |
+ yHysteresis:(float)yHysteresis; |
+- (void)performMouseDownAction:(NSEvent*)theEvent; |
+- (void)endDrag; |
+- (BOOL)dragShouldBeginFromMouseDown:(NSEvent*)mouseDownEvent |
+ withExpiration:(NSDate*)expiration; |
+- (BOOL)dragShouldBeginFromMouseDown:(NSEvent*)mouseDownEvent |
+ withExpiration:(NSDate*)expiration |
+ xHysteresis:(float)xHysteresis |
+ yHysteresis:(float)yHysteresis; |
+ |
+@end |
+ |
+// Implementation ////////////////////////////////////////////////////////////// |
+ |
+@implementation DraggableButtonImpl |
@synthesize draggable = draggable_; |
@synthesize actsOnMouseDown = actsOnMouseDown_; |
@@ -25,9 +52,9 @@ const CGFloat kDragExpirationTimeout = 0.45; |
@synthesize actionHasFired = actionHasFired_; |
@synthesize whenMouseDown = whenMouseDown_; |
- |
-- (id)initWithFrame:(NSRect)frame { |
- if ((self = [super initWithFrame:frame])) { |
+- (id)initWithButton:(NSButton<DraggableButtonMixin>*)button { |
+ if ((self = [super init])) { |
+ button_ = button; |
draggable_ = YES; |
actsOnMouseDown_ = NO; |
actionHasFired_ = NO; |
@@ -35,29 +62,117 @@ const CGFloat kDragExpirationTimeout = 0.45; |
return self; |
} |
-- (id)initWithCoder:(NSCoder*)coder { |
- if ((self = [super initWithCoder:coder])) { |
- draggable_ = YES; |
- actsOnMouseDown_ = NO; |
- actionHasFired_ = NO; |
+// NSButton/NSResponder Implementations //////////////////////////////////////// |
+ |
+- (DraggableButtonResult)mouseUp:(NSEvent*)theEvent { |
+ durationMouseWasDown_ = [theEvent timestamp] - whenMouseDown_; |
+ |
+ if (actionHasFired_) |
Mark Mentovai
2011/08/11 00:51:17
Here’s where the turd pile begins, but it’s actual
|
+ return kDraggableButtonImplDidWork; |
+ |
+ if (!draggable_) |
+ return kDraggableButtonMixinCallSuper; |
+ |
+ // There are non-drag cases where a |-mouseUp:| may happen (e.g. mouse-down, |
+ // cmd-tab to another application, move mouse, mouse-up), so check. |
+ NSPoint viewLocal = [button_ convertPoint:[theEvent locationInWindow] |
+ fromView:[[button_ window] contentView]]; |
+ if (NSPointInRect(viewLocal, [button_ bounds])) |
+ [button_ performClick:self]; |
+ |
+ return kDraggableButtonImplDidWork; |
+} |
+ |
+// Mimic "begin a click" operation visually. Do NOT follow through with normal |
+// button event handling. |
+- (DraggableButtonResult)mouseDown:(NSEvent*)theEvent { |
+ [[NSCursor arrowCursor] set]; |
+ |
+ whenMouseDown_ = [theEvent timestamp]; |
+ actionHasFired_ = NO; |
+ |
+ if (draggable_) { |
+ NSDate* date = [NSDate dateWithTimeIntervalSinceNow:kDragExpirationTimeout]; |
+ if ([self dragShouldBeginFromMouseDown:theEvent |
+ withExpiration:date]) { |
+ [button_ beginDrag:theEvent]; |
+ [self endDrag]; |
+ } else { |
+ if (actsOnMouseDown_) { |
+ [self performMouseDownAction:theEvent]; |
+ return kDraggableButtonImplDidWork; |
+ } else { |
+ return kDraggableButtonMixinCallSuper; |
+ } |
+ } |
+ } else { |
+ if (actsOnMouseDown_) { |
+ [self performMouseDownAction:theEvent]; |
+ return kDraggableButtonImplDidWork; |
+ } else { |
+ return kDraggableButtonMixinCallSuper; |
+ } |
} |
- return self; |
+ |
+ return kDraggableButtonImplDidWork; |
} |
+// Idempotent Helpers ////////////////////////////////////////////////////////// |
+ |
- (BOOL)deltaIndicatesDragStartWithXDelta:(float)xDelta |
yDelta:(float)yDelta |
xHysteresis:(float)xHysteresis |
yHysteresis:(float)yHysteresis { |
- return (ABS(xDelta) >= xHysteresis) || (ABS(yDelta) >= yHysteresis); |
+ if ([button_ respondsToSelector:@selector(deltaIndicatesDragStartWithXDelta: |
+ yDelta: |
+ xHysteresis: |
+ yHysteresis: |
+ indicates:)]) { |
+ BOOL indicates = NO; |
+ DraggableButtonResult result = [button_ |
+ deltaIndicatesDragStartWithXDelta:xDelta |
+ yDelta:yDelta |
+ xHysteresis:xHysteresis |
+ yHysteresis:yHysteresis |
+ indicates:&indicates]; |
+ if (result != kDraggableButtonImplUseBase) |
+ return indicates; |
+ } |
+ return (std::abs(xDelta) >= xHysteresis) || (std::abs(yDelta) >= yHysteresis); |
} |
- (BOOL)deltaIndicatesConclusionReachedWithXDelta:(float)xDelta |
yDelta:(float)yDelta |
xHysteresis:(float)xHysteresis |
yHysteresis:(float)yHysteresis { |
- return (ABS(xDelta) >= xHysteresis) || (ABS(yDelta) >= yHysteresis); |
+ if ([button_ respondsToSelector: |
+ @selector(deltaIndicatesConclusionReachedWithXDelta: |
+ yDelta: |
+ xHysteresis: |
+ yHysteresis: |
+ indicates:)]) { |
+ BOOL indicates = NO; |
+ DraggableButtonResult result = [button_ |
+ deltaIndicatesConclusionReachedWithXDelta:xDelta |
+ yDelta:yDelta |
+ xHysteresis:xHysteresis |
+ yHysteresis:yHysteresis |
+ indicates:&indicates]; |
+ if (result != kDraggableButtonImplUseBase) |
+ return indicates; |
+ } |
+ return (std::abs(xDelta) >= xHysteresis) || (std::abs(yDelta) >= yHysteresis); |
+} |
+ |
+- (BOOL)dragShouldBeginFromMouseDown:(NSEvent*)mouseDownEvent |
+ withExpiration:(NSDate*)expiration { |
+ return [self dragShouldBeginFromMouseDown:mouseDownEvent |
+ withExpiration:expiration |
+ xHysteresis:kWebDragStartHysteresisX |
+ yHysteresis:kWebDragStartHysteresisY]; |
} |
+// Implementation Details ////////////////////////////////////////////////////// |
// Determine whether a mouse down should turn into a drag; started as copy of |
// NSTableView code. |
@@ -75,8 +190,8 @@ const CGFloat kDragExpirationTimeout = 0.45; |
NSEvent* mouseUp = nil; |
BOOL dragIt = NO; |
- while ((nextEvent = [[self window] |
- nextEventMatchingMask:(NSLeftMouseUpMask | NSLeftMouseDraggedMask) |
+ while ((nextEvent = [[button_ window] |
+ nextEventMatchingMask:NSLeftMouseUpMask | NSLeftMouseDraggedMask |
untilDate:expiration |
inMode:NSEventTrackingRunLoopMode |
dequeue:YES]) != nil) { |
@@ -122,107 +237,53 @@ const CGFloat kDragExpirationTimeout = 0.45; |
return dragIt; |
} |
-- (BOOL)dragShouldBeginFromMouseDown:(NSEvent*)mouseDownEvent |
- withExpiration:(NSDate*)expiration { |
- return [self dragShouldBeginFromMouseDown:mouseDownEvent |
- withExpiration:expiration |
- xHysteresis:kWebDragStartHysteresisX |
- yHysteresis:kWebDragStartHysteresisY]; |
-} |
- |
-- (void)mouseUp:(NSEvent*)theEvent { |
- durationMouseWasDown_ = [theEvent timestamp] - whenMouseDown_; |
- |
- if (actionHasFired_) |
- return; |
- |
- if (!draggable_) { |
- [super mouseUp:theEvent]; |
- return; |
- } |
+- (void)secondaryMouseUpAction:(BOOL)wasInside { |
+ if ([button_ respondsToSelector:_cmd]) |
+ [button_ secondaryMouseUpAction:wasInside]; |
- // 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]; |
- } |
+ // No actual implementation yet. |
} |
-- (void)secondaryMouseUpAction:(BOOL)wasInside { |
- // Override if you want to do any extra work on mouseUp, after a mouseDown |
- // action has already fired. |
-} |
+- (void)performMouseDownAction:(NSEvent*)event { |
+ if ([button_ respondsToSelector:_cmd] && |
+ [button_ performMouseDownAction:event] != kDraggableButtonImplUseBase) { |
+ return; |
+ } |
-- (void)performMouseDownAction:(NSEvent*)theEvent { |
int eventMask = NSLeftMouseUpMask; |
- [[self target] performSelector:[self action] withObject:self]; |
+ [[button_ target] performSelector:[button_ action] withObject:self]; |
actionHasFired_ = YES; |
while (1) { |
- theEvent = [[self window] nextEventMatchingMask:eventMask]; |
- if (!theEvent) |
+ event = [[button_ window] nextEventMatchingMask:eventMask]; |
+ if (!event) |
continue; |
- NSPoint mouseLoc = [self convertPoint:[theEvent locationInWindow] |
- fromView:nil]; |
- BOOL isInside = [self mouse:mouseLoc inRect:[self bounds]]; |
- [self highlight:isInside]; |
+ NSPoint mouseLoc = [button_ convertPoint:[event locationInWindow] |
+ fromView:nil]; |
+ BOOL isInside = [button_ mouse:mouseLoc inRect:[button_ bounds]]; |
+ [button_ highlight:isInside]; |
- switch ([theEvent type]) { |
+ switch ([event type]) { |
case NSLeftMouseUp: |
- durationMouseWasDown_ = [theEvent timestamp] - whenMouseDown_; |
+ durationMouseWasDown_ = [event timestamp] - whenMouseDown_; |
[self secondaryMouseUpAction:isInside]; |
break; |
default: |
- /* Ignore any other kind of event. */ |
+ // Ignore any other kind of event. |
break; |
} |
} |
- [self highlight:NO]; |
-} |
- |
-// Mimic "begin a click" operation visually. Do NOT follow through |
-// with normal button event handling. |
-- (void)mouseDown:(NSEvent*)theEvent { |
- [[NSCursor arrowCursor] set]; |
- |
- whenMouseDown_ = [theEvent timestamp]; |
- actionHasFired_ = NO; |
- |
- if (draggable_) { |
- NSDate* date = [NSDate dateWithTimeIntervalSinceNow:kDragExpirationTimeout]; |
- if ([self dragShouldBeginFromMouseDown:theEvent |
- withExpiration:date]) { |
- [self beginDrag:theEvent]; |
- [self endDrag]; |
- } else { |
- if (actsOnMouseDown_) { |
- [self performMouseDownAction:theEvent]; |
- } else { |
- [super mouseDown:theEvent]; |
- } |
- |
- } |
- } else { |
- if (actsOnMouseDown_) { |
- [self performMouseDownAction:theEvent]; |
- } else { |
- [super mouseDown:theEvent]; |
- } |
- } |
-} |
- |
-- (void)beginDrag:(NSEvent*)dragEvent { |
- // Must be overridden by subclasses. |
- NOTREACHED(); |
+ [button_ highlight:NO]; |
} |
- (void)endDrag { |
- [self highlight:NO]; |
+ if ([button_ respondsToSelector:_cmd] && |
+ [button_ endDrag] != kDraggableButtonImplUseBase) { |
+ return; |
+ } |
+ [button_ highlight:NO]; |
} |
-@end // @interface DraggableButton |
+@end |