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 #ifndef PopupListBox_h 32 #define PopupListBox_h 33 34 #include "core/dom/Element.h" 35 #include "platform/scroll/FramelessScrollView.h" 36 #include "platform/text/TextDirection.h" 37 #include "wtf/text/WTFString.h" 38 39 namespace WebCore { 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; 49 } 50 51 namespace blink { 52 53 typedef unsigned long long TimeStamp; 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; ~PopupContent()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 PopupItemPopupItem73 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. 82 WebCore::TextDirection textDirection; 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). 89 class PopupListBox FINAL : public WebCore::FramelessScrollView, public PopupContent { 90 public: create(WebCore::PopupMenuClient * client,bool deviceSupportsTouch)91 static PassRefPtr<PopupListBox> create(WebCore::PopupMenuClient* client, bool deviceSupportsTouch) 92 { 93 return adoptRef(new PopupListBox(client, deviceSupportsTouch)); 94 } 95 96 // FramelessScrollView 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; 105 106 // ScrollView 107 virtual WebCore::HostWindow* hostWindow() const OVERRIDE; 108 virtual bool shouldPlaceVerticalScrollbarOnLeft() const OVERRIDE; 109 110 // PopupListBox methods 111 112 // Closes the popup 113 void abandon(); 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 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. selectedIndex()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. numItems()135 int numItems() const { return static_cast<int>(m_items.size()); } 136 setBaseWidth(int width)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 setMaxHeight(int maxHeight)148 virtual void setMaxHeight(int maxHeight) OVERRIDE { m_maxHeight = maxHeight; } 149 setMaxWidth(int maxWidth)150 void setMaxWidth(int maxWidth) { m_maxWindowWidth = maxWidth; } 151 152 virtual void setMaxWidthAndLayout(int) OVERRIDE; 153 disconnectClient()154 void disconnectClient() { m_popupClient = 0; } 155 items()156 const Vector<PopupItem*>& items() const { return m_items; } 157 158 virtual int popupContentHeight() const OVERRIDE; 159 160 static const int defaultMaxHeight; 161 162 private: 163 friend class PopupContainer; 164 friend class RefCounted<PopupListBox>; 165 166 PopupListBox(WebCore::PopupMenuClient*, bool deviceSupportsTouch); 167 ~PopupListBox()168 virtual ~PopupListBox() 169 { 170 clear(); 171 } 172 173 // Hides the popup. Other classes should not call this. Use abandon instead. 174 void hidePopup(); 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 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. 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); scrollToRevealSelection()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. 199 WebCore::IntRect getRowBounds(int index); 200 201 // Converts a point to an index of the row the point is over 202 int pointToRowIndex(const WebCore::IntPoint&); 203 204 // Paint an individual row 205 void paintRow(WebCore::GraphicsContext*, const WebCore::IntRect&, int rowIndex); 206 207 // Test if the given point is within the bounds of the popup window. 208 bool isPointInBounds(const WebCore::IntPoint&); 209 210 // Called when the user presses a text key. Does a prefix-search of the items. 211 void typeAheadFind(const WebCore::PlatformKeyboardEvent&); 212 213 // Returns the font to use for the given row 214 WebCore::Font getRowFont(int index); 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 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; 224 225 // This is the index of the item marked as "selected" - i.e. displayed in 226 // the widget on the page. 227 int m_originalIndex; 228 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. 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 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. 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. 255 WebCore::PopupMenuClient* m_popupClient; 256 257 // The scrollbar which has mouse capture. Mouse events go straight to this 258 // if not null. 259 RefPtr<WebCore::Scrollbar> m_capturingScrollbar; 260 261 // The last scrollbar that the mouse was over. Used for mouseover highlights. 262 RefPtr<WebCore::Scrollbar> m_lastScrollbarUnderMouse; 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. 277 RefPtrWillBePersistent<WebCore::Element> m_focusedElement; 278 }; 279 280 } // namespace blink 281 282 #endif 283