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