summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorfeldstein@chromium.org <feldstein@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-16 22:01:25 +0000
committerfeldstein@chromium.org <feldstein@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-16 22:01:25 +0000
commit9038098882dc2e0c8963132de61df26ac4253811 (patch)
treee34f4136d5a99cdda340035bb4571285ab5be9a4 /chrome/browser
parent1239af15394f84827718c0c1327afd85c6bd1c22 (diff)
downloadchromium_src-9038098882dc2e0c8963132de61df26ac4253811.zip
chromium_src-9038098882dc2e0c8963132de61df26ac4253811.tar.gz
chromium_src-9038098882dc2e0c8963132de61df26ac4253811.tar.bz2
Fix stuck highlight state when dragging other bookmarks folder, or when dragging something from the download shelf.
DraggingButton.mm was setting up non-draggable buttons to be ready for drag. DownloadShelfButtons never called endDrag. BUG=40594 TEST=Try to drag the Other Bookmarks button. It shouldn't stay highlighted. Review URL: http://codereview.chromium.org/1648003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@44830 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/cocoa/bookmark_button.mm6
-rw-r--r--chrome/browser/cocoa/draggable_button.h21
-rw-r--r--chrome/browser/cocoa/draggable_button.mm125
-rw-r--r--chrome/browser/cocoa/draggable_button_unittest.mm144
4 files changed, 216 insertions, 80 deletions
diff --git a/chrome/browser/cocoa/bookmark_button.mm b/chrome/browser/cocoa/bookmark_button.mm
index 4be967a..fea9d25 100644
--- a/chrome/browser/cocoa/bookmark_button.mm
+++ b/chrome/browser/cocoa/bookmark_button.mm
@@ -96,12 +96,6 @@ static const CGFloat kDragImageOpacity = 0.7;
[super endDrag];
}
-- (void)draggedImage:(NSImage*)anImage
- endedAt:(NSPoint)aPoint
- operation:(NSDragOperation)operation {
- [self endDrag];
-}
-
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
return isLocal ? NSDragOperationCopy | NSDragOperationMove
: NSDragOperationCopy;
diff --git a/chrome/browser/cocoa/draggable_button.h b/chrome/browser/cocoa/draggable_button.h
index ed9a94f..eee719a 100644
--- a/chrome/browser/cocoa/draggable_button.h
+++ b/chrome/browser/cocoa/draggable_button.h
@@ -10,21 +10,24 @@
@interface DraggableButton : NSButton {
@private
BOOL draggable_; // Is this a draggable type of button?
- BOOL mayDragStart_; // Set to YES on mouse down, NO on up or drag.
- BOOL beingDragged_;
-
- // Initial mouse-down to prevent a hair-trigger drag.
- NSPoint initialMouseDownLocation_;
}
// Enable or disable dragability for special buttons like "Other Bookmarks".
@property BOOL draggable;
-// Called when a drag starts. Subclasses must override this.
+// Called when a drag should start. Subclasses must override this to do any
+// pasteboard manipulation and begin the drag, usually with
+// -dragImage:at:offset:event:. Subclasses must call one of the blocking
+// -drag* methods of NSView when overriding this method.
- (void)beginDrag:(NSEvent*)dragEvent;
-// Subclasses should call this method to notify DraggableButton when a drag is
-// over.
+@end // @interface DraggableButton
+
+@interface DraggableButton (Private)
+
+// Resets the draggable state of the button after dragging is finished. This is
+// called by DraggableButton when the beginDrag call returns, it should not be
+// called by the subclass.
- (void)endDrag;
-@end // @interface DraggableButton
+@end // @interface DraggableButton(Private)
diff --git a/chrome/browser/cocoa/draggable_button.mm b/chrome/browser/cocoa/draggable_button.mm
index 4ede10e..b45c2a8 100644
--- a/chrome/browser/cocoa/draggable_button.mm
+++ b/chrome/browser/cocoa/draggable_button.mm
@@ -13,6 +13,7 @@ namespace {
// 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;
}
@@ -33,10 +34,81 @@ const CGFloat kWebDragStartHysteresisY = 5.0;
}
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 {
- // Make sure that we can't start a drag until we see a mouse down again.
- mayDragStart_ = NO;
+ 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,
@@ -45,48 +117,24 @@ const CGFloat kWebDragStartHysteresisY = 5.0;
fromView:[[self window] contentView]];
if (NSPointInRect(viewLocal, [self bounds])) {
[self performClick:self];
- } else {
- // Make sure an ESC to end a drag doesn't trigger 2 endDrags.
- if (beingDragged_) {
- [self endDrag];
- } else {
- [super mouseUp:theEvent];
- }
}
}
// Mimic "begin a click" operation visually. Do NOT follow through
// with normal button event handling.
- (void)mouseDown:(NSEvent*)theEvent {
- mayDragStart_ = YES;
- [[self cell] setHighlighted:YES];
- initialMouseDownLocation_ = [theEvent locationInWindow];
-}
-
-// Return YES if we have crossed a threshold of movement after
-// mouse-down when we should begin a drag. Else NO.
-- (BOOL)hasCrossedDragThreshold:(NSEvent*)theEvent {
- NSPoint currentLocation = [theEvent locationInWindow];
-
- if ((abs(currentLocation.x - initialMouseDownLocation_.x) >
- kWebDragStartHysteresisX) ||
- (abs(currentLocation.y - initialMouseDownLocation_.y) >
- kWebDragStartHysteresisY)) {
- return YES;
+ 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 {
- return NO;
- }
-}
-
-- (void)mouseDragged:(NSEvent*)theEvent {
- if (beingDragged_) {
- [super mouseDragged:theEvent];
- } else if (draggable_ && mayDragStart_ &&
- [self hasCrossedDragThreshold:theEvent]) {
- // Starting drag. Never start another drag until another mouse down.
- mayDragStart_ = NO;
- beingDragged_ = YES;
- [self beginDrag:theEvent];
+ [super mouseDown:theEvent];
}
}
@@ -96,8 +144,7 @@ const CGFloat kWebDragStartHysteresisY = 5.0;
}
- (void)endDrag {
- beingDragged_ = NO;
[[self cell] setHighlighted:NO];
}
-@end
+@end // @interface DraggableButton
diff --git a/chrome/browser/cocoa/draggable_button_unittest.mm b/chrome/browser/cocoa/draggable_button_unittest.mm
index 9b892cc..6742721 100644
--- a/chrome/browser/cocoa/draggable_button_unittest.mm
+++ b/chrome/browser/cocoa/draggable_button_unittest.mm
@@ -5,41 +5,133 @@
#include "base/scoped_nsobject.h"
#import "chrome/browser/cocoa/bookmark_button.h"
#import "chrome/browser/cocoa/cocoa_test_helper.h"
+#import "chrome/browser/cocoa/test_event_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
-NSEvent* Event(const NSPoint point, const NSEventType type) {
- static NSUInteger eventNumber = 0; // thx shess
- return [NSEvent mouseEventWithType:type
- location:point
- modifierFlags:0
- timestamp:0
- windowNumber:183 // picked out of thin air.
- context:nil
- eventNumber:eventNumber++
- clickCount:1
- pressure:0.0];
+@interface TestableDraggableButton : DraggableButton {
+ NSUInteger dragCount_;
+ BOOL wasTriggered_;
}
+- (void)trigger:(id)sender;
+- (BOOL)wasTriggered;
+- (NSUInteger)dragCount;
+@end
+
+@implementation TestableDraggableButton
+- (id)initWithFrame:(NSRect)frame {
+ if ((self = [super initWithFrame:frame])) {
+ dragCount_ = 0;
+ wasTriggered_ = NO;
+ }
+ return self;
+}
+- (void)beginDrag:(NSEvent*)theEvent {
+ dragCount_++;
+}
+
+- (void)trigger:(id)sender {
+ wasTriggered_ = YES;
+}
+
+- (BOOL)wasTriggered {
+ return wasTriggered_;
+}
+
+- (NSUInteger)dragCount {
+ return dragCount_;
+}
+@end
+
+class DraggableButtonTest : public CocoaTest {};
// Make sure the basic case of "click" still works.
-TEST(DraggableButtonTest, DownUp) {
- scoped_nsobject<NSMutableArray> array;
- array.reset([[NSMutableArray alloc] init]);
- [array addObject:@"foo"];
- [array addObject:@"bar"];
+TEST_F(DraggableButtonTest, DownUp) {
+ scoped_nsobject<TestableDraggableButton> button(
+ [[TestableDraggableButton alloc] initWithFrame:NSMakeRect(0,0,500,500)]);
+ [[test_window() contentView] addSubview:button.get()];
+ [button setTarget:button];
+ [button setAction:@selector(trigger:)];
+ EXPECT_FALSE([button wasTriggered]);
+ NSEvent* downEvent =
+ test_event_utils::MouseEventAtPoint(NSMakePoint(10,10),
+ NSLeftMouseDown, 0);
+ NSEvent* upEvent =
+ test_event_utils::MouseEventAtPoint(NSMakePoint(10,10),
+ NSLeftMouseUp, 0);
+ [NSApp postEvent:upEvent atStart:YES];
+ [test_window() sendEvent:downEvent];
+ EXPECT_TRUE([button wasTriggered]); // confirms target/action fired
+}
- scoped_nsobject<DraggableButton> button;
- button.reset([[DraggableButton alloc] initWithFrame:NSMakeRect(0,0,500,500)]);
+TEST_F(DraggableButtonTest, DraggableHysteresis) {
+ scoped_nsobject<TestableDraggableButton> button(
+ [[TestableDraggableButton alloc] initWithFrame:NSMakeRect(0,0,500,500)]);
+ [[test_window() contentView] addSubview:button.get()];
+ NSEvent* downEvent =
+ test_event_utils::MouseEventAtPoint(NSMakePoint(10,10),
+ NSLeftMouseDown,
+ 0);
+ NSEvent* firstMove =
+ test_event_utils::MouseEventAtPoint(NSMakePoint(11,11),
+ NSLeftMouseDragged,
+ 0);
+ NSEvent* firstUpEvent =
+ test_event_utils::MouseEventAtPoint(NSMakePoint(11,11),
+ NSLeftMouseUp,
+ 0);
+ NSEvent* secondMove =
+ test_event_utils::MouseEventAtPoint(NSMakePoint(100,100),
+ NSLeftMouseDragged,
+ 0);
+ NSEvent* secondUpEvent =
+ test_event_utils::MouseEventAtPoint(NSMakePoint(100,100),
+ NSLeftMouseUp,
+ 0);
+ // If the mouse only moves one pixel in each direction
+ // it should not cause a drag.
+ [NSApp postEvent:firstUpEvent atStart:YES];
+ [NSApp postEvent:firstMove atStart:YES];
+ [button mouseDown:downEvent];
+ EXPECT_EQ(0U, [button dragCount]);
- [button setTarget:array.get()];
- [button setAction:@selector(removeAllObjects)];
- EXPECT_FALSE([[button cell] isHighlighted]);
+ // If the mouse moves > 5 pixels in either direciton
+ // it should cause a drag.
+ [NSApp postEvent:secondUpEvent atStart:YES];
+ [NSApp postEvent:secondMove atStart:YES];
+ [button mouseDown:downEvent];
+ EXPECT_EQ(1U, [button dragCount]);
+}
+
+TEST_F(DraggableButtonTest, ResetState) {
+ scoped_nsobject<TestableDraggableButton> button(
+ [[TestableDraggableButton alloc] initWithFrame:NSMakeRect(0,0,500,500)]);
+ [[test_window() contentView] addSubview:button.get()];
+ NSEvent* downEvent =
+ test_event_utils::MouseEventAtPoint(NSMakePoint(10,10),
+ NSLeftMouseDown,
+ 0);
+ NSEvent* moveEvent =
+ test_event_utils::MouseEventAtPoint(NSMakePoint(100,100),
+ NSLeftMouseDragged,
+ 0);
+ NSEvent* upEvent =
+ test_event_utils::MouseEventAtPoint(NSMakePoint(100,100),
+ NSLeftMouseUp,
+ 0);
+ // If the mouse moves > 5 pixels in either direciton it should cause a drag.
+ [NSApp postEvent:upEvent atStart:YES];
+ [NSApp postEvent:moveEvent atStart:YES];
+ [button mouseDown:downEvent];
- NSEvent* downEvent(Event(NSMakePoint(10,10), NSLeftMouseDown));
- NSEvent* upEvent(Event(NSMakePoint(10,10), NSLeftMouseDown));
+ // The button should not be highlighted after the drag finishes.
+ EXPECT_FALSE([[button cell] isHighlighted]);
+ EXPECT_EQ(1U, [button dragCount]);
+
+ // We should be able to initiate another drag immediately after the first one.
+ [NSApp postEvent:upEvent atStart:YES];
+ [NSApp postEvent:moveEvent atStart:YES];
[button mouseDown:downEvent];
- EXPECT_TRUE([[button cell] isHighlighted]);
- [button mouseUp:upEvent];
+ EXPECT_EQ(2U, [button dragCount]);
EXPECT_FALSE([[button cell] isHighlighted]);
- EXPECT_FALSE([array count]); // confirms target/action fired
}