Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 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 | */ |
Torne (Richard Coles) | 93ac45c | 2013-05-29 14:40:20 +0100 | [diff] [blame] | 30 | |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 31 | #ifndef PopupListBox_h |
| 32 | #define PopupListBox_h |
| 33 | |
Ben Murdoch | 7757ec2 | 2013-07-23 11:17:36 +0100 | [diff] [blame] | 34 | #include "core/dom/Element.h" |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 35 | #include "platform/scroll/FramelessScrollView.h" |
Torne (Richard Coles) | 1e20218 | 2013-10-18 15:46:42 +0100 | [diff] [blame] | 36 | #include "platform/text/TextDirection.h" |
Torne (Richard Coles) | 93ac45c | 2013-05-29 14:40:20 +0100 | [diff] [blame] | 37 | #include "wtf/text/WTFString.h" |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 38 | |
| 39 | namespace WebCore { |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 40 | class Font; |
| 41 | class GraphicsContext; |
| 42 | class IntRect; |
| 43 | class PlatformKeyboardEvent; |
| 44 | class PlatformMouseEvent; |
| 45 | class PlatformGestureEvent; |
| 46 | class PlatformTouchEvent; |
| 47 | class PlatformWheelEvent; |
| 48 | class PopupMenuClient; |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 49 | } |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 50 | |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 51 | namespace blink { |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 52 | |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 53 | typedef unsigned long long TimeStamp; |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 54 | |
| 55 | class PopupContent { |
| 56 | public: |
| 57 | virtual void layout() = 0; |
| 58 | virtual void setMaxHeight(int) = 0; |
| 59 | virtual void setMaxWidthAndLayout(int) = 0; |
| 60 | virtual int popupContentHeight() const = 0; |
| 61 | virtual ~PopupContent() { }; |
| 62 | }; |
| 63 | |
| 64 | // A container for the data for each menu item (e.g. represented by <option> |
| 65 | // or <optgroup> in a <select> widget) and is used by PopupListBox. |
| 66 | struct PopupItem { |
| 67 | enum Type { |
| 68 | TypeOption, |
| 69 | TypeGroup, |
| 70 | TypeSeparator |
| 71 | }; |
| 72 | |
| 73 | PopupItem(const String& label, Type type) |
| 74 | : label(label) |
| 75 | , type(type) |
| 76 | , yOffset(0) |
| 77 | { |
| 78 | } |
| 79 | String label; |
| 80 | Type type; |
| 81 | int yOffset; // y offset of this item, relative to the top of the popup. |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 82 | WebCore::TextDirection textDirection; |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 83 | bool hasTextDirectionOverride; |
| 84 | bool enabled; |
| 85 | }; |
| 86 | |
| 87 | // This class uses WebCore code to paint and handle events for a drop-down list |
| 88 | // box ("combobox" on Windows). |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 89 | class PopupListBox FINAL : public WebCore::FramelessScrollView, public PopupContent { |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 90 | public: |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 91 | static PassRefPtr<PopupListBox> create(WebCore::PopupMenuClient* client, bool deviceSupportsTouch) |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 92 | { |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 93 | return adoptRef(new PopupListBox(client, deviceSupportsTouch)); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 94 | } |
| 95 | |
| 96 | // FramelessScrollView |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 97 | virtual void paint(WebCore::GraphicsContext*, const WebCore::IntRect&) OVERRIDE; |
| 98 | virtual bool handleMouseDownEvent(const WebCore::PlatformMouseEvent&) OVERRIDE; |
| 99 | virtual bool handleMouseMoveEvent(const WebCore::PlatformMouseEvent&) OVERRIDE; |
| 100 | virtual bool handleMouseReleaseEvent(const WebCore::PlatformMouseEvent&) OVERRIDE; |
| 101 | virtual bool handleWheelEvent(const WebCore::PlatformWheelEvent&) OVERRIDE; |
| 102 | virtual bool handleKeyEvent(const WebCore::PlatformKeyboardEvent&) OVERRIDE; |
| 103 | virtual bool handleTouchEvent(const WebCore::PlatformTouchEvent&) OVERRIDE; |
| 104 | virtual bool handleGestureEvent(const WebCore::PlatformGestureEvent&) OVERRIDE; |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 105 | |
| 106 | // ScrollView |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 107 | virtual WebCore::HostWindow* hostWindow() const OVERRIDE; |
Torne (Richard Coles) | 51b2906 | 2013-11-28 11:56:03 +0000 | [diff] [blame] | 108 | virtual bool shouldPlaceVerticalScrollbarOnLeft() const OVERRIDE; |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 109 | |
| 110 | // PopupListBox methods |
| 111 | |
Torne (Richard Coles) | 06f816c | 2013-09-26 13:25:12 +0100 | [diff] [blame] | 112 | // Closes the popup |
| 113 | void abandon(); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 114 | |
| 115 | // Updates our internal list to match the client. |
| 116 | void updateFromElement(); |
| 117 | |
| 118 | // Frees any allocated resources used in a particular popup session. |
| 119 | void clear(); |
| 120 | |
| 121 | // Sets the index of the option that is displayed in the <select> widget in the page |
| 122 | void setOriginalIndex(int); |
| 123 | |
Torne (Richard Coles) | 93ac45c | 2013-05-29 14:40:20 +0100 | [diff] [blame] | 124 | // Gets the index of the item that the user is currently moused over or has |
| 125 | // selected with the keyboard. This is not the same as the original index, |
| 126 | // since the user has not yet accepted this input. |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 127 | int selectedIndex() const { return m_selectedIndex; } |
| 128 | |
| 129 | // Moves selection down/up the given number of items, scrolling if necessary. |
| 130 | // Positive is down. The resulting index will be clamped to the range |
| 131 | // [0, numItems), and non-option items will be skipped. |
| 132 | void adjustSelectedIndex(int delta); |
| 133 | |
| 134 | // Returns the number of items in the list. |
| 135 | int numItems() const { return static_cast<int>(m_items.size()); } |
| 136 | |
| 137 | void setBaseWidth(int width) { m_baseWidth = std::min(m_maxWindowWidth, width); } |
| 138 | |
| 139 | // Computes the size of widget and children. |
| 140 | virtual void layout() OVERRIDE; |
| 141 | |
| 142 | // Returns whether the popup wants to process events for the passed key. |
| 143 | bool isInterestedInEventForKey(int keyCode); |
| 144 | |
| 145 | // Gets the height of a row. |
| 146 | int getRowHeight(int index); |
| 147 | |
| 148 | virtual void setMaxHeight(int maxHeight) OVERRIDE { m_maxHeight = maxHeight; } |
| 149 | |
| 150 | void setMaxWidth(int maxWidth) { m_maxWindowWidth = maxWidth; } |
| 151 | |
| 152 | virtual void setMaxWidthAndLayout(int) OVERRIDE; |
| 153 | |
| 154 | void disconnectClient() { m_popupClient = 0; } |
| 155 | |
| 156 | const Vector<PopupItem*>& items() const { return m_items; } |
| 157 | |
| 158 | virtual int popupContentHeight() const OVERRIDE; |
| 159 | |
Torne (Richard Coles) | 93ac45c | 2013-05-29 14:40:20 +0100 | [diff] [blame] | 160 | static const int defaultMaxHeight; |
| 161 | |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 162 | private: |
| 163 | friend class PopupContainer; |
| 164 | friend class RefCounted<PopupListBox>; |
| 165 | |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 166 | PopupListBox(WebCore::PopupMenuClient*, bool deviceSupportsTouch); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 167 | |
| 168 | virtual ~PopupListBox() |
| 169 | { |
| 170 | clear(); |
| 171 | } |
| 172 | |
Torne (Richard Coles) | 06f816c | 2013-09-26 13:25:12 +0100 | [diff] [blame] | 173 | // Hides the popup. Other classes should not call this. Use abandon instead. |
| 174 | void hidePopup(); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 175 | |
| 176 | // Returns true if the selection can be changed to index. |
| 177 | // Disabled items, or labels cannot be selected. |
| 178 | bool isSelectableItem(int index); |
| 179 | |
| 180 | // Select an index in the list, scrolling if necessary. |
| 181 | void selectIndex(int index); |
| 182 | |
Torne (Richard Coles) | 93ac45c | 2013-05-29 14:40:20 +0100 | [diff] [blame] | 183 | // Accepts the selected index as the value to be displayed in the <select> |
| 184 | // widget on the web page, and closes the popup. Returns true if index is |
| 185 | // accepted. |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 186 | bool acceptIndex(int index); |
| 187 | |
| 188 | // Clears the selection (so no row appears selected). |
| 189 | void clearSelection(); |
| 190 | |
| 191 | // Scrolls to reveal the given index. |
| 192 | void scrollToRevealRow(int index); |
| 193 | void scrollToRevealSelection() { scrollToRevealRow(m_selectedIndex); } |
| 194 | |
| 195 | // Invalidates the row at the given index. |
| 196 | void invalidateRow(int index); |
| 197 | |
| 198 | // Get the bounds of a row. |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 199 | WebCore::IntRect getRowBounds(int index); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 200 | |
| 201 | // Converts a point to an index of the row the point is over |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 202 | int pointToRowIndex(const WebCore::IntPoint&); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 203 | |
| 204 | // Paint an individual row |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 205 | void paintRow(WebCore::GraphicsContext*, const WebCore::IntRect&, int rowIndex); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 206 | |
| 207 | // Test if the given point is within the bounds of the popup window. |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 208 | bool isPointInBounds(const WebCore::IntPoint&); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 209 | |
| 210 | // Called when the user presses a text key. Does a prefix-search of the items. |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 211 | void typeAheadFind(const WebCore::PlatformKeyboardEvent&); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 212 | |
| 213 | // Returns the font to use for the given row |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 214 | WebCore::Font getRowFont(int index); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 215 | |
| 216 | // Moves the selection down/up one item, taking care of looping back to the |
| 217 | // first/last element if m_loopSelectionNavigation is true. |
| 218 | void selectPreviousRow(); |
| 219 | void selectNextRow(); |
| 220 | |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 221 | // If the device is a touch screen we increase the height of menu items |
| 222 | // to make it easier to unambiguously touch them. |
| 223 | bool m_deviceSupportsTouch; |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 224 | |
Torne (Richard Coles) | 93ac45c | 2013-05-29 14:40:20 +0100 | [diff] [blame] | 225 | // This is the index of the item marked as "selected" - i.e. displayed in |
| 226 | // the widget on the page. |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 227 | int m_originalIndex; |
| 228 | |
Torne (Richard Coles) | 93ac45c | 2013-05-29 14:40:20 +0100 | [diff] [blame] | 229 | // This is the index of the item that the user is hovered over or has |
| 230 | // selected using the keyboard in the list. They have not confirmed this |
| 231 | // selection by clicking or pressing enter yet however. |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 232 | int m_selectedIndex; |
| 233 | |
| 234 | // If >= 0, this is the index we should accept if the popup is "abandoned". |
| 235 | // This is used for keyboard navigation, where we want the |
| 236 | // selection to change immediately, and is only used if the settings |
| 237 | // acceptOnAbandon field is true. |
| 238 | int m_acceptedIndexOnAbandon; |
| 239 | |
Torne (Richard Coles) | 93ac45c | 2013-05-29 14:40:20 +0100 | [diff] [blame] | 240 | // This is the number of rows visible in the popup. The maximum number |
| 241 | // visible at a time is defined as being kMaxVisibleRows. For a scrolled |
| 242 | // popup, this can be thought of as the page size in data units. |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 243 | int m_visibleRows; |
| 244 | |
| 245 | // Our suggested width, not including scrollbar. |
| 246 | int m_baseWidth; |
| 247 | |
| 248 | // The maximum height we can be without being off-screen. |
| 249 | int m_maxHeight; |
| 250 | |
| 251 | // A list of the options contained within the <select> |
| 252 | Vector<PopupItem*> m_items; |
| 253 | |
| 254 | // The <select> PopupMenuClient that opened us. |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 255 | WebCore::PopupMenuClient* m_popupClient; |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 256 | |
| 257 | // The scrollbar which has mouse capture. Mouse events go straight to this |
| 258 | // if not null. |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 259 | RefPtr<WebCore::Scrollbar> m_capturingScrollbar; |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 260 | |
| 261 | // The last scrollbar that the mouse was over. Used for mouseover highlights. |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 262 | RefPtr<WebCore::Scrollbar> m_lastScrollbarUnderMouse; |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 263 | |
| 264 | // The string the user has typed so far into the popup. Used for typeAheadFind. |
| 265 | String m_typedString; |
| 266 | |
| 267 | // The char the user has hit repeatedly. Used for typeAheadFind. |
| 268 | UChar m_repeatingChar; |
| 269 | |
| 270 | // The last time the user hit a key. Used for typeAheadFind. |
| 271 | TimeStamp m_lastCharTime; |
| 272 | |
| 273 | // If width exeeds screen width, we have to clip it. |
| 274 | int m_maxWindowWidth; |
| 275 | |
| 276 | // To forward last mouse release event. |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 277 | RefPtr<WebCore::Element> m_focusedElement; |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 278 | }; |
| 279 | |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame^] | 280 | } // namespace blink |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 281 | |
| 282 | #endif |