diff options
-rw-r--r-- | chrome/browser/cocoa/clickhold_button_cell.h | 17 | ||||
-rw-r--r-- | chrome/browser/cocoa/clickhold_button_cell.mm | 61 | ||||
-rw-r--r-- | chrome/browser/cocoa/clickhold_button_cell_unittest.mm | 62 | ||||
-rw-r--r-- | chrome/browser/cocoa/delayedmenu_button.mm | 44 | ||||
-rw-r--r-- | chrome/browser/cocoa/delayedmenu_button_unittest.mm | 71 | ||||
-rw-r--r-- | chrome/chrome.gyp | 2 |
6 files changed, 221 insertions, 36 deletions
diff --git a/chrome/browser/cocoa/clickhold_button_cell.h b/chrome/browser/cocoa/clickhold_button_cell.h index 28dde69..483336e 100644 --- a/chrome/browser/cocoa/clickhold_button_cell.h +++ b/chrome/browser/cocoa/clickhold_button_cell.h @@ -10,10 +10,9 @@ #include "base/scoped_nsobject.h" #import "chrome/browser/cocoa/gradient_button_cell.h" -// A button cell that implements "click hold" behavior after a specified -// delay. If -setClickHoldTimeout: is never called, this behaves like a normal -// button. - +// A button cell that implements "click hold" behavior after a specified delay +// or after dragging. If click-hold is never enabled (e.g., if +// |-setEnableClickHold:| is never called), this behaves like a normal button. @interface ClickHoldButtonCell : GradientButtonCell { @private BOOL enableClickHold_; @@ -24,16 +23,18 @@ BOOL activateOnDrag_; } -// Enable click-hold? +// Enable click-hold? Default: NO. @property(assign, nonatomic) BOOL enableClickHold; -// Timeout is in seconds (at least 0.01, at most 3600). +// Timeout is in seconds (at least 0.01, at most 3600). Default: 0.25 (a guess +// at a Cocoa-ish value). @property(assign, nonatomic) NSTimeInterval clickHoldTimeout; -// Track only in the frame rectangle? +// Track only in the frame rectangle? Default: NO. @property(assign, nonatomic) BOOL trackOnlyInRect; -// Activate (click-hold) immediately on drag? +// Activate (click-hold) immediately on a sufficiently-large drag (if not, +// always wait for timeout)? Default: YES. @property(assign, nonatomic) BOOL activateOnDrag; // Defines what to do when click-held (as per usual action/target). diff --git a/chrome/browser/cocoa/clickhold_button_cell.mm b/chrome/browser/cocoa/clickhold_button_cell.mm index ce39d2a..67b3df6 100644 --- a/chrome/browser/cocoa/clickhold_button_cell.mm +++ b/chrome/browser/cocoa/clickhold_button_cell.mm @@ -13,6 +13,12 @@ static const NSTimeInterval kMaxTimeout = 3600.0; // Drag distance threshold to activate click-hold; should be >= 0. static const CGFloat kDragDistThreshold = 2.5; +// See |-resetToDefaults| (and header file) for other default values. + +@interface ClickHoldButtonCell (Private) +- (void)resetToDefaults; +@end // @interface ClickHoldButtonCell (Private) + @implementation ClickHoldButtonCell // Overrides: @@ -21,22 +27,44 @@ static const CGFloat kDragDistThreshold = 2.5; return NO; } +- (id)init { + if ((self = [super init])) + [self resetToDefaults]; + return self; +} + +- (id)initWithCoder:(NSCoder*)decoder { + if ((self = [super initWithCoder:decoder])) + [self resetToDefaults]; + return self; +} + +- (id)initImageCell:(NSImage*)image { + if ((self = [super initImageCell:image])) + [self resetToDefaults]; + return self; +} + +- (id)initTextCell:(NSString*)string { + if ((self = [super initTextCell:string])) + [self resetToDefaults]; + return self; +} + - (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView*)controlView { - return enableClickHold_ ? - YES : - [super startTrackingAt:startPoint - inView:controlView]; + return enableClickHold_ ? YES : + [super startTrackingAt:startPoint + inView:controlView]; } - (BOOL)continueTracking:(NSPoint)lastPoint at:(NSPoint)currentPoint inView:(NSView*)controlView { - return enableClickHold_ ? - YES : - [super continueTracking:lastPoint - at:currentPoint - inView:controlView]; + return enableClickHold_ ? YES : + [super continueTracking:lastPoint + at:currentPoint + inView:controlView]; } - (BOOL)trackMouse:(NSEvent*)originalEvent @@ -145,3 +173,18 @@ static const CGFloat kDragDistThreshold = 2.5; @synthesize clickHoldAction = clickHoldAction_; @end // @implementation ClickHoldButtonCell + +@implementation ClickHoldButtonCell (Private) + +// Resets various members to defaults indicated in the header file. (Those +// without indicated defaults are *not* touched.) Please keep the values below +// in sync with the header file, and please be aware of side-effects on code +// which relies on the "published" defaults. +- (void)resetToDefaults { + [self setEnableClickHold:NO]; + [self setClickHoldTimeout:0.25]; + [self setTrackOnlyInRect:NO]; + [self setActivateOnDrag:YES]; +} + +@end // @implementation ClickHoldButtonCell (Private) diff --git a/chrome/browser/cocoa/clickhold_button_cell_unittest.mm b/chrome/browser/cocoa/clickhold_button_cell_unittest.mm new file mode 100644 index 0000000..80edfb4 --- /dev/null +++ b/chrome/browser/cocoa/clickhold_button_cell_unittest.mm @@ -0,0 +1,62 @@ +// Copyright (c) 2009 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 <Cocoa/Cocoa.h> + +#include "base/scoped_nsobject.h" +#import "chrome/browser/cocoa/clickhold_button_cell.h" +#import "chrome/browser/cocoa/cocoa_test_helper.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace { + +class ClickHoldButtonCellTest : public PlatformTest { + public: + ClickHoldButtonCellTest() { + NSRect frame = NSMakeRect(0, 0, 50, 30); + view_.reset([[NSButton alloc] initWithFrame:frame]); + scoped_nsobject<ClickHoldButtonCell> cell( + [[ClickHoldButtonCell alloc] initTextCell:@"Testing"]); + [view_ setCell:cell.get()]; + [cocoa_helper_.contentView() addSubview:view_.get()]; + } + + CocoaTestHelper cocoa_helper_; // Inits Cocoa, creates window, etc. + scoped_nsobject<NSButton> view_; +}; + +// Test adding/removing from the view hierarchy, mostly to ensure nothing leaks +// or crashes. +TEST_F(ClickHoldButtonCellTest, AddRemove) { + EXPECT_EQ(cocoa_helper_.contentView(), [view_ superview]); + [view_.get() removeFromSuperview]; + EXPECT_FALSE([view_ superview]); +} + +// Test drawing, mostly to ensure nothing leaks or crashes. +TEST_F(ClickHoldButtonCellTest, Display) { + [view_ display]; +} + +// Test default values; make sure they are what they should be. +TEST_F(ClickHoldButtonCellTest, Defaults) { + ClickHoldButtonCell* cell = static_cast<ClickHoldButtonCell*>([view_ cell]); + ASSERT_TRUE([cell isKindOfClass:[ClickHoldButtonCell class]]); + + EXPECT_FALSE([cell enableClickHold]); + + NSTimeInterval clickHoldTimeout = [cell clickHoldTimeout]; + EXPECT_GE(clickHoldTimeout, 0.15); // Check for a "Cocoa-ish" value. + EXPECT_LE(clickHoldTimeout, 0.35); + + EXPECT_FALSE([cell trackOnlyInRect]); + EXPECT_TRUE([cell activateOnDrag]); +} + +// TODO(viettrungluu): (1) Enable click-hold and figure out how to test the +// tracking loop (i.e., |-trackMouse:...|), which is the nontrivial part. +// (2) Test various initialization code paths (in particular, loading from nib). + +} // namespace diff --git a/chrome/browser/cocoa/delayedmenu_button.mm b/chrome/browser/cocoa/delayedmenu_button.mm index 5269730..2be6928 100644 --- a/chrome/browser/cocoa/delayedmenu_button.mm +++ b/chrome/browser/cocoa/delayedmenu_button.mm @@ -10,7 +10,7 @@ @interface DelayedMenuButton (Private) -- (void)resetToDefaults; +- (void)setupCell; - (void)menuAction:(id)sender; @end // @interface DelayedMenuButton (Private) @@ -25,19 +25,19 @@ - (id)init { if ((self = [super init])) - [self resetToDefaults]; + [self setupCell]; return self; } - (id)initWithCoder:(NSCoder*)decoder { if ((self = [super initWithCoder:decoder])) - [self resetToDefaults]; + [self setupCell]; return self; } - (id)initWithFrame:(NSRect)frameRect { if ((self = [super initWithFrame:frameRect])) - [self resetToDefaults]; + [self setupCell]; return self; } @@ -47,7 +47,12 @@ } - (void)awakeFromNib { - [self resetToDefaults]; + [self setupCell]; +} + +- (void)setCell:(NSCell*)cell { + [super setCell:cell]; + [self setupCell]; } // Accessors and mutators: @@ -68,19 +73,20 @@ @implementation DelayedMenuButton (Private) -- (void)resetToDefaults { - id cell = [self cell]; - DCHECK([cell isKindOfClass:[ClickHoldButtonCell class]]); - [self setEnabled:NO]; // Make the controller put in a menu and +// Set up the button's cell if we've reached a point where it's been set. +- (void)setupCell { + ClickHoldButtonCell* cell = [self cell]; + if (cell) { + DCHECK([cell isKindOfClass:[ClickHoldButtonCell class]]); + [self setEnabled:NO]; // Make the controller put in a menu and // enable it explicitly. This also takes // care of |[cell setEnableClickHold:]|. - [cell setClickHoldTimeout:0.25]; // Random guess at Cocoa-ish value. - [cell setTrackOnlyInRect:NO]; - [cell setActivateOnDrag:YES]; - [cell setClickHoldAction:@selector(menuAction:)]; - [cell setClickHoldTarget:self]; + [cell setClickHoldAction:@selector(menuAction:)]; + [cell setClickHoldTarget:self]; + } } +// Display the menu. - (void)menuAction:(id)sender { // We shouldn't get here unless the menu is enabled. DCHECK(menuEnabled_); @@ -96,11 +102,11 @@ return; } - // FIXME(viettrungluu@gmail.com): We have some fudge factors below to make - // things line up (approximately). I wish I knew how to get rid of them. (Note - // that our view is flipped, and that frame should be in our coordinates.) - // The y/height is very odd, since it doesn't seem to respond to changes the - // way that it should. I don't understand it. + // FIXME(viettrungluu): We have some fudge factors below to make things line + // up (approximately). I wish I knew how to get rid of them. (Note that our + // view is flipped, and that frame should be in our coordinates.) The y/height + // is very odd, since it doesn't seem to respond to changes the way that it + // should. I don't understand it. NSRect frame = [self convertRect:[self frame] fromView:[self superview]]; frame.origin.x -= 2.0; diff --git a/chrome/browser/cocoa/delayedmenu_button_unittest.mm b/chrome/browser/cocoa/delayedmenu_button_unittest.mm new file mode 100644 index 0000000..b04cb27 --- /dev/null +++ b/chrome/browser/cocoa/delayedmenu_button_unittest.mm @@ -0,0 +1,71 @@ +// Copyright (c) 2009 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 <Cocoa/Cocoa.h> + +#include "base/scoped_nsobject.h" +#import "chrome/browser/cocoa/clickhold_button_cell.h" +#import "chrome/browser/cocoa/cocoa_test_helper.h" +#import "chrome/browser/cocoa/delayedmenu_button.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace { + +class DelayedMenuButtonTest : public PlatformTest { + public: + DelayedMenuButtonTest() { + NSRect frame = NSMakeRect(0, 0, 50, 30); + button_.reset([[DelayedMenuButton alloc] initWithFrame:frame]); + scoped_nsobject<ClickHoldButtonCell> cell( + [[ClickHoldButtonCell alloc] initTextCell:@"Testing"]); + [button_ setCell:cell.get()]; + [cocoa_helper_.contentView() addSubview:button_.get()]; + } + + scoped_nsobject<DelayedMenuButton> button_; + CocoaTestHelper cocoa_helper_; // Inits Cocoa, creates window, etc. +}; + +// Test adding/removing from the view hierarchy, mostly to ensure nothing leaks +// or crashes. +TEST_F(DelayedMenuButtonTest, AddRemove) { + EXPECT_EQ(cocoa_helper_.contentView(), [button_ superview]); + [button_.get() removeFromSuperview]; + EXPECT_FALSE([button_ superview]); +} + +// Test drawing, mostly to ensure nothing leaks or crashes. +TEST_F(DelayedMenuButtonTest, Display) { + [button_ display]; +} + +// Test assigning and enabling a menu, again mostly to ensure nothing leaks or +// crashes. +TEST_F(DelayedMenuButtonTest, MenuAssign) { + scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@""]); + ASSERT_TRUE(menu.get()); + + [menu insertItemWithTitle:@"" action:nil keyEquivalent:@"" atIndex:0]; + [menu insertItemWithTitle:@"foo" action:nil keyEquivalent:@"" atIndex:1]; + [menu insertItemWithTitle:@"bar" action:nil keyEquivalent:@"" atIndex:2]; + [menu insertItemWithTitle:@"baz" action:nil keyEquivalent:@"" atIndex:3]; + + [button_ setMenu:menu]; + EXPECT_TRUE([button_ menu]); + + [button_ setMenuEnabled:YES]; + EXPECT_TRUE([button_ menuEnabled]); + + // TODO(viettrungluu): Display the menu. (Calling DelayedMenuButton's private + // |-menuAction:| method displays it fine, but the problem is getting rid of + // the menu. We can catch the |NSMenuDidBeginTrackingNotification| from |menu| + // fine, but then |-cancelTracking| doesn't dismiss it. I don't know why.) +} + +// TODO(viettrungluu): Test the two actions of the button (the normal one and +// displaying the menu, also making sure the latter drags correctly)? It would +// require "emulating" a mouse.... + +} // namespace diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index b2becc4..e4bbe6f 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -3972,8 +3972,10 @@ 'browser/cocoa/browser_window_cocoa_unittest.mm', 'browser/cocoa/browser_window_controller_unittest.mm', 'browser/cocoa/clear_browsing_data_controller_unittest.mm', + 'browser/cocoa/clickhold_button_cell_unittest.mm', 'browser/cocoa/command_observer_bridge_unittest.mm', 'browser/cocoa/custom_home_pages_model_unittest.mm', + 'browser/cocoa/delayedmenu_button_unittest.mm', 'browser/cocoa/download_shelf_mac_unittest.mm', 'browser/cocoa/download_shelf_view_unittest.mm', 'browser/cocoa/download_util_mac_unittest.mm', |