OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2011, Google Inc. All rights reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions are | |
6 * met: | |
7 * | |
8 * * Redistributions of source code must retain the above copyright | |
9 * notice, this list of conditions and the following disclaimer. | |
10 * * Redistributions in binary form must reproduce the above | |
11 * copyright notice, this list of conditions and the following disclaimer | |
12 * in the documentation and/or other materials provided with the | |
13 * distribution. | |
14 * * Neither the name of Google Inc. nor the names of its | |
15 * contributors may be used to endorse or promote products derived from | |
16 * this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 */ | |
30 | |
31 #include "config.h" | |
32 #include "web/PopupContainer.h" | |
33 | |
34 #include "core/dom/Document.h" | |
35 #include "core/frame/FrameView.h" | |
36 #include "core/frame/LocalFrame.h" | |
37 #include "core/html/forms/PopupMenuClient.h" | |
38 #include "core/page/Chrome.h" | |
39 #include "core/page/ChromeClient.h" | |
40 #include "core/page/Page.h" | |
41 #include "platform/PlatformGestureEvent.h" | |
42 #include "platform/PlatformKeyboardEvent.h" | |
43 #include "platform/PlatformMouseEvent.h" | |
44 #include "platform/PlatformScreen.h" | |
45 #include "platform/PlatformTouchEvent.h" | |
46 #include "platform/PlatformWheelEvent.h" | |
47 #include "platform/UserGestureIndicator.h" | |
48 #include "platform/geometry/IntRect.h" | |
49 #include "platform/graphics/GraphicsContext.h" | |
50 #include "public/web/WebPopupMenuInfo.h" | |
51 #include "public/web/WebPopupType.h" | |
52 #include "public/web/WebViewClient.h" | |
53 #include "web/PopupContainerClient.h" | |
54 #include "web/WebPopupMenuImpl.h" | |
55 #include "web/WebViewImpl.h" | |
56 #include <algorithm> | |
57 #include <limits> | |
58 | |
59 namespace blink { | |
60 | |
61 static const int borderSize = 1; | |
62 | |
63 static PlatformMouseEvent constructRelativeMouseEvent(const PlatformMouseEvent&
e, PopupContainer* parent, PopupListBox* child) | |
64 { | |
65 IntPoint pos = parent->convertSelfToChild(child, e.position()); | |
66 | |
67 // FIXME: This is a horrible hack since PlatformMouseEvent has no setters fo
r x/y. | |
68 PlatformMouseEvent relativeEvent = e; | |
69 IntPoint& relativePos = const_cast<IntPoint&>(relativeEvent.position()); | |
70 relativePos.setX(pos.x()); | |
71 relativePos.setY(pos.y()); | |
72 return relativeEvent; | |
73 } | |
74 | |
75 static PlatformWheelEvent constructRelativeWheelEvent(const PlatformWheelEvent&
e, PopupContainer* parent, PopupListBox* child) | |
76 { | |
77 IntPoint pos = parent->convertSelfToChild(child, e.position()); | |
78 | |
79 // FIXME: This is a horrible hack since PlatformWheelEvent has no setters fo
r x/y. | |
80 PlatformWheelEvent relativeEvent = e; | |
81 IntPoint& relativePos = const_cast<IntPoint&>(relativeEvent.position()); | |
82 relativePos.setX(pos.x()); | |
83 relativePos.setY(pos.y()); | |
84 return relativeEvent; | |
85 } | |
86 | |
87 // static | |
88 PassRefPtrWillBeRawPtr<PopupContainer> PopupContainer::create(PopupMenuClient* c
lient, bool deviceSupportsTouch) | |
89 { | |
90 return adoptRefWillBeNoop(new PopupContainer(client, deviceSupportsTouch)); | |
91 } | |
92 | |
93 PopupContainer::PopupContainer(PopupMenuClient* client, bool deviceSupportsTouch
) | |
94 : m_listBox(PopupListBox::create(client, deviceSupportsTouch, this)) | |
95 , m_popupOpen(false) | |
96 , m_client(0) | |
97 { | |
98 } | |
99 | |
100 PopupContainer::~PopupContainer() | |
101 { | |
102 #if !ENABLE(OILPAN) | |
103 if (m_listBox->parent()) | |
104 m_listBox->setParent(0); | |
105 #endif | |
106 } | |
107 | |
108 void PopupContainer::trace(Visitor* visitor) | |
109 { | |
110 visitor->trace(m_frameView); | |
111 visitor->trace(m_listBox); | |
112 Widget::trace(visitor); | |
113 } | |
114 | |
115 IntRect PopupContainer::layoutAndCalculateWidgetRectInternal(IntRect widgetRectI
nScreen, int targetControlHeight, const FloatRect& windowRect, const FloatRect&
screen, bool isRTL, const int rtlOffset, const int verticalOffset, const IntSize
& transformOffset, PopupContent* listBox, bool& needToResizeView) | |
116 { | |
117 ASSERT(listBox); | |
118 if (windowRect.x() >= screen.x() && windowRect.maxX() <= screen.maxX() && (w
idgetRectInScreen.x() < screen.x() || widgetRectInScreen.maxX() > screen.maxX())
) { | |
119 // First, inverse the popup alignment if it does not fit the screen - | |
120 // this might fix things (or make them better). | |
121 IntRect inverseWidgetRectInScreen = widgetRectInScreen; | |
122 inverseWidgetRectInScreen.setX(inverseWidgetRectInScreen.x() + (isRTL ?
-rtlOffset : rtlOffset)); | |
123 inverseWidgetRectInScreen.setY(inverseWidgetRectInScreen.y() + (isRTL ?
-verticalOffset : verticalOffset)); | |
124 IntRect enclosingScreen = enclosingIntRect(screen); | |
125 unsigned originalCutoff = std::max(enclosingScreen.x() - widgetRectInScr
een.x(), 0) + std::max(widgetRectInScreen.maxX() - enclosingScreen.maxX(), 0); | |
126 unsigned inverseCutoff = std::max(enclosingScreen.x() - inverseWidgetRec
tInScreen.x(), 0) + std::max(inverseWidgetRectInScreen.maxX() - enclosingScreen.
maxX(), 0); | |
127 | |
128 // Accept the inverse popup alignment if the trimmed content gets | |
129 // shorter than that in the original alignment case. | |
130 if (inverseCutoff < originalCutoff) | |
131 widgetRectInScreen = inverseWidgetRectInScreen; | |
132 | |
133 if (widgetRectInScreen.x() < screen.x()) { | |
134 widgetRectInScreen.setWidth(widgetRectInScreen.maxX() - screen.x()); | |
135 widgetRectInScreen.setX(screen.x()); | |
136 listBox->setMaxWidthAndLayout(std::max(widgetRectInScreen.width() -
borderSize * 2, 0)); | |
137 } else if (widgetRectInScreen.maxX() > screen.maxX()) { | |
138 widgetRectInScreen.setWidth(screen.maxX() - widgetRectInScreen.x()); | |
139 listBox->setMaxWidthAndLayout(std::max(widgetRectInScreen.width() -
borderSize * 2, 0)); | |
140 } | |
141 } | |
142 | |
143 // Calculate Y axis size. | |
144 if (widgetRectInScreen.maxY() > static_cast<int>(screen.maxY())) { | |
145 if (widgetRectInScreen.y() - widgetRectInScreen.height() - targetControl
Height - transformOffset.height() > 0) { | |
146 // There is enough room to open upwards. | |
147 widgetRectInScreen.move(-transformOffset.width(), -(widgetRectInScre
en.height() + targetControlHeight + transformOffset.height())); | |
148 } else { | |
149 // Figure whether upwards or downwards has more room and set the | |
150 // maximum number of items. | |
151 int spaceAbove = widgetRectInScreen.y() - targetControlHeight + tran
sformOffset.height(); | |
152 int spaceBelow = screen.maxY() - widgetRectInScreen.y(); | |
153 if (spaceAbove > spaceBelow) | |
154 listBox->setMaxHeight(spaceAbove); | |
155 else | |
156 listBox->setMaxHeight(spaceBelow); | |
157 listBox->layout(); | |
158 needToResizeView = true; | |
159 widgetRectInScreen.setHeight(listBox->popupContentHeight() + borderS
ize * 2); | |
160 // Move WebWidget upwards if necessary. | |
161 if (spaceAbove > spaceBelow) | |
162 widgetRectInScreen.move(-transformOffset.width(), -(widgetRectIn
Screen.height() + targetControlHeight + transformOffset.height())); | |
163 } | |
164 } | |
165 return widgetRectInScreen; | |
166 } | |
167 | |
168 IntRect PopupContainer::layoutAndCalculateWidgetRect(int targetControlHeight, co
nst IntSize& transformOffset, const IntPoint& popupInitialCoordinate) | |
169 { | |
170 // Reset the max width and height to their default values, they will be | |
171 // recomputed below if necessary. | |
172 m_listBox->setMaxHeight(PopupListBox::defaultMaxHeight); | |
173 m_listBox->setMaxWidth(std::numeric_limits<int>::max()); | |
174 | |
175 // Lay everything out to figure out our preferred size, then tell the view's | |
176 // WidgetClient about it. It should assign us a client. | |
177 m_listBox->layout(); | |
178 fitToListBox(); | |
179 bool isRTL = this->isRTL(); | |
180 | |
181 // Compute the starting x-axis for a normal RTL or right-aligned LTR | |
182 // dropdown. For those, the right edge of dropdown box should be aligned | |
183 // with the right edge of <select>/<input> element box, and the dropdown box | |
184 // should be expanded to the left if more space is needed. | |
185 // m_originalFrameRect.width() is the width of the target <select>/<input> | |
186 // element. | |
187 int rtlOffset = m_controlPosition.p2().x() - m_controlPosition.p1().x() - (m
_listBox->width() + borderSize * 2); | |
188 int rightOffset = isRTL ? rtlOffset : 0; | |
189 | |
190 // Compute the y-axis offset between the bottom left and bottom right | |
191 // points. If the <select>/<input> is transformed, they are not the same. | |
192 int verticalOffset = - m_controlPosition.p4().y() + m_controlPosition.p3().y
(); | |
193 int verticalForRTLOffset = isRTL ? verticalOffset : 0; | |
194 | |
195 // Assume m_listBox size is already calculated. | |
196 IntSize targetSize(m_listBox->width() + borderSize * 2, m_listBox->height()
+ borderSize * 2); | |
197 | |
198 IntRect widgetRectInScreen; | |
199 // If the popup would extend past the bottom of the screen, open upwards | |
200 // instead. | |
201 FloatRect screen = screenAvailableRect(m_frameView.get()); | |
202 // Use popupInitialCoordinate.x() + rightOffset because RTL position | |
203 // needs to be considered. | |
204 float pageScaleFactor = m_frameView->frame().page()->pageScaleFactor(); | |
205 int popupX = round((popupInitialCoordinate.x() + rightOffset) * pageScaleFac
tor); | |
206 int popupY = round((popupInitialCoordinate.y() + verticalForRTLOffset) * pag
eScaleFactor); | |
207 widgetRectInScreen = chromeClient().rootViewToScreen(IntRect(popupX, popupY,
targetSize.width(), targetSize.height())); | |
208 | |
209 // If we have multiple screens and the browser rect is in one screen, we | |
210 // have to clip the window width to the screen width. | |
211 // When clipping, we also need to set a maximum width for the list box. | |
212 FloatRect windowRect = chromeClient().windowRect(); | |
213 | |
214 bool needToResizeView = false; | |
215 widgetRectInScreen = layoutAndCalculateWidgetRectInternal(widgetRectInScreen
, targetControlHeight, windowRect, screen, isRTL, rtlOffset, verticalOffset, tra
nsformOffset, m_listBox.get(), needToResizeView); | |
216 if (needToResizeView) | |
217 fitToListBox(); | |
218 | |
219 return widgetRectInScreen; | |
220 } | |
221 | |
222 void PopupContainer::showPopup(FrameView* view) | |
223 { | |
224 m_frameView = view; | |
225 m_listBox->m_focusedElement = m_frameView->frame().document()->focusedElemen
t(); | |
226 | |
227 IntSize transformOffset(m_controlPosition.p4().x() - m_controlPosition.p1().
x(), m_controlPosition.p4().y() - m_controlPosition.p1().y() - m_controlSize.hei
ght()); | |
228 popupOpened(layoutAndCalculateWidgetRect(m_controlSize.height(), transformOf
fset, roundedIntPoint(m_controlPosition.p4()))); | |
229 m_popupOpen = true; | |
230 | |
231 if (!m_listBox->parent()) | |
232 m_listBox->setParent(this); | |
233 | |
234 m_listBox->scrollToRevealSelection(); | |
235 | |
236 invalidate(); | |
237 } | |
238 | |
239 void PopupContainer::hidePopup() | |
240 { | |
241 m_listBox->abandon(); | |
242 } | |
243 | |
244 void PopupContainer::notifyPopupHidden() | |
245 { | |
246 if (!m_popupOpen) | |
247 return; | |
248 m_popupOpen = false; | |
249 | |
250 // With Oilpan, we cannot assume that the FrameView's LocalFrame's | |
251 // page is still available, as the LocalFrame itself may have been | |
252 // detached from its FrameHost by now. | |
253 // | |
254 // So, if a popup menu is left in an open/shown state when | |
255 // finalized, the PopupMenu implementation of this container's | |
256 // listbox will hide itself when destructed, delivering the | |
257 // notifyPopupHidden() notification in the process & ending up here. | |
258 // If the LocalFrame has been detached already -- done when its | |
259 // HTMLFrameOwnerElement frame owner is detached as part of being | |
260 // torn down -- the connection to the FrameHost has been snipped & | |
261 // there's no page. Hence the null check. | |
262 // | |
263 // In a non-Oilpan setting, the RenderMenuList that controls/owns | |
264 // the PopupMenuChromium object and this PopupContainer is torn | |
265 // down and destructed before the frame and frame owner, hence the | |
266 // page will always be available in that setting and this will | |
267 // not be an issue. | |
268 if (WebViewImpl* webView = WebViewImpl::fromPage(m_frameView->frame().page()
)) | |
269 webView->popupClosed(this); | |
270 } | |
271 | |
272 void PopupContainer::fitToListBox() | |
273 { | |
274 // Place the listbox within our border. | |
275 m_listBox->move(borderSize, borderSize); | |
276 | |
277 // Size ourselves to contain listbox + border. | |
278 resize(m_listBox->width() + borderSize * 2, m_listBox->height() + borderSize
* 2); | |
279 invalidate(); | |
280 } | |
281 | |
282 bool PopupContainer::handleMouseDownEvent(const PlatformMouseEvent& event) | |
283 { | |
284 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture); | |
285 return m_listBox->handleMouseDownEvent( | |
286 constructRelativeMouseEvent(event, this, m_listBox.get())); | |
287 } | |
288 | |
289 bool PopupContainer::handleMouseMoveEvent(const PlatformMouseEvent& event) | |
290 { | |
291 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture); | |
292 return m_listBox->handleMouseMoveEvent( | |
293 constructRelativeMouseEvent(event, this, m_listBox.get())); | |
294 } | |
295 | |
296 bool PopupContainer::handleMouseReleaseEvent(const PlatformMouseEvent& event) | |
297 { | |
298 RefPtrWillBeRawPtr<PopupContainer> protect(this); | |
299 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture); | |
300 return m_listBox->handleMouseReleaseEvent( | |
301 constructRelativeMouseEvent(event, this, m_listBox.get())); | |
302 } | |
303 | |
304 bool PopupContainer::handleWheelEvent(const PlatformWheelEvent& event) | |
305 { | |
306 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture); | |
307 return m_listBox->handleWheelEvent( | |
308 constructRelativeWheelEvent(event, this, m_listBox.get())); | |
309 } | |
310 | |
311 bool PopupContainer::handleTouchEvent(const PlatformTouchEvent&) | |
312 { | |
313 return false; | |
314 } | |
315 | |
316 // FIXME: Refactor this code to share functionality with | |
317 // EventHandler::handleGestureEvent. | |
318 bool PopupContainer::handleGestureEvent(const PlatformGestureEvent& gestureEvent
) | |
319 { | |
320 switch (gestureEvent.type()) { | |
321 case PlatformEvent::GestureTap: { | |
322 PlatformMouseEvent fakeMouseMove(gestureEvent.position(), gestureEvent.g
lobalPosition(), NoButton, PlatformEvent::MouseMoved, /* clickCount */ 1, gestur
eEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.m
etaKey(), PlatformMouseEvent::FromTouch, gestureEvent.timestamp()); | |
323 PlatformMouseEvent fakeMouseDown(gestureEvent.position(), gestureEvent.g
lobalPosition(), LeftButton, PlatformEvent::MousePressed, /* clickCount */ 1, ge
stureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEve
nt.metaKey(), PlatformMouseEvent::FromTouch, gestureEvent.timestamp()); | |
324 PlatformMouseEvent fakeMouseUp(gestureEvent.position(), gestureEvent.glo
balPosition(), LeftButton, PlatformEvent::MouseReleased, /* clickCount */ 1, ges
tureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEven
t.metaKey(), PlatformMouseEvent::FromTouch, gestureEvent.timestamp()); | |
325 // handleMouseMoveEvent(fakeMouseMove); | |
326 handleMouseDownEvent(fakeMouseDown); | |
327 handleMouseReleaseEvent(fakeMouseUp); | |
328 return true; | |
329 } | |
330 case PlatformEvent::GestureScrollUpdate: { | |
331 PlatformWheelEvent syntheticWheelEvent(gestureEvent.position(), gestureE
vent.globalPosition(), gestureEvent.deltaX(), gestureEvent.deltaY(), gestureEven
t.deltaX() / 120.0f, gestureEvent.deltaY() / 120.0f, ScrollByPixelWheelEvent, ge
stureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEve
nt.metaKey()); | |
332 handleWheelEvent(syntheticWheelEvent); | |
333 return true; | |
334 } | |
335 case PlatformEvent::GestureScrollBegin: | |
336 case PlatformEvent::GestureScrollEnd: | |
337 case PlatformEvent::GestureTapDown: | |
338 case PlatformEvent::GestureShowPress: | |
339 break; | |
340 default: | |
341 ASSERT_NOT_REACHED(); | |
342 } | |
343 return false; | |
344 } | |
345 | |
346 bool PopupContainer::handleKeyEvent(const PlatformKeyboardEvent& event) | |
347 { | |
348 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture); | |
349 return m_listBox->handleKeyEvent(event); | |
350 } | |
351 | |
352 void PopupContainer::hide() | |
353 { | |
354 m_listBox->abandon(); | |
355 } | |
356 | |
357 void PopupContainer::paint(GraphicsContext* gc, const IntRect& rect) | |
358 { | |
359 // Adjust coords for scrolled frame. | |
360 IntRect r = intersection(rect, frameRect()); | |
361 int tx = x(); | |
362 int ty = y(); | |
363 | |
364 r.move(-tx, -ty); | |
365 | |
366 gc->translate(static_cast<float>(tx), static_cast<float>(ty)); | |
367 m_listBox->paint(gc, r); | |
368 gc->translate(-static_cast<float>(tx), -static_cast<float>(ty)); | |
369 | |
370 paintBorder(gc, rect); | |
371 } | |
372 | |
373 void PopupContainer::paintBorder(GraphicsContext* gc, const IntRect& rect) | |
374 { | |
375 // FIXME: Where do we get the border color from? | |
376 Color borderColor(127, 157, 185); | |
377 | |
378 gc->setStrokeStyle(NoStroke); | |
379 gc->setFillColor(borderColor); | |
380 | |
381 int tx = x(); | |
382 int ty = y(); | |
383 | |
384 // top, left, bottom, right | |
385 gc->drawRect(IntRect(tx, ty, width(), borderSize)); | |
386 gc->drawRect(IntRect(tx, ty, borderSize, height())); | |
387 gc->drawRect(IntRect(tx, ty + height() - borderSize, width(), borderSize)); | |
388 gc->drawRect(IntRect(tx + width() - borderSize, ty, borderSize, height())); | |
389 } | |
390 | |
391 bool PopupContainer::isInterestedInEventForKey(int keyCode) | |
392 { | |
393 return m_listBox->isInterestedInEventForKey(keyCode); | |
394 } | |
395 | |
396 ChromeClient& PopupContainer::chromeClient() | |
397 { | |
398 return m_frameView->frame().page()->chrome().client(); | |
399 } | |
400 | |
401 void PopupContainer::showInRect(const FloatQuad& controlPosition, const IntSize&
controlSize, FrameView* v, int index) | |
402 { | |
403 // The controlSize is the size of the select box. It's usually larger than | |
404 // we need. Subtract border size so that usually the container will be | |
405 // displayed exactly the same width as the select box. | |
406 m_listBox->setBaseWidth(std::max(controlSize.width() - borderSize * 2, 0)); | |
407 | |
408 m_listBox->updateFromElement(); | |
409 | |
410 // We set the selected item in updateFromElement(), and disregard the | |
411 // index passed into this function (same as Webkit's PopupMenuWin.cpp) | |
412 // FIXME: make sure this is correct, and add an assertion. | |
413 // ASSERT(popupWindow(popup)->listBox()->selectedIndex() == index); | |
414 | |
415 // Save and convert the controlPosition to main window coords. Each point is
converted separately | |
416 // to window coordinates because the control could be in a transformed webvi
ew and then each point | |
417 // would be transformed by a different delta. | |
418 m_controlPosition.setP1(v->contentsToWindow(IntPoint(controlPosition.p1().x(
), controlPosition.p1().y()))); | |
419 m_controlPosition.setP2(v->contentsToWindow(IntPoint(controlPosition.p2().x(
), controlPosition.p2().y()))); | |
420 m_controlPosition.setP3(v->contentsToWindow(IntPoint(controlPosition.p3().x(
), controlPosition.p3().y()))); | |
421 m_controlPosition.setP4(v->contentsToWindow(IntPoint(controlPosition.p4().x(
), controlPosition.p4().y()))); | |
422 | |
423 m_controlSize = controlSize; | |
424 | |
425 // Position at (0, 0) since the frameRect().location() is relative to the | |
426 // parent WebWidget. | |
427 setFrameRect(IntRect(IntPoint(), controlSize)); | |
428 showPopup(v); | |
429 } | |
430 | |
431 IntRect PopupContainer::refresh(const IntRect& targetControlRect) | |
432 { | |
433 m_listBox->setBaseWidth(std::max(m_controlSize.width() - borderSize * 2, 0))
; | |
434 m_listBox->updateFromElement(); | |
435 | |
436 IntPoint locationInWindow = m_frameView->contentsToWindow(targetControlRect.
location()); | |
437 | |
438 // Move it below the select widget. | |
439 locationInWindow.move(0, targetControlRect.height()); | |
440 | |
441 IntRect widgetRectInScreen = layoutAndCalculateWidgetRect(targetControlRect.
height(), IntSize(), locationInWindow); | |
442 | |
443 // Reset the size (which can be set to the PopupListBox size in | |
444 // layoutAndGetRTLOffset(), exceeding the available widget rectangle.) | |
445 if (size() != widgetRectInScreen.size()) | |
446 resize(widgetRectInScreen.size()); | |
447 | |
448 invalidate(); | |
449 | |
450 return widgetRectInScreen; | |
451 } | |
452 | |
453 inline bool PopupContainer::isRTL() const | |
454 { | |
455 return m_listBox->m_popupClient->menuStyle().textDirection() == RTL; | |
456 } | |
457 | |
458 int PopupContainer::selectedIndex() const | |
459 { | |
460 return m_listBox->selectedIndex(); | |
461 } | |
462 | |
463 int PopupContainer::menuItemHeight() const | |
464 { | |
465 return m_listBox->getRowHeight(0); | |
466 } | |
467 | |
468 int PopupContainer::menuItemFontSize() const | |
469 { | |
470 return m_listBox->getRowFont(0).fontDescription().computedSize(); | |
471 } | |
472 | |
473 PopupMenuStyle PopupContainer::menuStyle() const | |
474 { | |
475 return m_listBox->m_popupClient->menuStyle(); | |
476 } | |
477 | |
478 const WTF::Vector<PopupItem*>& PopupContainer:: popupData() const | |
479 { | |
480 return m_listBox->items(); | |
481 } | |
482 | |
483 String PopupContainer::getSelectedItemToolTip() | |
484 { | |
485 // We cannot use m_popupClient->selectedIndex() to choose tooltip message, | |
486 // because the selectedIndex() might return final selected index, not | |
487 // hovering selection. | |
488 return m_listBox->m_popupClient->itemToolTip(m_listBox->m_selectedIndex); | |
489 } | |
490 | |
491 void PopupContainer::popupOpened(const IntRect& bounds) | |
492 { | |
493 WebViewImpl* webView = WebViewImpl::fromPage(m_frameView->frame().page()); | |
494 if (!webView->client()) | |
495 return; | |
496 | |
497 WebWidget* webwidget = webView->client()->createPopupMenu(WebPopupTypeSelect
); | |
498 if (!webwidget) | |
499 return; | |
500 // We only notify when the WebView has to handle the popup, as when | |
501 // the popup is handled externally, the fact that a popup is showing is | |
502 // transparent to the WebView. | |
503 webView->popupOpened(this); | |
504 toWebPopupMenuImpl(webwidget)->initialize(this, bounds); | |
505 } | |
506 | |
507 void PopupContainer::getPopupMenuInfo(WebPopupMenuInfo* info) | |
508 { | |
509 const Vector<PopupItem*>& inputItems = popupData(); | |
510 | |
511 WebVector<WebMenuItemInfo> outputItems(inputItems.size()); | |
512 | |
513 for (size_t i = 0; i < inputItems.size(); ++i) { | |
514 const PopupItem& inputItem = *inputItems[i]; | |
515 WebMenuItemInfo& outputItem = outputItems[i]; | |
516 | |
517 outputItem.label = inputItem.label; | |
518 outputItem.enabled = inputItem.enabled; | |
519 outputItem.textDirection = toWebTextDirection(inputItem.textDirection); | |
520 outputItem.hasTextDirectionOverride = inputItem.hasTextDirectionOverride
; | |
521 | |
522 switch (inputItem.type) { | |
523 case PopupItem::TypeOption: | |
524 outputItem.type = WebMenuItemInfo::Option; | |
525 break; | |
526 case PopupItem::TypeGroup: | |
527 outputItem.type = WebMenuItemInfo::Group; | |
528 break; | |
529 case PopupItem::TypeSeparator: | |
530 outputItem.type = WebMenuItemInfo::Separator; | |
531 break; | |
532 } | |
533 } | |
534 | |
535 info->itemHeight = menuItemHeight(); | |
536 info->itemFontSize = menuItemFontSize(); | |
537 info->selectedIndex = selectedIndex(); | |
538 info->items.swap(outputItems); | |
539 info->rightAligned = menuStyle().textDirection() == RTL; | |
540 } | |
541 | |
542 void PopupContainer::invalidateRect(const IntRect& rect) | |
543 { | |
544 if (HostWindow* h = hostWindow()) | |
545 h->invalidateContentsAndRootView(rect); | |
546 } | |
547 | |
548 HostWindow* PopupContainer::hostWindow() const | |
549 { | |
550 return const_cast<PopupContainerClient*>(m_client); | |
551 } | |
552 | |
553 IntPoint PopupContainer::convertChildToSelf(const Widget* child, const IntPoint&
point) const | |
554 { | |
555 IntPoint newPoint = point; | |
556 newPoint.moveBy(child->location()); | |
557 return newPoint; | |
558 } | |
559 | |
560 IntPoint PopupContainer::convertSelfToChild(const Widget* child, const IntPoint&
point) const | |
561 { | |
562 IntPoint newPoint = point; | |
563 newPoint.moveBy(-child->location()); | |
564 return newPoint; | |
565 } | |
566 | |
567 } // namespace blink | |
OLD | NEW |