2 * Copyright (C) 2010, 2011 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #import "WebPopupMenuProxyMac.h"
31 #import "NativeWebMouseEvent.h"
32 #import "PageClientImpl.h"
33 #import "PlatformPopupMenuData.h"
34 #import "StringUtilities.h"
36 #import "WebPopupItem.h"
37 #import <WebKitSystemInterface.h>
39 using namespace WebCore;
43 WebPopupMenuProxyMac::WebPopupMenuProxyMac(WKView *webView, WebPopupMenuProxy::Client* client)
44 : WebPopupMenuProxy(client)
49 WebPopupMenuProxyMac::~WebPopupMenuProxyMac()
52 [m_popup setControlView:nil];
55 void WebPopupMenuProxyMac::populate(const Vector<WebPopupItem>& items, NSFont *font, TextDirection menuTextDirection)
58 [m_popup removeAllItems];
60 m_popup = adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]);
61 [m_popup setUsesItemFromMenu:NO];
62 [m_popup setAutoenablesItems:NO];
65 int size = items.size();
67 for (int i = 0; i < size; i++) {
68 if (items[i].m_type == WebPopupItem::Separator)
69 [[m_popup menu] addItem:[NSMenuItem separatorItem]];
71 [m_popup addItemWithTitle:@""];
72 NSMenuItem *menuItem = [m_popup lastItem];
74 RetainPtr<NSMutableParagraphStyle> paragraphStyle = adoptNS([[NSParagraphStyle defaultParagraphStyle] mutableCopy]);
75 NSWritingDirection writingDirection = items[i].m_textDirection == LTR ? NSWritingDirectionLeftToRight : NSWritingDirectionRightToLeft;
76 [paragraphStyle setBaseWritingDirection:writingDirection];
77 [paragraphStyle setAlignment:menuTextDirection == LTR ? NSLeftTextAlignment : NSRightTextAlignment];
78 RetainPtr<NSMutableDictionary> attributes = adoptNS([[NSMutableDictionary alloc] initWithObjectsAndKeys:
79 paragraphStyle.get(), NSParagraphStyleAttributeName,
80 font, NSFontAttributeName,
82 if (items[i].m_hasTextDirectionOverride) {
83 RetainPtr<NSNumber> writingDirectionValue = adoptNS([[NSNumber alloc] initWithInteger:writingDirection + NSTextWritingDirectionOverride]);
84 RetainPtr<NSArray> writingDirectionArray = adoptNS([[NSArray alloc] initWithObjects:writingDirectionValue.get(), nil]);
85 [attributes setObject:writingDirectionArray.get() forKey:NSWritingDirectionAttributeName];
87 RetainPtr<NSAttributedString> string = adoptNS([[NSAttributedString alloc] initWithString:nsStringFromWebCoreString(items[i].m_text) attributes:attributes.get()]);
89 [menuItem setAttributedTitle:string.get()];
90 // We set the title as well as the attributed title here. The attributed title will be displayed in the menu,
91 // but typeahead will use the non-attributed string that doesn't contain any leading or trailing whitespace.
92 [menuItem setTitle:[[string string] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];
93 [menuItem setEnabled:items[i].m_isEnabled];
94 [menuItem setToolTip:nsStringFromWebCoreString(items[i].m_toolTip)];
99 void WebPopupMenuProxyMac::showPopupMenu(const IntRect& rect, TextDirection textDirection, double pageScaleFactor, const Vector<WebPopupItem>& items, const PlatformPopupMenuData& data, int32_t selectedIndex)
102 if (data.fontInfo.fontAttributeDictionary) {
103 NSFontDescriptor *fontDescriptor = [NSFontDescriptor fontDescriptorWithFontAttributes:(NSDictionary *)data.fontInfo.fontAttributeDictionary.get()];
104 font = [NSFont fontWithDescriptor:fontDescriptor size:((pageScaleFactor != 1) ? [fontDescriptor pointSize] * pageScaleFactor : 0)];
106 font = [NSFont menuFontOfSize:0];
108 populate(items, font, textDirection);
110 [m_popup attachPopUpWithFrame:rect inView:m_webView];
111 [m_popup selectItemAtIndex:selectedIndex];
112 [m_popup setUserInterfaceLayoutDirection:textDirection == LTR ? NSUserInterfaceLayoutDirectionLeftToRight : NSUserInterfaceLayoutDirectionRightToLeft];
114 NSMenu *menu = [m_popup menu];
116 // These values were borrowed from AppKit to match their placement of the menu.
117 const int popOverHorizontalAdjust = -10;
118 const int popUnderHorizontalAdjust = 6;
119 const int popUnderVerticalAdjust = 6;
121 // Menus that pop-over directly obscure the node that generated the popup menu.
122 // Menus that pop-under are offset underneath it.
124 if (data.shouldPopOver) {
125 NSRect titleFrame = [m_popup titleRectForBounds:rect];
126 if (titleFrame.size.width <= 0 || titleFrame.size.height <= 0)
128 float vertOffset = roundf((NSMaxY(rect) - NSMaxY(titleFrame)) + NSHeight(titleFrame));
129 location = NSMakePoint(NSMinX(rect) + popOverHorizontalAdjust, NSMaxY(rect) - vertOffset);
131 location = NSMakePoint(NSMinX(rect) + popUnderHorizontalAdjust, NSMaxY(rect) + popUnderVerticalAdjust);
133 RetainPtr<NSView> dummyView = adoptNS([[NSView alloc] initWithFrame:rect]);
134 [m_webView addSubview:dummyView.get()];
135 location = [dummyView convertPoint:location fromView:m_webView];
137 NSControlSize controlSize;
138 switch (data.menuSize) {
139 case WebCore::PopupMenuStyle::PopupMenuSizeNormal:
140 controlSize = NSRegularControlSize;
142 case WebCore::PopupMenuStyle::PopupMenuSizeSmall:
143 controlSize = NSSmallControlSize;
145 case WebCore::PopupMenuStyle::PopupMenuSizeMini:
146 controlSize = NSMiniControlSize;
150 WKPopupMenuWithSize(menu, location, roundf(NSWidth(rect)), dummyView.get(), selectedIndex, font, controlSize);
152 [m_popup dismissPopUp];
153 [dummyView removeFromSuperview];
158 m_client->valueChangedForPopupMenu(this, [m_popup indexOfSelectedItem]);
160 // <https://bugs.webkit.org/show_bug.cgi?id=57904> This code is adopted from EventHandler::sendFakeEventsAfterWidgetTracking().
161 if (!m_client->currentlyProcessedMouseDownEvent())
164 NSEvent* initiatingNSEvent = m_client->currentlyProcessedMouseDownEvent()->nativeEvent();
165 if ([initiatingNSEvent type] != NSLeftMouseDown)
168 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
169 location:[initiatingNSEvent locationInWindow]
170 modifierFlags:[initiatingNSEvent modifierFlags]
171 timestamp:[initiatingNSEvent timestamp]
172 windowNumber:[initiatingNSEvent windowNumber]
173 context:[initiatingNSEvent context]
174 eventNumber:[initiatingNSEvent eventNumber]
175 clickCount:[initiatingNSEvent clickCount]
176 pressure:[initiatingNSEvent pressure]];
178 [NSApp postEvent:fakeEvent atStart:YES];
179 #pragma clang diagnostic push
180 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
181 fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
182 location:[[m_webView window] convertScreenToBase:[NSEvent mouseLocation]]
183 modifierFlags:[initiatingNSEvent modifierFlags]
184 timestamp:[initiatingNSEvent timestamp]
185 windowNumber:[initiatingNSEvent windowNumber]
186 context:[initiatingNSEvent context]
190 #pragma clang diagnostic pop
191 [NSApp postEvent:fakeEvent atStart:YES];
194 void WebPopupMenuProxyMac::hidePopupMenu()
196 [m_popup dismissPopUp];
199 } // namespace WebKit
201 #endif // USE(APPKIT)