blob: 10520d5c144677298d7bd11e7e75e9817a0dafa5 (
plain)
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
// 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 configureCell];
}
- (void)setOpenMenuOnClick:(BOOL)enabled {
openMenuOnClick_ = enabled;
[self configureCell];
}
- (void)setOpenMenuOnRightClick:(BOOL)enabled {
openMenuOnRightClick_ = enabled;
[self configureCell];
}
- (NSRect)menuRect {
return [self bounds];
}
@end // @implementation MenuButton
@implementation MenuButton (Private)
// Synchronize the state of this class with its ClickHoldButtonCell.
- (void)configureCell {
ClickHoldButtonCell* cell = [self cell];
DCHECK([cell isKindOfClass:[ClickHoldButtonCell class]]);
if (![self attachedMenu]) {
[cell setEnableClickHold:NO];
[cell setEnableRightClick:NO];
[cell setClickHoldAction:nil];
[cell setClickHoldTarget:nil];
[cell setAccessibilityShowMenuAction:nil];
[cell setAccessibilityShowMenuTarget:nil];
return;
}
if (openMenuOnClick_) {
[cell setClickHoldTimeout:0.0]; // Make menu trigger immediately.
[cell setAction:@selector(clickShowMenu:)];
[cell setTarget:self];
} else {
[cell setClickHoldTimeout:0.25]; // Default value.
}
// Even when openMenuOnClick_ is true, click hold action and target still
// need to be set in order to allow classic Mac menu behavior.
[cell setEnableClickHold:YES];
[cell setClickHoldAction:@selector(dragShowMenu:)];
[cell setClickHoldTarget:self];
[cell setEnableRightClick:openMenuOnRightClick_];
if (!openMenuOnClick_ || openMenuOnRightClick_) {
[cell setAccessibilityShowMenuAction:@selector(clickShowMenu:)];
[cell setAccessibilityShowMenuTarget:self];
} else {
[cell setAccessibilityShowMenuAction:nil];
[cell setAccessibilityShowMenuTarget: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 {
// We shouldn't get here unless the menu is enabled.
DCHECK([self attachedMenu]);
[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)
|