Home | History | Annotate | Download | only in web
      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