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 "core/platform/chromium/FramelessScrollView.h" 36 #include "core/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 132 // PopupListBox methods 133 134 // Hides the popup. 135 void hidePopup(); 136 137 // Updates our internal list to match the client. 138 void updateFromElement(); 139 140 // Frees any allocated resources used in a particular popup session. 141 void clear(); 142 143 // Sets the index of the option that is displayed in the <select> widget in the page 144 void setOriginalIndex(int); 145 146 // Gets the index of the item that the user is currently moused over or has 147 // selected with the keyboard. This is not the same as the original index, 148 // since the user has not yet accepted this input. 149 int selectedIndex() const { return m_selectedIndex; } 150 151 // Moves selection down/up the given number of items, scrolling if necessary. 152 // Positive is down. The resulting index will be clamped to the range 153 // [0, numItems), and non-option items will be skipped. 154 void adjustSelectedIndex(int delta); 155 156 // Returns the number of items in the list. 157 int numItems() const { return static_cast<int>(m_items.size()); } 158 159 void setBaseWidth(int width) { m_baseWidth = std::min(m_maxWindowWidth, width); } 160 161 // Computes the size of widget and children. 162 virtual void layout() OVERRIDE; 163 164 // Returns whether the popup wants to process events for the passed key. 165 bool isInterestedInEventForKey(int keyCode); 166 167 // Gets the height of a row. 168 int getRowHeight(int index); 169 170 virtual void setMaxHeight(int maxHeight) OVERRIDE { m_maxHeight = maxHeight; } 171 172 void setMaxWidth(int maxWidth) { m_maxWindowWidth = maxWidth; } 173 174 virtual void setMaxWidthAndLayout(int) OVERRIDE; 175 176 void disconnectClient() { m_popupClient = 0; } 177 178 const Vector<PopupItem*>& items() const { return m_items; } 179 180 virtual int popupContentHeight() const OVERRIDE; 181 182 static const int defaultMaxHeight; 183 184 private: 185 friend class PopupContainer; 186 friend class RefCounted<PopupListBox>; 187 188 PopupListBox(PopupMenuClient*, const PopupContainerSettings&); 189 190 virtual ~PopupListBox() 191 { 192 clear(); 193 } 194 195 // Closes the popup 196 void abandon(); 197 198 // Returns true if the selection can be changed to index. 199 // Disabled items, or labels cannot be selected. 200 bool isSelectableItem(int index); 201 202 // Select an index in the list, scrolling if necessary. 203 void selectIndex(int index); 204 205 // Accepts the selected index as the value to be displayed in the <select> 206 // widget on the web page, and closes the popup. Returns true if index is 207 // accepted. 208 bool acceptIndex(int index); 209 210 // Clears the selection (so no row appears selected). 211 void clearSelection(); 212 213 // Scrolls to reveal the given index. 214 void scrollToRevealRow(int index); 215 void scrollToRevealSelection() { scrollToRevealRow(m_selectedIndex); } 216 217 // Invalidates the row at the given index. 218 void invalidateRow(int index); 219 220 // Get the bounds of a row. 221 IntRect getRowBounds(int index); 222 223 // Converts a point to an index of the row the point is over 224 int pointToRowIndex(const IntPoint&); 225 226 // Paint an individual row 227 void paintRow(GraphicsContext*, const IntRect&, int rowIndex); 228 229 // Test if the given point is within the bounds of the popup window. 230 bool isPointInBounds(const IntPoint&); 231 232 // Called when the user presses a text key. Does a prefix-search of the items. 233 void typeAheadFind(const PlatformKeyboardEvent&); 234 235 // Returns the font to use for the given row 236 Font getRowFont(int index); 237 238 // Moves the selection down/up one item, taking care of looping back to the 239 // first/last element if m_loopSelectionNavigation is true. 240 void selectPreviousRow(); 241 void selectNextRow(); 242 243 // The settings that specify the behavior for this Popup window. 244 PopupContainerSettings m_settings; 245 246 // This is the index of the item marked as "selected" - i.e. displayed in 247 // the widget on the page. 248 int m_originalIndex; 249 250 // This is the index of the item that the user is hovered over or has 251 // selected using the keyboard in the list. They have not confirmed this 252 // selection by clicking or pressing enter yet however. 253 int m_selectedIndex; 254 255 // If >= 0, this is the index we should accept if the popup is "abandoned". 256 // This is used for keyboard navigation, where we want the 257 // selection to change immediately, and is only used if the settings 258 // acceptOnAbandon field is true. 259 int m_acceptedIndexOnAbandon; 260 261 // This is the number of rows visible in the popup. The maximum number 262 // visible at a time is defined as being kMaxVisibleRows. For a scrolled 263 // popup, this can be thought of as the page size in data units. 264 int m_visibleRows; 265 266 // Our suggested width, not including scrollbar. 267 int m_baseWidth; 268 269 // The maximum height we can be without being off-screen. 270 int m_maxHeight; 271 272 // A list of the options contained within the <select> 273 Vector<PopupItem*> m_items; 274 275 // The <select> PopupMenuClient that opened us. 276 PopupMenuClient* m_popupClient; 277 278 // The scrollbar which has mouse capture. Mouse events go straight to this 279 // if not null. 280 RefPtr<Scrollbar> m_capturingScrollbar; 281 282 // The last scrollbar that the mouse was over. Used for mouseover highlights. 283 RefPtr<Scrollbar> m_lastScrollbarUnderMouse; 284 285 // The string the user has typed so far into the popup. Used for typeAheadFind. 286 String m_typedString; 287 288 // The char the user has hit repeatedly. Used for typeAheadFind. 289 UChar m_repeatingChar; 290 291 // The last time the user hit a key. Used for typeAheadFind. 292 TimeStamp m_lastCharTime; 293 294 // If width exeeds screen width, we have to clip it. 295 int m_maxWindowWidth; 296 297 // To forward last mouse release event. 298 RefPtr<Element> m_focusedElement; 299 }; 300 301 } // namespace WebCore 302 303 #endif 304