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 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;
     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 
     73     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:
     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.
    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.
    135     int numItems() const { return static_cast<int>(m_items.size()); }
    136 
    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 
    148     virtual void setMaxHeight(int maxHeight) OVERRIDE { m_maxHeight = maxHeight; }
    149 
    150     void setMaxWidth(int maxWidth) { m_maxWindowWidth = maxWidth; }
    151 
    152     virtual void setMaxWidthAndLayout(int) OVERRIDE;
    153 
    154     void disconnectClient() { m_popupClient = 0; }
    155 
    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 
    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);
    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