OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "webkit/glue/webmenurunner_mac.h" | 5 #include "webkit/glue/webmenurunner_mac.h" |
6 | 6 |
7 #include "base/sys_string_conversions.h" | 7 #include "base/sys_string_conversions.h" |
8 | 8 |
9 namespace { | |
10 | |
11 const CGFloat kPopupXOffset = -10.0f; | |
12 BOOL gNewNSMenuAPI; | |
13 | |
14 } // namespace | |
15 | |
16 #if !defined(MAC_OS_X_VERSION_10_6) || \ | 9 #if !defined(MAC_OS_X_VERSION_10_6) || \ |
17 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 | 10 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 |
18 @interface NSMenu (SnowLeopardSDKDeclarations) | 11 enum { |
19 - (BOOL)popUpMenuPositioningItem:(NSMenuItem *)item | 12 NSUserInterfaceLayoutDirectionLeftToRight = 0, |
20 atLocation:(NSPoint)location | 13 NSUserInterfaceLayoutDirectionRightToLeft = 1 |
21 inView:(NSView *)view; | 14 }; |
22 - (void)setFont:(NSFont *)font; | 15 typedef NSInteger NSUserInterfaceLayoutDirection; |
| 16 |
| 17 @interface NSCell (SnowLeopardSDKDeclarations) |
| 18 - (void)setUserInterfaceLayoutDirection: |
| 19 (NSUserInterfaceLayoutDirection)layoutDirection; |
23 @end | 20 @end |
| 21 |
| 22 enum { |
| 23 NSTextWritingDirectionEmbedding = (0 << 1), |
| 24 NSTextWritingDirectionOverride = (1 << 1) |
| 25 }; |
| 26 |
| 27 static NSString* NSWritingDirectionAttributeName = @"NSWritingDirection"; |
24 #endif | 28 #endif |
25 | 29 |
26 @interface WebMenuRunner (PrivateAPI) | 30 @interface WebMenuRunner (PrivateAPI) |
27 | 31 |
28 // Worker function used during initialization. | 32 // Worker function used during initialization. |
29 - (void)addItem:(const WebMenuItem&)item | 33 - (void)addItem:(const WebMenuItem&)item; |
30 withAttributes:(NSDictionary*)attrs; | |
31 | 34 |
32 // A callback for the menu controller object to call when an item is selected | 35 // A callback for the menu controller object to call when an item is selected |
33 // from the menu. This is not called if the menu is dismissed without a | 36 // from the menu. This is not called if the menu is dismissed without a |
34 // selection. | 37 // selection. |
35 - (void)menuItemSelected:(id)sender; | 38 - (void)menuItemSelected:(id)sender; |
36 | 39 |
37 @end // WebMenuRunner (PrivateAPI) | 40 @end // WebMenuRunner (PrivateAPI) |
38 | 41 |
39 @implementation WebMenuRunner | 42 @implementation WebMenuRunner |
40 | 43 |
41 - (id)initWithItems:(const std::vector<WebMenuItem>&)items | 44 - (id)initWithItems:(const std::vector<WebMenuItem>&)items |
42 fontSize:(CGFloat)fontSize | 45 fontSize:(CGFloat)fontSize |
43 rightAligned:(BOOL)rightAligned { | 46 rightAligned:(BOOL)rightAligned { |
44 static BOOL newNSMenuAPIInitialized = NO; | |
45 if (!newNSMenuAPIInitialized) { | |
46 newNSMenuAPIInitialized = YES; | |
47 gNewNSMenuAPI = [NSMenu instancesRespondToSelector: | |
48 @selector(popUpMenuPositioningItem:atLocation:inView:)] && | |
49 [NSMenu instancesRespondToSelector:@selector(setFont:)]; | |
50 } | |
51 | |
52 if ((self = [super init])) { | 47 if ((self = [super init])) { |
53 menu_.reset([[NSMenu alloc] initWithTitle:@""]); | 48 menu_.reset([[NSMenu alloc] initWithTitle:@""]); |
54 if (gNewNSMenuAPI) | |
55 [menu_ setFont:[NSFont menuFontOfSize:fontSize]]; | |
56 [menu_ setAutoenablesItems:NO]; | 49 [menu_ setAutoenablesItems:NO]; |
57 index_ = -1; | 50 index_ = -1; |
58 fontSize_ = fontSize; | 51 fontSize_ = fontSize; |
59 scoped_nsobject<NSDictionary> attrs; | 52 rightAligned_ = rightAligned; |
60 if (rightAligned) { | |
61 // NB: Right-aligning menu items in this manner is known to not work in | |
62 // Mac OS X 10.5. | |
63 scoped_nsobject<NSMutableParagraphStyle> paragraphStyle( | |
64 [[NSMutableParagraphStyle alloc] init]); | |
65 [paragraphStyle setAlignment:NSRightTextAlignment]; | |
66 attrs.reset([[NSDictionary alloc] initWithObjectsAndKeys: | |
67 paragraphStyle, NSParagraphStyleAttributeName, nil]); | |
68 } | |
69 for (size_t i = 0; i < items.size(); ++i) | 53 for (size_t i = 0; i < items.size(); ++i) |
70 [self addItem:items[i] withAttributes:attrs]; | 54 [self addItem:items[i]]; |
71 } | 55 } |
72 return self; | 56 return self; |
73 } | 57 } |
74 | 58 |
75 - (void)addItem:(const WebMenuItem&)item | 59 - (void)addItem:(const WebMenuItem&)item { |
76 withAttributes:(NSDictionary*)attrs { | |
77 if (item.type == WebMenuItem::SEPARATOR) { | 60 if (item.type == WebMenuItem::SEPARATOR) { |
78 [menu_ addItem:[NSMenuItem separatorItem]]; | 61 [menu_ addItem:[NSMenuItem separatorItem]]; |
79 return; | 62 return; |
80 } | 63 } |
81 | 64 |
82 NSString* title = base::SysUTF16ToNSString(item.label); | 65 NSString* title = base::SysUTF16ToNSString(item.label); |
83 NSMenuItem* menuItem = [menu_ addItemWithTitle:title | 66 NSMenuItem* menuItem = [menu_ addItemWithTitle:title |
84 action:@selector(menuItemSelected:) | 67 action:@selector(menuItemSelected:) |
85 keyEquivalent:@""]; | 68 keyEquivalent:@""]; |
86 [menuItem setEnabled:(item.enabled && item.type != WebMenuItem::GROUP)]; | 69 [menuItem setEnabled:(item.enabled && item.type != WebMenuItem::GROUP)]; |
87 [menuItem setTarget:self]; | 70 [menuItem setTarget:self]; |
88 if (attrs) { | 71 |
89 scoped_nsobject<NSAttributedString> attrTitle( | 72 // Set various alignment/language attributes. Note that many (if not most) of |
90 [[NSAttributedString alloc] initWithString:title | 73 // these attributes are functional only on 10.6 and above. |
91 attributes:attrs]); | 74 scoped_nsobject<NSMutableDictionary> attrs( |
92 [menuItem setAttributedTitle:attrTitle]; | 75 [[NSMutableDictionary alloc] initWithCapacity:3]); |
| 76 scoped_nsobject<NSMutableParagraphStyle> paragraphStyle( |
| 77 [[NSMutableParagraphStyle alloc] init]); |
| 78 [paragraphStyle setAlignment:rightAligned_ ? NSRightTextAlignment |
| 79 : NSLeftTextAlignment]; |
| 80 NSWritingDirection writingDirection = |
| 81 item.rtl ? NSWritingDirectionRightToLeft |
| 82 : NSWritingDirectionLeftToRight; |
| 83 [paragraphStyle setBaseWritingDirection:writingDirection]; |
| 84 [attrs setObject:paragraphStyle forKey:NSParagraphStyleAttributeName]; |
| 85 |
| 86 if (item.directional_override) { |
| 87 scoped_nsobject<NSNumber> directionValue( |
| 88 [[NSNumber alloc] initWithInteger: |
| 89 writingDirection + NSTextWritingDirectionOverride]); |
| 90 scoped_nsobject<NSArray> directionArray( |
| 91 [[NSArray alloc] initWithObjects:directionValue.get(), nil]); |
| 92 [attrs setObject:directionArray forKey:NSWritingDirectionAttributeName]; |
93 } | 93 } |
| 94 |
| 95 [attrs setObject:[NSFont menuFontOfSize:fontSize_] |
| 96 forKey:NSFontAttributeName]; |
| 97 |
| 98 scoped_nsobject<NSAttributedString> attrTitle( |
| 99 [[NSAttributedString alloc] initWithString:title |
| 100 attributes:attrs]); |
| 101 [menuItem setAttributedTitle:attrTitle]; |
| 102 |
94 [menuItem setTag:[menu_ numberOfItems] - 1]; | 103 [menuItem setTag:[menu_ numberOfItems] - 1]; |
95 } | 104 } |
96 | 105 |
97 // Reflects the result of the user's interaction with the popup menu. If NO, the | 106 // Reflects the result of the user's interaction with the popup menu. If NO, the |
98 // menu was dismissed without the user choosing an item, which can happen if the | 107 // menu was dismissed without the user choosing an item, which can happen if the |
99 // user clicked outside the menu region or hit the escape key. If YES, the user | 108 // user clicked outside the menu region or hit the escape key. If YES, the user |
100 // selected an item from the menu. | 109 // selected an item from the menu. |
101 - (BOOL)menuItemWasChosen { | 110 - (BOOL)menuItemWasChosen { |
102 return menuItemWasChosen_; | 111 return menuItemWasChosen_; |
103 } | 112 } |
104 | 113 |
105 - (void)menuItemSelected:(id)sender { | 114 - (void)menuItemSelected:(id)sender { |
106 menuItemWasChosen_ = YES; | 115 menuItemWasChosen_ = YES; |
107 if (gNewNSMenuAPI) | |
108 index_ = [sender tag]; | |
109 } | 116 } |
110 | 117 |
111 - (void)runMenuInView:(NSView*)view | 118 - (void)runMenuInView:(NSView*)view |
112 withBounds:(NSRect)bounds | 119 withBounds:(NSRect)bounds |
113 initialIndex:(int)index { | 120 initialIndex:(int)index { |
114 if (gNewNSMenuAPI) { | 121 // Set up the button cell, converting to NSView coordinates. The menu is |
115 // index might be out of bounds, in which case we set no selection. | 122 // positioned such that the currently selected menu item appears over the |
116 NSMenuItem* selectedItem = [menu_ itemWithTag:index]; | 123 // popup button, which is the expected Mac popup menu behavior. |
117 if (selectedItem) { | 124 NSPopUpButtonCell* button = [[NSPopUpButtonCell alloc] initTextCell:@"" |
118 [selectedItem setState:NSOnState]; | 125 pullsDown:NO]; |
119 } else { | 126 [button autorelease]; |
120 selectedItem = [menu_ itemWithTag:0]; | 127 [button setMenu:menu_]; |
121 } | 128 // We use selectItemWithTag below so if the index is out-of-bounds nothing |
122 NSPoint anchor = NSMakePoint(NSMinX(bounds) + kPopupXOffset, | 129 // bad happens. |
123 NSMaxY(bounds)); | 130 [button selectItemWithTag:index]; |
124 [menu_ popUpMenuPositioningItem:selectedItem | |
125 atLocation:anchor | |
126 inView:view]; | |
127 } else { | |
128 // Set up the button cell, converting to NSView coordinates. The menu is | |
129 // positioned such that the currently selected menu item appears over the | |
130 // popup button, which is the expected Mac popup menu behavior. | |
131 NSPopUpButtonCell* button = [[NSPopUpButtonCell alloc] initTextCell:@"" | |
132 pullsDown:NO]; | |
133 [button autorelease]; | |
134 [button setMenu:menu_]; | |
135 // We use selectItemWithTag below so if the index is out-of-bounds nothing | |
136 // bad happens. | |
137 [button selectItemWithTag:index]; | |
138 [button setFont:[NSFont menuFontOfSize:fontSize_]]; | |
139 | 131 |
140 // Create a dummy view to associate the popup with, since the OS will use | 132 if (rightAligned_ && |
141 // that view for positioning the menu. | 133 [button respondsToSelector:@selector(setUserInterfaceLayoutDirection:)]) { |
142 NSView* dummyView = [[[NSView alloc] initWithFrame:bounds] autorelease]; | 134 [button setUserInterfaceLayoutDirection: |
143 [view addSubview:dummyView]; | 135 NSUserInterfaceLayoutDirectionRightToLeft]; |
144 NSRect dummyBounds = [dummyView convertRect:bounds fromView:view]; | 136 } |
145 | 137 |
146 // Display the menu, and set a flag if a menu item was chosen. | 138 // Create a dummy view to associate the popup with, since the OS will use |
147 [button performClickWithFrame:dummyBounds inView:dummyView]; | 139 // that view for positioning the menu. |
| 140 NSView* dummyView = [[[NSView alloc] initWithFrame:bounds] autorelease]; |
| 141 [view addSubview:dummyView]; |
| 142 NSRect dummyBounds = [dummyView convertRect:bounds fromView:view]; |
148 | 143 |
149 if ([self menuItemWasChosen]) | 144 // Display the menu, and set a flag if a menu item was chosen. |
150 index_ = [button indexOfSelectedItem]; | 145 [button performClickWithFrame:dummyBounds inView:dummyView]; |
151 | 146 |
152 [dummyView removeFromSuperview]; | 147 if ([self menuItemWasChosen]) |
153 } | 148 index_ = [button indexOfSelectedItem]; |
| 149 |
| 150 [dummyView removeFromSuperview]; |
154 } | 151 } |
155 | 152 |
156 - (int)indexOfSelectedItem { | 153 - (int)indexOfSelectedItem { |
157 return index_; | 154 return index_; |
158 } | 155 } |
159 | 156 |
160 @end // WebMenuRunner | 157 @end // WebMenuRunner |
161 | 158 |
162 namespace webkit_glue { | 159 namespace webkit_glue { |
163 | 160 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
209 characters:@"" | 206 characters:@"" |
210 charactersIgnoringModifiers:escape_str | 207 charactersIgnoringModifiers:escape_str |
211 isARepeat:NO | 208 isARepeat:NO |
212 keyCode:0x1B]; | 209 keyCode:0x1B]; |
213 } | 210 } |
214 | 211 |
215 return event; | 212 return event; |
216 } | 213 } |
217 | 214 |
218 } // namespace webkit_glue | 215 } // namespace webkit_glue |
OLD | NEW |