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 "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