/* * Copyright (c) 2011, Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef PopupListBox_h #define PopupListBox_h #include "core/dom/Element.h" #include "platform/scroll/FramelessScrollView.h" #include "platform/text/TextDirection.h" #include "wtf/text/WTFString.h" namespace WebCore { class Font; class GraphicsContext; class IntRect; class PlatformKeyboardEvent; class PlatformMouseEvent; class PlatformGestureEvent; class PlatformTouchEvent; class PlatformWheelEvent; class PopupMenuClient; } namespace blink { typedef unsigned long long TimeStamp; class PopupContent { public: virtual void layout() = 0; virtual void setMaxHeight(int) = 0; virtual void setMaxWidthAndLayout(int) = 0; virtual int popupContentHeight() const = 0; virtual ~PopupContent() { }; }; // A container for the data for each menu item (e.g. represented by <option> // or <optgroup> in a <select> widget) and is used by PopupListBox. struct PopupItem { enum Type { TypeOption, TypeGroup, TypeSeparator }; PopupItem(const String& label, Type type) : label(label) , type(type) , yOffset(0) { } String label; Type type; int yOffset; // y offset of this item, relative to the top of the popup. WebCore::TextDirection textDirection; bool hasTextDirectionOverride; bool enabled; }; // This class uses WebCore code to paint and handle events for a drop-down list // box ("combobox" on Windows). class PopupListBox FINAL : public WebCore::FramelessScrollView, public PopupContent { public: static PassRefPtr<PopupListBox> create(WebCore::PopupMenuClient* client, bool deviceSupportsTouch) { return adoptRef(new PopupListBox(client, deviceSupportsTouch)); } // FramelessScrollView virtual void paint(WebCore::GraphicsContext*, const WebCore::IntRect&) OVERRIDE; virtual bool handleMouseDownEvent(const WebCore::PlatformMouseEvent&) OVERRIDE; virtual bool handleMouseMoveEvent(const WebCore::PlatformMouseEvent&) OVERRIDE; virtual bool handleMouseReleaseEvent(const WebCore::PlatformMouseEvent&) OVERRIDE; virtual bool handleWheelEvent(const WebCore::PlatformWheelEvent&) OVERRIDE; virtual bool handleKeyEvent(const WebCore::PlatformKeyboardEvent&) OVERRIDE; virtual bool handleTouchEvent(const WebCore::PlatformTouchEvent&) OVERRIDE; virtual bool handleGestureEvent(const WebCore::PlatformGestureEvent&) OVERRIDE; // ScrollView virtual WebCore::HostWindow* hostWindow() const OVERRIDE; virtual bool shouldPlaceVerticalScrollbarOnLeft() const OVERRIDE; // PopupListBox methods // Closes the popup void abandon(); // Updates our internal list to match the client. void updateFromElement(); // Frees any allocated resources used in a particular popup session. void clear(); // Sets the index of the option that is displayed in the <select> widget in the page void setOriginalIndex(int); // Gets the index of the item that the user is currently moused over or has // selected with the keyboard. This is not the same as the original index, // since the user has not yet accepted this input. int selectedIndex() const { return m_selectedIndex; } // Moves selection down/up the given number of items, scrolling if necessary. // Positive is down. The resulting index will be clamped to the range // [0, numItems), and non-option items will be skipped. void adjustSelectedIndex(int delta); // Returns the number of items in the list. int numItems() const { return static_cast<int>(m_items.size()); } void setBaseWidth(int width) { m_baseWidth = std::min(m_maxWindowWidth, width); } // Computes the size of widget and children. virtual void layout() OVERRIDE; // Returns whether the popup wants to process events for the passed key. bool isInterestedInEventForKey(int keyCode); // Gets the height of a row. int getRowHeight(int index); virtual void setMaxHeight(int maxHeight) OVERRIDE { m_maxHeight = maxHeight; } void setMaxWidth(int maxWidth) { m_maxWindowWidth = maxWidth; } virtual void setMaxWidthAndLayout(int) OVERRIDE; void disconnectClient() { m_popupClient = 0; } const Vector<PopupItem*>& items() const { return m_items; } virtual int popupContentHeight() const OVERRIDE; static const int defaultMaxHeight; private: friend class PopupContainer; friend class RefCounted<PopupListBox>; PopupListBox(WebCore::PopupMenuClient*, bool deviceSupportsTouch); virtual ~PopupListBox() { clear(); } // Hides the popup. Other classes should not call this. Use abandon instead. void hidePopup(); // Returns true if the selection can be changed to index. // Disabled items, or labels cannot be selected. bool isSelectableItem(int index); // Select an index in the list, scrolling if necessary. void selectIndex(int index); // Accepts the selected index as the value to be displayed in the <select> // widget on the web page, and closes the popup. Returns true if index is // accepted. bool acceptIndex(int index); // Clears the selection (so no row appears selected). void clearSelection(); // Scrolls to reveal the given index. void scrollToRevealRow(int index); void scrollToRevealSelection() { scrollToRevealRow(m_selectedIndex); } // Invalidates the row at the given index. void invalidateRow(int index); // Get the bounds of a row. WebCore::IntRect getRowBounds(int index); // Converts a point to an index of the row the point is over int pointToRowIndex(const WebCore::IntPoint&); // Paint an individual row void paintRow(WebCore::GraphicsContext*, const WebCore::IntRect&, int rowIndex); // Test if the given point is within the bounds of the popup window. bool isPointInBounds(const WebCore::IntPoint&); // Called when the user presses a text key. Does a prefix-search of the items. void typeAheadFind(const WebCore::PlatformKeyboardEvent&); // Returns the font to use for the given row WebCore::Font getRowFont(int index); // Moves the selection down/up one item, taking care of looping back to the // first/last element if m_loopSelectionNavigation is true. void selectPreviousRow(); void selectNextRow(); // If the device is a touch screen we increase the height of menu items // to make it easier to unambiguously touch them. bool m_deviceSupportsTouch; // This is the index of the item marked as "selected" - i.e. displayed in // the widget on the page. int m_originalIndex; // This is the index of the item that the user is hovered over or has // selected using the keyboard in the list. They have not confirmed this // selection by clicking or pressing enter yet however. int m_selectedIndex; // If >= 0, this is the index we should accept if the popup is "abandoned". // This is used for keyboard navigation, where we want the // selection to change immediately, and is only used if the settings // acceptOnAbandon field is true. int m_acceptedIndexOnAbandon; // This is the number of rows visible in the popup. The maximum number // visible at a time is defined as being kMaxVisibleRows. For a scrolled // popup, this can be thought of as the page size in data units. int m_visibleRows; // Our suggested width, not including scrollbar. int m_baseWidth; // The maximum height we can be without being off-screen. int m_maxHeight; // A list of the options contained within the <select> Vector<PopupItem*> m_items; // The <select> PopupMenuClient that opened us. WebCore::PopupMenuClient* m_popupClient; // The scrollbar which has mouse capture. Mouse events go straight to this // if not null. RefPtr<WebCore::Scrollbar> m_capturingScrollbar; // The last scrollbar that the mouse was over. Used for mouseover highlights. RefPtr<WebCore::Scrollbar> m_lastScrollbarUnderMouse; // The string the user has typed so far into the popup. Used for typeAheadFind. String m_typedString; // The char the user has hit repeatedly. Used for typeAheadFind. UChar m_repeatingChar; // The last time the user hit a key. Used for typeAheadFind. TimeStamp m_lastCharTime; // If width exeeds screen width, we have to clip it. int m_maxWindowWidth; // To forward last mouse release event. RefPtr<WebCore::Element> m_focusedElement; }; } // namespace blink #endif