Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / web / PopupListBox.cpp
index 1c47a00..da25eac 100644 (file)
 #include "platform/fonts/FontSelector.h"
 #include "platform/geometry/IntRect.h"
 #include "platform/graphics/GraphicsContext.h"
-#include "platform/scroll/FramelessScrollViewClient.h"
+#include "platform/graphics/GraphicsContextStateSaver.h"
 #include "platform/scroll/ScrollbarTheme.h"
 #include "platform/text/StringTruncator.h"
 #include "platform/text/TextRun.h"
 #include "web/PopupContainer.h"
+#include "web/PopupContainerClient.h"
 #include "web/PopupMenuChromium.h"
 #include "wtf/ASCIICType.h"
 #include "wtf/CurrentTime.h"
@@ -66,7 +67,7 @@ static const int maxVisibleRows = 20;
 static const int minEndOfLinePadding = 2;
 static const TimeStamp typeAheadTimeoutMs = 1000;
 
-PopupListBox::PopupListBox(PopupMenuClient* client, bool deviceSupportsTouch)
+PopupListBox::PopupListBox(PopupMenuClient* client, bool deviceSupportsTouch, PopupContainer* container)
     : m_deviceSupportsTouch(deviceSupportsTouch)
     , m_originalIndex(0)
     , m_selectedIndex(0)
@@ -78,13 +79,20 @@ PopupListBox::PopupListBox(PopupMenuClient* client, bool deviceSupportsTouch)
     , m_repeatingChar(0)
     , m_lastCharTime(0)
     , m_maxWindowWidth(std::numeric_limits<int>::max())
+    , m_container(container)
 {
-    setScrollbarModes(ScrollbarAlwaysOff, ScrollbarAlwaysOff);
+}
+
+PopupListBox::~PopupListBox()
+{
+    clear();
+
+    setHasVerticalScrollbar(false);
 }
 
 bool PopupListBox::handleMouseDownEvent(const PlatformMouseEvent& event)
 {
-    Scrollbar* scrollbar = scrollbarAtPoint(event.position());
+    Scrollbar* scrollbar = scrollbarAtWindowPoint(event.position());
     if (scrollbar) {
         m_capturingScrollbar = scrollbar;
         m_capturingScrollbar->mouseDown(event);
@@ -104,7 +112,7 @@ bool PopupListBox::handleMouseMoveEvent(const PlatformMouseEvent& event)
         return true;
     }
 
-    Scrollbar* scrollbar = scrollbarAtPoint(event.position());
+    Scrollbar* scrollbar = scrollbarAtWindowPoint(event.position());
     if (m_lastScrollbarUnderMouse != scrollbar) {
         // Send mouse exited to the old scrollbar.
         if (m_lastScrollbarUnderMouse)
@@ -374,7 +382,17 @@ void PopupListBox::paint(GraphicsContext* gc, const IntRect& rect)
 
     gc->restore();
 
-    ScrollView::paint(gc, rect);
+    if (m_verticalScrollbar) {
+        GraphicsContextStateSaver stateSaver(*gc);
+        IntRect scrollbarDirtyRect = rect;
+        IntRect visibleAreaWithScrollbars(location(), visibleContentRect(IncludeScrollbars).size());
+        scrollbarDirtyRect.intersect(visibleAreaWithScrollbars);
+        gc->translate(x(), y());
+        scrollbarDirtyRect.moveBy(-location());
+        gc->clip(IntRect(IntPoint(), visibleAreaWithScrollbars.size()));
+
+        m_verticalScrollbar->paint(gc, scrollbarDirtyRect);
+    }
 }
 
 static const int separatorPadding = 4;
@@ -467,7 +485,7 @@ void PopupListBox::paintRow(GraphicsContext* gc, const IntRect& rect, int rowInd
     gc->drawBidiText(itemFont, textRunPaintInfo, IntPoint(textX, textY));
 }
 
-Font PopupListBox::getRowFont(int rowIndex)
+Font PopupListBox::getRowFont(int rowIndex) const
 {
     Font itemFont = m_popupClient->itemStyle(rowIndex).font();
     if (m_popupClient->itemIsLabel(rowIndex)) {
@@ -508,7 +526,7 @@ int PopupListBox::pointToRowIndex(const IntPoint& point)
     }
 
     // Last item?
-    if (y < contentsHeight())
+    if (y < contentsSize().height())
         return m_items.size()-1;
 
     return -1;
@@ -568,7 +586,7 @@ void PopupListBox::setOriginalIndex(int index)
     m_originalIndex = m_selectedIndex = index;
 }
 
-int PopupListBox::getRowHeight(int index)
+int PopupListBox::getRowHeight(int index) const
 {
     int minimumHeight = m_deviceSupportsTouch ? optionRowHeightForTouch : minRowHeight;
 
@@ -596,8 +614,7 @@ void PopupListBox::invalidateRow(int index)
     if (index < 0)
         return;
 
-    // Invalidate in the window contents, as FramelessScrollView::invalidateRect
-    // paints in the window coordinates.
+    // Invalidate in the window contents, as invalidateRect paints in the window coordinates.
     IntRect clipRect = contentsToWindow(getRowBounds(index));
     if (shouldPlaceVerticalScrollbarOnLeft() && verticalScrollbar() && !verticalScrollbar()->isOverlayScrollbar())
         clipRect.move(verticalScrollbar()->width(), 0);
@@ -613,10 +630,10 @@ void PopupListBox::scrollToRevealRow(int index)
 
     if (rowRect.y() < scrollY()) {
         // Row is above current scroll position, scroll up.
-        ScrollView::setScrollPosition(IntPoint(0, rowRect.y()));
+        updateScrollbars(IntPoint(0, rowRect.y()));
     } else if (rowRect.maxY() > scrollY() + visibleHeight()) {
         // Row is below current scroll position, scroll down.
-        ScrollView::setScrollPosition(IntPoint(0, rowRect.maxY() - visibleHeight()));
+        updateScrollbars(IntPoint(0, rowRect.maxY() - visibleHeight()));
     }
 }
 
@@ -683,10 +700,9 @@ void PopupListBox::adjustSelectedIndex(int delta)
 void PopupListBox::hidePopup()
 {
     if (parent()) {
-        PopupContainer* container = static_cast<PopupContainer*>(parent());
-        if (container->client())
-            container->client()->popupClosed(container);
-        container->notifyPopupHidden();
+        if (m_container->client())
+            m_container->client()->popupClosed(m_container);
+        m_container->notifyPopupHidden();
     }
 
     if (m_popupClient)
@@ -834,4 +850,201 @@ int PopupListBox::popupContentHeight() const
     return height();
 }
 
+void PopupListBox::invalidateRect(const IntRect& rect)
+{
+    if (HostWindow* h = hostWindow())
+        h->invalidateContentsAndRootView(rect);
+}
+
+IntRect PopupListBox::windowClipRect() const
+{
+    IntRect clipRect = visibleContentRect();
+    if (shouldPlaceVerticalScrollbarOnLeft() && verticalScrollbar() && !verticalScrollbar()->isOverlayScrollbar())
+        clipRect.move(verticalScrollbar()->width(), 0);
+    return contentsToWindow(clipRect);
+}
+
+void PopupListBox::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
+{
+    // Add in our offset within the ScrollView.
+    IntRect dirtyRect = rect;
+    dirtyRect.move(scrollbar->x(), scrollbar->y());
+    invalidateRect(dirtyRect);
+}
+
+bool PopupListBox::isActive() const
+{
+    // FIXME
+    return true;
+}
+
+bool PopupListBox::scrollbarsCanBeActive() const
+{
+    return isActive();
+}
+
+IntRect PopupListBox::scrollableAreaBoundingBox() const
+{
+    return windowClipRect();
+}
+
+// FIXME: The following methods are based on code in ScrollView, with
+// simplifications for the constraints of PopupListBox (e.g. only vertical
+// scrollbar, not horizontal). This functionality should be moved into
+// ScrollableArea after http://crbug.com/417782 is fixed.
+
+void PopupListBox::setHasVerticalScrollbar(bool hasBar)
+{
+    if (hasBar && !m_verticalScrollbar) {
+        m_verticalScrollbar = Scrollbar::create(this, VerticalScrollbar, RegularScrollbar);
+        m_verticalScrollbar->setParent(this);
+        didAddScrollbar(m_verticalScrollbar.get(), VerticalScrollbar);
+        m_verticalScrollbar->styleChanged();
+    } else if (!hasBar && m_verticalScrollbar) {
+        m_verticalScrollbar->setParent(0);
+        willRemoveScrollbar(m_verticalScrollbar.get(), VerticalScrollbar);
+        m_verticalScrollbar = nullptr;
+    }
+}
+
+Scrollbar* PopupListBox::scrollbarAtWindowPoint(const IntPoint& windowPoint)
+{
+    return m_verticalScrollbar && m_verticalScrollbar->frameRect().contains(
+        convertFromContainingWindow(windowPoint)) ? m_verticalScrollbar.get() : 0;
+}
+
+IntRect PopupListBox::contentsToWindow(const IntRect& contentsRect) const
+{
+    IntRect viewRect = contentsRect;
+    viewRect.moveBy(-scrollPosition());
+    return convertToContainingWindow(viewRect);
+}
+
+void PopupListBox::setContentsSize(const IntSize& newSize)
+{
+    if (contentsSize() == newSize)
+        return;
+    m_contentsSize = newSize;
+    updateScrollbars(scrollPosition());
+}
+
+void PopupListBox::setFrameRect(const IntRect& newRect)
+{
+    IntRect oldRect = frameRect();
+    if (newRect == oldRect)
+        return;
+
+    Widget::setFrameRect(newRect);
+    updateScrollbars(scrollPosition());
+    // NOTE: We do not need to call m_verticalScrollbar->frameRectsChanged as
+    // Scrollbar does not implement it.
+}
+
+IntRect PopupListBox::visibleContentRect(IncludeScrollbarsInRect scrollbarInclusion) const
+{
+    // NOTE: Unlike ScrollView we do not need to incorporate any scaling factor,
+    // and there is only one scrollbar to exclude.
+    IntSize size = frameRect().size();
+    Scrollbar* verticalBar = verticalScrollbar();
+    if (scrollbarInclusion == ExcludeScrollbars && verticalBar && !verticalBar->isOverlayScrollbar()) {
+        size.setWidth(std::max(0, size.width() - verticalBar->width()));
+    }
+    return IntRect(m_scrollOffset, size);
+}
+
+void PopupListBox::updateScrollbars(const IntPoint& desiredOffset)
+{
+    IntSize oldVisibleSize = visibleContentRect().size();
+    adjustScrollbarExistence();
+    updateScrollbarGeometry();
+    IntSize newVisibleSize = visibleContentRect().size();
+
+    if (newVisibleSize.width() > oldVisibleSize.width()) {
+        if (shouldPlaceVerticalScrollbarOnLeft())
+            invalidateRect(IntRect(0, 0, newVisibleSize.width() - oldVisibleSize.width(), newVisibleSize.height()));
+        else
+            invalidateRect(IntRect(oldVisibleSize.width(), 0, newVisibleSize.width() - oldVisibleSize.width(), newVisibleSize.height()));
+    }
+    if (desiredOffset != scrollPosition())
+        ScrollableArea::scrollToOffsetWithoutAnimation(desiredOffset);
+}
+
+void PopupListBox::adjustScrollbarExistence()
+{
+    bool needsVerticalScrollbar = contentsSize().height() > visibleHeight();
+    if (!!m_verticalScrollbar != needsVerticalScrollbar) {
+        setHasVerticalScrollbar(needsVerticalScrollbar);
+        contentsResized();
+    }
+}
+
+void PopupListBox::updateScrollbarGeometry()
+{
+    if (m_verticalScrollbar) {
+        int clientHeight = visibleHeight();
+        IntRect oldRect(m_verticalScrollbar->frameRect());
+        IntRect vBarRect(shouldPlaceVerticalScrollbarOnLeft() ? 0 : (width() - m_verticalScrollbar->width()),
+            0, m_verticalScrollbar->width(), height());
+        m_verticalScrollbar->setFrameRect(vBarRect);
+        if (oldRect != m_verticalScrollbar->frameRect())
+            m_verticalScrollbar->invalidate();
+
+        m_verticalScrollbar->setEnabled(contentsSize().height() > clientHeight);
+        m_verticalScrollbar->setProportion(clientHeight, contentsSize().height());
+        m_verticalScrollbar->offsetDidChange();
+        // NOTE: PopupListBox does not support suppressing scrollbars.
+    }
+}
+
+IntPoint PopupListBox::convertChildToSelf(const Widget* child, const IntPoint& point) const
+{
+    // NOTE: m_verticalScrollbar is the only child.
+    IntPoint newPoint = point;
+    newPoint.moveBy(child->location());
+    return newPoint;
+}
+
+IntPoint PopupListBox::convertSelfToChild(const Widget* child, const IntPoint& point) const
+{
+    // NOTE: m_verticalScrollbar is the only child.
+    IntPoint newPoint = point;
+    newPoint.moveBy(-child->location());
+    return newPoint;
+}
+
+int PopupListBox::scrollSize(ScrollbarOrientation orientation) const
+{
+    return (orientation == HorizontalScrollbar || !m_verticalScrollbar) ?
+        0 : m_verticalScrollbar->totalSize() - m_verticalScrollbar->visibleSize();
+}
+
+void PopupListBox::setScrollOffset(const IntPoint& newOffset)
+{
+    // NOTE: We do not support any "fast path" for scrolling. When the scroll
+    // offset changes, we just repaint the whole popup.
+    IntSize scrollDelta = newOffset - m_scrollOffset;
+    if (scrollDelta == IntSize())
+        return;
+    m_scrollOffset = newOffset;
+
+    if (HostWindow* window = hostWindow()) {
+        IntRect clipRect = windowClipRect();
+        IntRect updateRect = clipRect;
+        updateRect.intersect(convertToContainingWindow(IntRect((shouldPlaceVerticalScrollbarOnLeft() && verticalScrollbar()) ? verticalScrollbar()->width() : 0, 0, visibleWidth(), visibleHeight())));
+        window->invalidateContentsForSlowScroll(updateRect);
+    }
+}
+
+IntPoint PopupListBox::maximumScrollPosition() const
+{
+    IntPoint maximumOffset(contentsSize().width() - visibleWidth() - scrollOrigin().x(), contentsSize().height() - visibleHeight() - scrollOrigin().y());
+    maximumOffset.clampNegativeToZero();
+    return maximumOffset;
+}
+
+IntPoint PopupListBox::minimumScrollPosition() const
+{
+    return IntPoint(-scrollOrigin().x(), -scrollOrigin().y());
+}
+
 } // namespace blink