diff options
author | viettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-03 20:42:24 +0000 |
---|---|---|
committer | viettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-03 20:42:24 +0000 |
commit | 1c977ab1081a4891419e40d6e4b1615c6e2583fb (patch) | |
tree | c82a78a053227719ae46739ef67f807ad095db4a /chrome/browser | |
parent | 588ac782287d7cbe82b71b825f268b8e0f026174 (diff) | |
download | chromium_src-1c977ab1081a4891419e40d6e4b1615c6e2583fb.zip chromium_src-1c977ab1081a4891419e40d6e4b1615c6e2583fb.tar.gz chromium_src-1c977ab1081a4891419e40d6e4b1615c6e2583fb.tar.bz2 |
(Mac) Make the Page/Wrench buttons place their menu in the right place.
Also makes the buttons drag-able -- you can drag or click and wait to
open their menu. Lucky for me, I wrote reusable code.
To do (not now): The "off-the-end" button on the bookmark bar needs the
same fix.
Bug (not to be fixed now): The menu doesn't do the right thing when the
window is moved off the left edge to the screen -- it doesn't open to
the right in the correct fashion. Nor does it do the proper thing when
you move the window off the bottom edge of the screen -- it doesn't open
upwards entirely correctly. <shrug>
BUG=18572
TEST=Click on the Page and Wrench menu buttons and observe. Also test \
dragging to open.
Review URL: http://codereview.chromium.org/174556
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@25362 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/cocoa/clickhold_button_cell.h | 5 | ||||
-rw-r--r-- | chrome/browser/cocoa/clickhold_button_cell.mm | 4 | ||||
-rw-r--r-- | chrome/browser/cocoa/menu_button.h | 24 | ||||
-rw-r--r-- | chrome/browser/cocoa/menu_button.mm | 118 | ||||
-rw-r--r-- | chrome/browser/cocoa/menu_button_unittest.mm | 64 | ||||
-rw-r--r-- | chrome/browser/cocoa/toolbar_controller.h | 15 | ||||
-rw-r--r-- | chrome/browser/cocoa/toolbar_controller.mm | 13 |
7 files changed, 216 insertions, 27 deletions
diff --git a/chrome/browser/cocoa/clickhold_button_cell.h b/chrome/browser/cocoa/clickhold_button_cell.h index 483336e..1982281 100644 --- a/chrome/browser/cocoa/clickhold_button_cell.h +++ b/chrome/browser/cocoa/clickhold_button_cell.h @@ -26,8 +26,9 @@ // Enable click-hold? Default: NO. @property(assign, nonatomic) BOOL enableClickHold; -// Timeout is in seconds (at least 0.01, at most 3600). Default: 0.25 (a guess -// at a Cocoa-ish value). +// Timeout is in seconds (at least 0.0, at most 5; 0.0 means that the button +// will always have its click-hold action activated immediately on press). +// Default: 0.25 (a guess at a Cocoa-ish value). @property(assign, nonatomic) NSTimeInterval clickHoldTimeout; // Track only in the frame rectangle? Default: NO. diff --git a/chrome/browser/cocoa/clickhold_button_cell.mm b/chrome/browser/cocoa/clickhold_button_cell.mm index 67b3df6..0dd5c3f 100644 --- a/chrome/browser/cocoa/clickhold_button_cell.mm +++ b/chrome/browser/cocoa/clickhold_button_cell.mm @@ -7,8 +7,8 @@ #include "base/logging.h" // Minimum and maximum click-hold timeout. -static const NSTimeInterval kMinTimeout = 0.01; -static const NSTimeInterval kMaxTimeout = 3600.0; +static const NSTimeInterval kMinTimeout = 0.0; +static const NSTimeInterval kMaxTimeout = 5.0; // Drag distance threshold to activate click-hold; should be >= 0. static const CGFloat kDragDistThreshold = 2.5; diff --git a/chrome/browser/cocoa/menu_button.h b/chrome/browser/cocoa/menu_button.h new file mode 100644 index 0000000..92b0569 --- /dev/null +++ b/chrome/browser/cocoa/menu_button.h @@ -0,0 +1,24 @@ +// 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. + +#ifndef CHROME_BROWSER_COCOA_MENU_BUTTON_H_ +#define CHROME_BROWSER_COCOA_MENU_BUTTON_H_ + +#import <Cocoa/Cocoa.h> + +// This a button which displays a user-provided menu "attached" below it upon +// being clicked or dragged (or clicked and held). It expects a +// |ClickHoldButtonCell| as cell. +@interface MenuButton : NSButton { + @private + IBOutlet NSMenu* menu_; + BOOL openAtRight_; +} + +// The menu to display. +@property(assign, nonatomic) NSMenu* menu; + +@end // @interface MenuButton + +#endif // CHROME_BROWSER_COCOA_MENU_BUTTON_H_ diff --git a/chrome/browser/cocoa/menu_button.mm b/chrome/browser/cocoa/menu_button.mm new file mode 100644 index 0000000..aed8034 --- /dev/null +++ b/chrome/browser/cocoa/menu_button.mm @@ -0,0 +1,118 @@ +// 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 "chrome/browser/cocoa/menu_button.h" + +#include "base/logging.h" +#include "base/scoped_nsobject.h" +#import "chrome/browser/cocoa/clickhold_button_cell.h" + +@interface MenuButton (Private) + +- (void)resetToDefaults; +- (void)showMenu:(BOOL)isDragging; +- (void)clickShowMenu:(id)sender; +- (void)dragShowMenu:(id)sender; + +@end // @interface MenuButton (Private) + +@implementation MenuButton + +// Overrides: + ++ (Class)cellClass { + return [ClickHoldButtonCell class]; +} + +- (id)init { + if ((self = [super init])) + [self resetToDefaults]; + return self; +} + +- (id)initWithCoder:(NSCoder*)decoder { + if ((self = [super initWithCoder:decoder])) + [self resetToDefaults]; + return self; +} + +- (id)initWithFrame:(NSRect)frameRect { + if ((self = [super initWithFrame:frameRect])) + [self resetToDefaults]; + return self; +} + +// Accessors and mutators: + +@synthesize menu = menu_; + +@end // @implementation MenuButton + +@implementation MenuButton (Private) + +// Reset various settings of the button and its associated |ClickHoldButtonCell| +// to the standard state which provides reasonable defaults. +- (void)resetToDefaults { + id cell = [self cell]; + DCHECK([cell isKindOfClass:[ClickHoldButtonCell class]]); + [cell setEnableClickHold:YES]; + [cell setClickHoldTimeout:0.0]; // Make menu trigger immediately. + [cell setAction:@selector(clickShowMenu:)]; + [cell setTarget:self]; + [cell setClickHoldAction:@selector(dragShowMenu:)]; + [cell setClickHoldTarget:self]; +} + +// Actually show the menu (in the correct location). |isDragging| indicates +// whether the mouse button is still down or not. +- (void)showMenu:(BOOL)isDragging { + if (!menu_) { + LOG(WARNING) << "No menu available."; + if (isDragging) { + // If we're dragging, wait for mouse up. + [NSApp nextEventMatchingMask:NSLeftMouseUpMask + untilDate:[NSDate distantFuture] + inMode:NSEventTrackingRunLoopMode + dequeue:YES]; + } + return; + } + + // FIXME(viettrungluu): Silly fudge factors (same as in + // delayedmenu_button.mm). + NSRect frame = [self convertRect:[self frame] + fromView:[self superview]]; + frame.origin.x -= 2.0; + frame.size.height += 10.0; + + // Make our pop-up button cell and set things up. This is, as of 10.5, the + // official Apple-recommended hack. Later, perhaps |-[NSMenu + // popUpMenuPositioningItem:atLocation:inView:]| may be a better option. + // However, using a pulldown has the benefit that Cocoa automatically places + // the menu correctly even when we're at the edge of the screen (including + // "dragging upwards" when the button is close to the bottom of the screen). + scoped_nsobject<NSPopUpButtonCell> popUpCell( + [[NSPopUpButtonCell alloc] initTextCell:@"" + pullsDown:YES]); + DCHECK(popUpCell.get()); + [popUpCell setMenu:menu_]; + [popUpCell selectItem:nil]; + [popUpCell attachPopUpWithFrame:frame + inView:self]; + [popUpCell performClickWithFrame:frame + inView:self]; +} + +// Called when the button is clicked and released. (Shouldn't happen with +// timeout of 0, though there may be some strange pointing devices out there.) +- (void)clickShowMenu:(id)sender { + [self showMenu:NO]; +} + +// Called when the button is clicked and dragged/held. +- (void)dragShowMenu:(id)sender { + [self showMenu:YES]; +} + +@end // @implementation MenuButton (Private) diff --git a/chrome/browser/cocoa/menu_button_unittest.mm b/chrome/browser/cocoa/menu_button_unittest.mm new file mode 100644 index 0000000..1861974 --- /dev/null +++ b/chrome/browser/cocoa/menu_button_unittest.mm @@ -0,0 +1,64 @@ +// 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/menu_button.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace { + +class MenuButtonTest : public PlatformTest { + public: + MenuButtonTest() { + NSRect frame = NSMakeRect(0, 0, 50, 30); + button_.reset([[MenuButton alloc] initWithFrame:frame]); + scoped_nsobject<ClickHoldButtonCell> cell( + [[ClickHoldButtonCell alloc] initTextCell:@"Testing"]); + [button_ setCell:cell.get()]; + [cocoa_helper_.contentView() addSubview:button_.get()]; + } + + scoped_nsobject<MenuButton> 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(MenuButtonTest, 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(MenuButtonTest, Display) { + [button_ display]; +} + +// Test assigning a menu, again mostly to ensure nothing leaks or crashes. +TEST_F(MenuButtonTest, 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]); + + // TODO(viettrungluu): Display the menu. (The tough part is closing the menu, + // not opening it!) + + // Since |button_| doesn't retain menu, we should probably unset it here. + [button_ setMenu:nil]; +} + +} // namespace diff --git a/chrome/browser/cocoa/toolbar_controller.h b/chrome/browser/cocoa/toolbar_controller.h index f5819a5..a442d88 100644 --- a/chrome/browser/cocoa/toolbar_controller.h +++ b/chrome/browser/cocoa/toolbar_controller.h @@ -18,8 +18,9 @@ class AutocompletePopupPositioner; @class AutocompleteTextField; @class AutocompleteTextFieldEditor; -@class DelayedMenuButton; @class BackForwardMenuController; +@class DelayedMenuButton; +@class MenuButton; class Browser; class CommandUpdater; class LocationBar; @@ -69,9 +70,6 @@ class ToolbarView; scoped_nsobject<NSTrackingArea> trackingArea_; NSButton* hoveredButton_; // weak. Button under the mouse cursor. - IBOutlet NSMenu* pageMenu_; - IBOutlet NSMenu* wrenchMenu_; - // The ordering is important for unit tests. If new items are added or the // ordering is changed, make sure to update |-toolbarViews| and the // corresponding enum in the unit tests. @@ -81,8 +79,8 @@ class ToolbarView; IBOutlet NSButton* homeButton_; IBOutlet NSButton* starButton_; IBOutlet NSButton* goButton_; - IBOutlet NSButton* pageButton_; - IBOutlet NSButton* wrenchButton_; + IBOutlet MenuButton* pageButton_; + IBOutlet MenuButton* wrenchButton_; IBOutlet AutocompleteTextField* locationBar_; } @@ -130,11 +128,6 @@ class ToolbarView; // Return the bookmark bar controller. - (BookmarkBarController*)bookmarkBarController; -// Actions for the optional menu buttons for the page and wrench menus. These -// will show a menu while the mouse is down. -- (IBAction)showPageMenu:(id)sender; -- (IBAction)showWrenchMenu:(id)sender; - // The bookmark bubble (when you click the star) needs to know where to go. // Somewhere near the star button seems like a good start. - (NSRect)starButtonInWindowCoordinates; diff --git a/chrome/browser/cocoa/toolbar_controller.mm b/chrome/browser/cocoa/toolbar_controller.mm index 5db38fb..4127615 100644 --- a/chrome/browser/cocoa/toolbar_controller.mm +++ b/chrome/browser/cocoa/toolbar_controller.mm @@ -14,6 +14,7 @@ #import "chrome/browser/cocoa/back_forward_menu_controller.h" #import "chrome/browser/cocoa/gradient_button_cell.h" #import "chrome/browser/cocoa/location_bar_view_mac.h" +#import "chrome/browser/cocoa/menu_button.h" #include "chrome/browser/cocoa/nsimage_cache.h" #include "chrome/browser/profile.h" #include "chrome/browser/toolbar_model.h" @@ -425,18 +426,6 @@ class PrefObserverBridge : public NotificationObserver { } } -- (IBAction)showPageMenu:(id)sender { - [NSMenu popUpContextMenu:pageMenu_ - withEvent:[NSApp currentEvent] - forView:pageButton_]; -} - -- (IBAction)showWrenchMenu:(id)sender { - [NSMenu popUpContextMenu:wrenchMenu_ - withEvent:[NSApp currentEvent] - forView:wrenchButton_]; -} - - (NSRect)starButtonInWindowCoordinates { return [[[starButton_ window] contentView] convertRect:[starButton_ bounds] fromView:starButton_]; |