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