| 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 |