1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
// Copyright (c) 2011 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/ui/cocoa/menu_button.h"
#include "base/logging.h"
#import "chrome/browser/ui/cocoa/clickhold_button_cell.h"
#import "ui/base/cocoa/nsview_additions.h"
@interface MenuButton (Private)
- (void)showMenu:(BOOL)isDragging;
- (void)clickShowMenu:(id)sender;
- (void)dragShowMenu:(id)sender;
@end // @interface MenuButton (Private)
@implementation MenuButton
@synthesize openMenuOnClick = openMenuOnClick_;
@synthesize openMenuOnRightClick = openMenuOnRightClick_;
// Overrides:
+ (Class)cellClass {
return [ClickHoldButtonCell class];
}
- (id)init {
if ((self = [super init]))
[self configureCell];
return self;
}
- (id)initWithCoder:(NSCoder*)decoder {
if ((self = [super initWithCoder:decoder]))
[self configureCell];
return self;
}
- (id)initWithFrame:(NSRect)frameRect {
if ((self = [super initWithFrame:frameRect]))
[self configureCell];
return self;
}
- (void)dealloc {
self.attachedMenu = nil;
[super dealloc];
}
- (void)awakeFromNib {
[self configureCell];
}
- (void)setCell:(NSCell*)cell {
[super setCell:cell];
[self configureCell];
}
- (void)rightMouseDown:(NSEvent*)theEvent {
if (!openMenuOnRightClick_) {
[super rightMouseDown:theEvent];
return;
}
[self clickShowMenu:self];
}
// Accessors and mutators:
- (NSMenu*)attachedMenu {
return attachedMenu_.get();
}
- (void)setAttachedMenu:(NSMenu*)menu {
attachedMenu_.reset([menu retain]);
[[self cell] setEnableClickHold:(menu != nil)];
}
- (void)setOpenMenuOnClick:(BOOL)enabled {
openMenuOnClick_ = enabled;
if (enabled) {
[[self cell] setClickHoldTimeout:0.0]; // Make menu trigger immediately.
[[self cell] setAction:@selector(clickShowMenu:)];
[[self cell] setTarget:self];
} else {
[[self cell] setClickHoldTimeout:0.25]; // Default value.
}
}
- (void)setOpenMenuOnRightClick:(BOOL)enabled {
openMenuOnRightClick_ = enabled;
}
- (NSRect)menuRect {
return [self bounds];
}
@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)configureCell {
ClickHoldButtonCell* cell = [self cell];
DCHECK([cell isKindOfClass:[ClickHoldButtonCell class]]);
[cell setClickHoldAction:@selector(dragShowMenu:)];
[cell setClickHoldTarget:self];
[cell setEnableClickHold:([self attachedMenu] != nil)];
}
// Actually show the menu (in the correct location). |isDragging| indicates
// whether the mouse button is still down or not.
- (void)showMenu:(BOOL)isDragging {
if (![self attachedMenu]) {
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;
}
// TODO(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 menuRect];
frame.origin.x -= 2.0;
frame.size.height -= 19.0 - NSHeight(frame);
// 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).
// A |scoped_nsobject| local variable cannot be used here because
// Accessibility on 10.5 grabs the NSPopUpButtonCell without retaining it, and
// uses it later. (This is fixed in 10.6.)
if (!popUpCell_.get()) {
popUpCell_.reset([[NSPopUpButtonCell alloc] initTextCell:@""
pullsDown:YES]);
}
DCHECK(popUpCell_.get());
[popUpCell_ setMenu:[self attachedMenu]];
[popUpCell_ selectItem:nil];
[popUpCell_ attachPopUpWithFrame:frame inView:self];
[popUpCell_ performClickWithFrame:frame inView:self];
// Once the menu is dismissed send a mouseExited event if necessary. If the
// menu action caused the super view to resize then we won't automatically
// get a mouseExited event so we need to do this manually.
// See http://crbug.com/82456
if (![self cr_isMouseInView]) {
if ([[self cell] respondsToSelector:@selector(mouseExited:)])
[[self cell] mouseExited:nil];
}
}
// 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 {
// This should only be called if openMenuOnClick has been set (which hooks
// up this target-action).
DCHECK(openMenuOnClick_ || openMenuOnRightClick_);
[self showMenu:NO];
}
// Called when the button is clicked and dragged/held.
- (void)dragShowMenu:(id)sender {
// We shouldn't get here unless the menu is enabled.
DCHECK([self attachedMenu]);
[self showMenu:YES];
}
@end // @implementation MenuButton (Private)
|