Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(426)

Side by Side Diff: Source/core/platform/chromium/PopupContainer.cpp

Issue 16077002: Move popup menu code from core/platform to WebKit/chromium/src. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 "core/platform/chromium/PopupContainer.h"
33
34 #include "core/dom/Document.h"
35 #include "core/dom/UserGestureIndicator.h"
36 #include "core/page/Chrome.h"
37 #include "core/page/ChromeClient.h"
38 #include "core/page/Frame.h"
39 #include "core/page/FrameView.h"
40 #include "core/page/Page.h"
41 #include "core/platform/PlatformGestureEvent.h"
42 #include "core/platform/PlatformKeyboardEvent.h"
43 #include "core/platform/PlatformMouseEvent.h"
44 #include "core/platform/PlatformScreen.h"
45 #include "core/platform/PlatformTouchEvent.h"
46 #include "core/platform/PlatformWheelEvent.h"
47 #include "core/platform/PopupMenuClient.h"
48 #include "core/platform/chromium/FramelessScrollView.h"
49 #include "core/platform/chromium/FramelessScrollViewClient.h"
50 #include "core/platform/chromium/PopupListBox.h"
51 #include "core/platform/graphics/GraphicsContext.h"
52 #include "core/platform/graphics/IntRect.h"
53 #include <limits>
54
55 namespace WebCore {
56
57 static const int borderSize = 1;
58
59 static PlatformMouseEvent constructRelativeMouseEvent(const PlatformMouseEvent& e, FramelessScrollView* parent, FramelessScrollView* child)
60 {
61 IntPoint pos = parent->convertSelfToChild(child, e.position());
62
63 // FIXME: This is a horrible hack since PlatformMouseEvent has no setters fo r x/y.
64 PlatformMouseEvent relativeEvent = e;
65 IntPoint& relativePos = const_cast<IntPoint&>(relativeEvent.position());
66 relativePos.setX(pos.x());
67 relativePos.setY(pos.y());
68 return relativeEvent;
69 }
70
71 static PlatformWheelEvent constructRelativeWheelEvent(const PlatformWheelEvent& e, FramelessScrollView* parent, FramelessScrollView* child)
72 {
73 IntPoint pos = parent->convertSelfToChild(child, e.position());
74
75 // FIXME: This is a horrible hack since PlatformWheelEvent has no setters fo r x/y.
76 PlatformWheelEvent relativeEvent = e;
77 IntPoint& relativePos = const_cast<IntPoint&>(relativeEvent.position());
78 relativePos.setX(pos.x());
79 relativePos.setY(pos.y());
80 return relativeEvent;
81 }
82
83 // static
84 PassRefPtr<PopupContainer> PopupContainer::create(PopupMenuClient* client, Popup Type popupType, const PopupContainerSettings& settings)
85 {
86 return adoptRef(new PopupContainer(client, popupType, settings));
87 }
88
89 PopupContainer::PopupContainer(PopupMenuClient* client, PopupType popupType, con st PopupContainerSettings& settings)
90 : m_listBox(PopupListBox::create(client, settings))
91 , m_settings(settings)
92 , m_popupType(popupType)
93 , m_popupOpen(false)
94 {
95 setScrollbarModes(ScrollbarAlwaysOff, ScrollbarAlwaysOff);
96 }
97
98 PopupContainer::~PopupContainer()
99 {
100 if (m_listBox && m_listBox->parent())
101 removeChild(m_listBox.get());
102 }
103
104 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)
105 {
106 ASSERT(listBox);
107 if (windowRect.x() >= screen.x() && windowRect.maxX() <= screen.maxX() && (w idgetRectInScreen.x() < screen.x() || widgetRectInScreen.maxX() > screen.maxX()) ) {
108 // First, inverse the popup alignment if it does not fit the screen -
109 // this might fix things (or make them better).
110 IntRect inverseWidgetRectInScreen = widgetRectInScreen;
111 inverseWidgetRectInScreen.setX(inverseWidgetRectInScreen.x() + (isRTL ? -rtlOffset : rtlOffset));
112 inverseWidgetRectInScreen.setY(inverseWidgetRectInScreen.y() + (isRTL ? -verticalOffset : verticalOffset));
113 IntRect enclosingScreen = enclosingIntRect(screen);
114 unsigned originalCutoff = std::max(enclosingScreen.x() - widgetRectInScr een.x(), 0) + std::max(widgetRectInScreen.maxX() - enclosingScreen.maxX(), 0);
115 unsigned inverseCutoff = std::max(enclosingScreen.x() - inverseWidgetRec tInScreen.x(), 0) + std::max(inverseWidgetRectInScreen.maxX() - enclosingScreen. maxX(), 0);
116
117 // Accept the inverse popup alignment if the trimmed content gets
118 // shorter than that in the original alignment case.
119 if (inverseCutoff < originalCutoff)
120 widgetRectInScreen = inverseWidgetRectInScreen;
121
122 if (widgetRectInScreen.x() < screen.x()) {
123 widgetRectInScreen.setWidth(widgetRectInScreen.maxX() - screen.x());
124 widgetRectInScreen.setX(screen.x());
125 listBox->setMaxWidthAndLayout(std::max(widgetRectInScreen.width() - borderSize * 2, 0));
126 } else if (widgetRectInScreen.maxX() > screen.maxX()) {
127 widgetRectInScreen.setWidth(screen.maxX() - widgetRectInScreen.x());
128 listBox->setMaxWidthAndLayout(std::max(widgetRectInScreen.width() - borderSize * 2, 0));
129 }
130 }
131
132 // Calculate Y axis size.
133 if (widgetRectInScreen.maxY() > static_cast<int>(screen.maxY())) {
134 if (widgetRectInScreen.y() - widgetRectInScreen.height() - targetControl Height - transformOffset.height() > 0) {
135 // There is enough room to open upwards.
136 widgetRectInScreen.move(-transformOffset.width(), -(widgetRectInScre en.height() + targetControlHeight + transformOffset.height()));
137 } else {
138 // Figure whether upwards or downwards has more room and set the
139 // maximum number of items.
140 int spaceAbove = widgetRectInScreen.y() - targetControlHeight + tran sformOffset.height();
141 int spaceBelow = screen.maxY() - widgetRectInScreen.y();
142 if (spaceAbove > spaceBelow)
143 listBox->setMaxHeight(spaceAbove);
144 else
145 listBox->setMaxHeight(spaceBelow);
146 listBox->layout();
147 needToResizeView = true;
148 widgetRectInScreen.setHeight(listBox->popupContentHeight() + borderS ize * 2);
149 // Move WebWidget upwards if necessary.
150 if (spaceAbove > spaceBelow)
151 widgetRectInScreen.move(-transformOffset.width(), -(widgetRectIn Screen.height() + targetControlHeight + transformOffset.height()));
152 }
153 }
154 return widgetRectInScreen;
155 }
156
157 IntRect PopupContainer::layoutAndCalculateWidgetRect(int targetControlHeight, co nst IntSize& transformOffset, const IntPoint& popupInitialCoordinate)
158 {
159 // Reset the max width and height to their default values, they will be
160 // recomputed below if necessary.
161 m_listBox->setMaxHeight(PopupListBox::defaultMaxHeight);
162 m_listBox->setMaxWidth(std::numeric_limits<int>::max());
163
164 // Lay everything out to figure out our preferred size, then tell the view's
165 // WidgetClient about it. It should assign us a client.
166 m_listBox->layout();
167 fitToListBox();
168 bool isRTL = this->isRTL();
169
170 // Compute the starting x-axis for a normal RTL or right-aligned LTR
171 // dropdown. For those, the right edge of dropdown box should be aligned
172 // with the right edge of <select>/<input> element box, and the dropdown box
173 // should be expanded to the left if more space is needed.
174 // m_originalFrameRect.width() is the width of the target <select>/<input>
175 // element.
176 int rtlOffset = m_controlPosition.p2().x() - m_controlPosition.p1().x() - (m _listBox->width() + borderSize * 2);
177 int rightOffset = isRTL ? rtlOffset : 0;
178
179 // Compute the y-axis offset between the bottom left and bottom right
180 // points. If the <select>/<input> is transformed, they are not the same.
181 int verticalOffset = - m_controlPosition.p4().y() + m_controlPosition.p3().y ();
182 int verticalForRTLOffset = isRTL ? verticalOffset : 0;
183
184 // Assume m_listBox size is already calculated.
185 IntSize targetSize(m_listBox->width() + borderSize * 2, m_listBox->height() + borderSize * 2);
186
187 IntRect widgetRectInScreen;
188 if (ChromeClient* client = chromeClient()) {
189 // If the popup would extend past the bottom of the screen, open upwards
190 // instead.
191 FloatRect screen = screenAvailableRect(m_frameView.get());
192 // Use popupInitialCoordinate.x() + rightOffset because RTL position
193 // needs to be considered.
194 widgetRectInScreen = client->rootViewToScreen(IntRect(popupInitialCoordi nate.x() + rightOffset, popupInitialCoordinate.y() + verticalForRTLOffset, targe tSize.width(), targetSize.height()));
195
196 // If we have multiple screens and the browser rect is in one screen, we
197 // have to clip the window width to the screen width.
198 // When clipping, we also need to set a maximum width for the list box.
199 FloatRect windowRect = client->windowRect();
200
201 bool needToResizeView = false;
202 widgetRectInScreen = layoutAndCalculateWidgetRectInternal(widgetRectInSc reen, targetControlHeight, windowRect, screen, isRTL, rtlOffset, verticalOffset, transformOffset, m_listBox.get(), needToResizeView);
203 if (needToResizeView)
204 fitToListBox();
205 }
206
207 return widgetRectInScreen;
208 }
209
210 void PopupContainer::showPopup(FrameView* view)
211 {
212 m_frameView = view;
213 listBox()->m_focusedNode = m_frameView->frame()->document()->focusedNode();
214
215 if (ChromeClient* client = chromeClient()) {
216 IntSize transformOffset(m_controlPosition.p4().x() - m_controlPosition.p 1().x(), m_controlPosition.p4().y() - m_controlPosition.p1().y() - m_controlSize .height());
217 client->popupOpened(this, layoutAndCalculateWidgetRect(m_controlSize.hei ght(), transformOffset, roundedIntPoint(m_controlPosition.p4())), false);
218 m_popupOpen = true;
219 }
220
221 if (!m_listBox->parent())
222 addChild(m_listBox.get());
223
224 // Enable scrollbars after the listbox is inserted into the hierarchy,
225 // so it has a proper WidgetClient.
226 m_listBox->setVerticalScrollbarMode(ScrollbarAuto);
227
228 m_listBox->scrollToRevealSelection();
229
230 invalidate();
231 }
232
233 void PopupContainer::hidePopup()
234 {
235 listBox()->hidePopup();
236 }
237
238 void PopupContainer::notifyPopupHidden()
239 {
240 if (!m_popupOpen)
241 return;
242 m_popupOpen = false;
243 chromeClient()->popupClosed(this);
244 }
245
246 void PopupContainer::fitToListBox()
247 {
248 // Place the listbox within our border.
249 m_listBox->move(borderSize, borderSize);
250
251 // Size ourselves to contain listbox + border.
252 resize(m_listBox->width() + borderSize * 2, m_listBox->height() + borderSize * 2);
253 invalidate();
254 }
255
256 bool PopupContainer::handleMouseDownEvent(const PlatformMouseEvent& event)
257 {
258 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
259 return m_listBox->handleMouseDownEvent(
260 constructRelativeMouseEvent(event, this, m_listBox.get()));
261 }
262
263 bool PopupContainer::handleMouseMoveEvent(const PlatformMouseEvent& event)
264 {
265 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
266 return m_listBox->handleMouseMoveEvent(
267 constructRelativeMouseEvent(event, this, m_listBox.get()));
268 }
269
270 bool PopupContainer::handleMouseReleaseEvent(const PlatformMouseEvent& event)
271 {
272 RefPtr<PopupContainer> protect(this);
273 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
274 return m_listBox->handleMouseReleaseEvent(
275 constructRelativeMouseEvent(event, this, m_listBox.get()));
276 }
277
278 bool PopupContainer::handleWheelEvent(const PlatformWheelEvent& event)
279 {
280 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
281 return m_listBox->handleWheelEvent(
282 constructRelativeWheelEvent(event, this, m_listBox.get()));
283 }
284
285 bool PopupContainer::handleTouchEvent(const PlatformTouchEvent&)
286 {
287 return false;
288 }
289
290 // FIXME: Refactor this code to share functionality with
291 // EventHandler::handleGestureEvent.
292 bool PopupContainer::handleGestureEvent(const PlatformGestureEvent& gestureEvent )
293 {
294 switch (gestureEvent.type()) {
295 case PlatformEvent::GestureTap: {
296 PlatformMouseEvent fakeMouseMove(gestureEvent.position(), gestureEvent.g lobalPosition(), NoButton, PlatformEvent::MouseMoved, /* clickCount */ 1, gestur eEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.m etaKey(), gestureEvent.timestamp());
297 PlatformMouseEvent fakeMouseDown(gestureEvent.position(), gestureEvent.g lobalPosition(), LeftButton, PlatformEvent::MousePressed, /* clickCount */ 1, ge stureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEve nt.metaKey(), gestureEvent.timestamp());
298 PlatformMouseEvent fakeMouseUp(gestureEvent.position(), gestureEvent.glo balPosition(), LeftButton, PlatformEvent::MouseReleased, /* clickCount */ 1, ges tureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEven t.metaKey(), gestureEvent.timestamp());
299 // handleMouseMoveEvent(fakeMouseMove);
300 handleMouseDownEvent(fakeMouseDown);
301 handleMouseReleaseEvent(fakeMouseUp);
302 return true;
303 }
304 case PlatformEvent::GestureScrollUpdate:
305 case PlatformEvent::GestureScrollUpdateWithoutPropagation: {
306 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());
307 handleWheelEvent(syntheticWheelEvent);
308 return true;
309 }
310 case PlatformEvent::GestureScrollBegin:
311 case PlatformEvent::GestureScrollEnd:
312 case PlatformEvent::GestureTapDown:
313 break;
314 default:
315 ASSERT_NOT_REACHED();
316 }
317 return false;
318 }
319
320 bool PopupContainer::handleKeyEvent(const PlatformKeyboardEvent& event)
321 {
322 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
323 return m_listBox->handleKeyEvent(event);
324 }
325
326 void PopupContainer::hide()
327 {
328 m_listBox->abandon();
329 }
330
331 void PopupContainer::paint(GraphicsContext* gc, const IntRect& rect)
332 {
333 // Adjust coords for scrolled frame.
334 IntRect r = intersection(rect, frameRect());
335 int tx = x();
336 int ty = y();
337
338 r.move(-tx, -ty);
339
340 gc->translate(static_cast<float>(tx), static_cast<float>(ty));
341 m_listBox->paint(gc, r);
342 gc->translate(-static_cast<float>(tx), -static_cast<float>(ty));
343
344 paintBorder(gc, rect);
345 }
346
347 void PopupContainer::paintBorder(GraphicsContext* gc, const IntRect& rect)
348 {
349 // FIXME: Where do we get the border color from?
350 Color borderColor(127, 157, 185);
351
352 gc->setStrokeStyle(NoStroke);
353 gc->setFillColor(borderColor, ColorSpaceDeviceRGB);
354
355 int tx = x();
356 int ty = y();
357
358 // top, left, bottom, right
359 gc->drawRect(IntRect(tx, ty, width(), borderSize));
360 gc->drawRect(IntRect(tx, ty, borderSize, height()));
361 gc->drawRect(IntRect(tx, ty + height() - borderSize, width(), borderSize));
362 gc->drawRect(IntRect(tx + width() - borderSize, ty, borderSize, height()));
363 }
364
365 bool PopupContainer::isInterestedInEventForKey(int keyCode)
366 {
367 return m_listBox->isInterestedInEventForKey(keyCode);
368 }
369
370 ChromeClient* PopupContainer::chromeClient()
371 {
372 return m_frameView->frame()->page()->chrome().client();
373 }
374
375 void PopupContainer::showInRect(const FloatQuad& controlPosition, const IntSize& controlSize, FrameView* v, int index)
376 {
377 // The controlSize is the size of the select box. It's usually larger than
378 // we need. Subtract border size so that usually the container will be
379 // displayed exactly the same width as the select box.
380 listBox()->setBaseWidth(max(controlSize.width() - borderSize * 2, 0));
381
382 listBox()->updateFromElement();
383
384 // We set the selected item in updateFromElement(), and disregard the
385 // index passed into this function (same as Webkit's PopupMenuWin.cpp)
386 // FIXME: make sure this is correct, and add an assertion.
387 // ASSERT(popupWindow(popup)->listBox()->selectedIndex() == index);
388
389 // Save and convert the controlPosition to main window coords.
390 m_controlPosition = controlPosition;
391 IntPoint delta = v->contentsToWindow(IntPoint());
392 m_controlPosition.move(delta.x(), delta.y());
393 m_controlSize = controlSize;
394
395 // Position at (0, 0) since the frameRect().location() is relative to the
396 // parent WebWidget.
397 setFrameRect(IntRect(IntPoint(), controlSize));
398 showPopup(v);
399 }
400
401 IntRect PopupContainer::refresh(const IntRect& targetControlRect)
402 {
403 listBox()->setBaseWidth(max(m_controlSize.width() - borderSize * 2, 0));
404 listBox()->updateFromElement();
405
406 IntPoint locationInWindow = m_frameView->contentsToWindow(targetControlRect. location());
407
408 // Move it below the select widget.
409 locationInWindow.move(0, targetControlRect.height());
410
411 IntRect widgetRectInScreen = layoutAndCalculateWidgetRect(targetControlRect. height(), IntSize(), locationInWindow);
412
413 // Reset the size (which can be set to the PopupListBox size in
414 // layoutAndGetRTLOffset(), exceeding the available widget rectangle.)
415 if (size() != widgetRectInScreen.size())
416 resize(widgetRectInScreen.size());
417
418 invalidate();
419
420 return widgetRectInScreen;
421 }
422
423 inline bool PopupContainer::isRTL() const
424 {
425 return m_listBox->m_popupClient->menuStyle().textDirection() == RTL;
426 }
427
428 int PopupContainer::selectedIndex() const
429 {
430 return m_listBox->selectedIndex();
431 }
432
433 int PopupContainer::menuItemHeight() const
434 {
435 return m_listBox->getRowHeight(0);
436 }
437
438 int PopupContainer::menuItemFontSize() const
439 {
440 return m_listBox->getRowFont(0).size();
441 }
442
443 PopupMenuStyle PopupContainer::menuStyle() const
444 {
445 return m_listBox->m_popupClient->menuStyle();
446 }
447
448 const WTF::Vector<PopupItem*>& PopupContainer:: popupData() const
449 {
450 return m_listBox->items();
451 }
452
453 String PopupContainer::getSelectedItemToolTip()
454 {
455 // We cannot use m_popupClient->selectedIndex() to choose tooltip message,
456 // because the selectedIndex() might return final selected index, not
457 // hovering selection.
458 return listBox()->m_popupClient->itemToolTip(listBox()->m_selectedIndex);
459 }
460
461 } // namespace WebCore
OLDNEW
« no previous file with comments | « Source/core/platform/chromium/PopupContainer.h ('k') | Source/core/platform/chromium/PopupListBox.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698