Beispiel #1
0
void PopupMenuWin::paint(const IntRect& damageRect, HDC hdc)
{
    if (!m_popup)
        return;

    if (!m_DC) {
        m_DC = adoptGDIObject(::CreateCompatibleDC(HWndDC(m_popup)));
        if (!m_DC)
            return;
    }

    if (m_bmp) {
        bool keepBitmap = false;
        BITMAP bitmap;
        if (::GetObject(m_bmp.get(), sizeof(bitmap), &bitmap))
            keepBitmap = bitmap.bmWidth == clientRect().width()
                         && bitmap.bmHeight == clientRect().height();
        if (!keepBitmap)
            m_bmp.clear();
    }
    if (!m_bmp) {
        BitmapInfo bitmapInfo = BitmapInfo::createBottomUp(clientRect().size());
        void* pixels = 0;
        m_bmp = adoptGDIObject(::CreateDIBSection(m_DC.get(), &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0));
        if (!m_bmp)
            return;

        ::SelectObject(m_DC.get(), m_bmp.get());
    }

    GraphicsContext context(m_DC.get());

    int itemCount = client()->listSize();

    // listRect is the damageRect translated into the coordinates of the entire menu list (which is itemCount * m_itemHeight pixels tall)
    IntRect listRect = damageRect;
    listRect.move(IntSize(0, m_scrollOffset * m_itemHeight));

    for (int y = listRect.y(); y < listRect.maxY(); y += m_itemHeight) {
        int index = y / m_itemHeight;

        Color optionBackgroundColor, optionTextColor;
        PopupMenuStyle itemStyle = client()->itemStyle(index);
        if (index == focusedIndex()) {
            optionBackgroundColor = RenderTheme::defaultTheme()->activeListBoxSelectionBackgroundColor();
            optionTextColor = RenderTheme::defaultTheme()->activeListBoxSelectionForegroundColor();
        } else {
            optionBackgroundColor = itemStyle.backgroundColor();
            optionTextColor = itemStyle.foregroundColor();
        }

        // itemRect is in client coordinates
        IntRect itemRect(0, (index - m_scrollOffset) * m_itemHeight, damageRect.width(), m_itemHeight);

        // Draw the background for this menu item
        if (itemStyle.isVisible())
            context.fillRect(itemRect, optionBackgroundColor);

        if (client()->itemIsSeparator(index)) {
            IntRect separatorRect(itemRect.x() + separatorPadding, itemRect.y() + (itemRect.height() - separatorHeight) / 2, itemRect.width() - 2 * separatorPadding, separatorHeight);
            context.fillRect(separatorRect, optionTextColor);
            continue;
        }

        String itemText = client()->itemText(index);

        TextRun textRun(itemText, 0, 0, AllowTrailingExpansion, itemStyle.textDirection(), itemStyle.hasTextDirectionOverride());
        context.setFillColor(optionTextColor);

        FontCascade itemFont = client()->menuStyle().font();
        if (client()->itemIsLabel(index)) {
            auto d = itemFont.fontDescription();
            d.setWeight(d.bolderWeight());
            itemFont = FontCascade(d, itemFont.letterSpacing(), itemFont.wordSpacing());
            itemFont.update(m_popupClient->fontSelector());
        }

        // Draw the item text
        if (itemStyle.isVisible()) {
            int textX = 0;
            if (client()->menuStyle().textDirection() == LTR) {
                textX = std::max<int>(0, client()->clientPaddingLeft() - client()->clientInsetLeft());
                if (RenderTheme::defaultTheme()->popupOptionSupportsTextIndent())
                    textX += minimumIntValueForLength(itemStyle.textIndent(), itemRect.width());
            } else {
                textX = itemRect.width() - client()->menuStyle().font().width(textRun);
                textX = std::min<int>(textX, textX - client()->clientPaddingRight() + client()->clientInsetRight());
                if (RenderTheme::defaultTheme()->popupOptionSupportsTextIndent())
                    textX -= minimumIntValueForLength(itemStyle.textIndent(), itemRect.width());
            }
            int textY = itemRect.y() + itemFont.fontMetrics().ascent() + (itemRect.height() - itemFont.fontMetrics().height()) / 2;
            context.drawBidiText(itemFont, textRun, IntPoint(textX, textY));
        }
    }

    if (m_scrollbar)
        m_scrollbar->paint(context, damageRect);

    HWndDC hWndDC;
    HDC localDC = hdc ? hdc : hWndDC.setHWnd(m_popup);

    ::BitBlt(localDC, damageRect.x(), damageRect.y(), damageRect.width(), damageRect.height(), m_DC.get(), damageRect.x(), damageRect.y(), SRCCOPY);
}
Beispiel #2
0
// The screen that the popup is placed on should be whichever one the popup menu button lies on.
// We fake an hwnd (here we use the popup's hwnd) on top of the button which we can then use to determine the screen.
// We can then proceed with our final position/size calculations.
void PopupMenuWin::calculatePositionAndSize(const IntRect& r, FrameView* v)
{
    // First get the screen coordinates of the popup menu client.
    HWND hostWindow = v->hostWindow()->platformPageClient();
    IntRect absoluteBounds = ((RenderMenuList*)m_popupClient)->absoluteBoundingBoxRect();
    IntRect absoluteScreenCoords(v->contentsToWindow(absoluteBounds.location()), absoluteBounds.size());
    POINT absoluteLocation(absoluteScreenCoords.location());
    if (!::ClientToScreen(hostWindow, &absoluteLocation))
        return;
    absoluteScreenCoords.setLocation(absoluteLocation);

    // Now set the popup menu's location temporarily to these coordinates so we can determine which screen the popup should lie on.
    // We create or move m_popup as necessary.
    if (!m_popup) {
        registerClass();
        DWORD exStyle = WS_EX_LTRREADING;
        m_popup = ::CreateWindowExW(exStyle, kPopupWindowClassName, L"PopupMenu",
                                    WS_POPUP | WS_BORDER,
                                    absoluteScreenCoords.x(), absoluteScreenCoords.y(), absoluteScreenCoords.width(), absoluteScreenCoords.height(),
                                    hostWindow, 0, WebCore::instanceHandle(), this);

        if (!m_popup)
            return;
    } else
        ::MoveWindow(m_popup, absoluteScreenCoords.x(), absoluteScreenCoords.y(), absoluteScreenCoords.width(), absoluteScreenCoords.height(), false);

    FloatRect screen = monitorFromHwnd(m_popup);

    // Now we determine the actual location and measurements of the popup itself.
    // r is in absolute document coordinates, but we want to be in screen coordinates.

    // First, move to WebView coordinates
    IntRect rScreenCoords(v->contentsToWindow(r.location()), r.size());
    if (Page* page = v->frame().page())
        rScreenCoords.scale(page->deviceScaleFactor());

    // Then, translate to screen coordinates
    POINT location(rScreenCoords.location());
    if (!::ClientToScreen(hostWindow, &location))
        return;

    rScreenCoords.setLocation(location);

    // First, determine the popup's height
    int itemCount = client()->listSize();
    m_itemHeight = client()->menuStyle().font().fontMetrics().height() + optionSpacingMiddle;
    int naturalHeight = m_itemHeight * itemCount;
    int popupHeight = std::min(maxPopupHeight, naturalHeight);
    // The popup should show an integral number of items (i.e. no partial items should be visible)
    popupHeight -= popupHeight % m_itemHeight;

    // Next determine its width
    int popupWidth = 0;
    for (int i = 0; i < itemCount; ++i) {
        String text = client()->itemText(i);
        if (text.isEmpty())
            continue;

        FontCascade itemFont = client()->menuStyle().font();
        if (client()->itemIsLabel(i)) {
            auto d = itemFont.fontDescription();
            d.setWeight(d.bolderWeight());
            itemFont = FontCascade(d, itemFont.letterSpacing(), itemFont.wordSpacing());
            itemFont.update(m_popupClient->fontSelector());
        }

        popupWidth = std::max(popupWidth, static_cast<int>(ceilf(itemFont.width(TextRun(text)))));
    }

    if (naturalHeight > maxPopupHeight)
        // We need room for a scrollbar
        popupWidth += ScrollbarTheme::theme().scrollbarThickness(SmallScrollbar);

    // Add padding to align the popup text with the <select> text
    popupWidth += std::max<int>(0, client()->clientPaddingRight() - client()->clientInsetRight()) + std::max<int>(0, client()->clientPaddingLeft() - client()->clientInsetLeft());

    // Leave room for the border
    popupWidth += 2 * popupWindowBorderWidth;
    popupHeight += 2 * popupWindowBorderWidth;

    // The popup should be at least as wide as the control on the page
    popupWidth = std::max(rScreenCoords.width() - client()->clientInsetLeft() - client()->clientInsetRight(), popupWidth);

    // Always left-align items in the popup.  This matches popup menus on the mac.
    int popupX = rScreenCoords.x() + client()->clientInsetLeft();

    IntRect popupRect(popupX, rScreenCoords.maxY(), popupWidth, popupHeight);

    // Check that we don't go off the screen vertically
    if (popupRect.maxY() > screen.height()) {
        // The popup will go off the screen, so try placing it above the client
        if (rScreenCoords.y() - popupRect.height() < 0) {
            // The popup won't fit above, either, so place it whereever's bigger and resize it to fit
            if ((rScreenCoords.y() + rScreenCoords.height() / 2) < (screen.height() / 2)) {
                // Below is bigger
                popupRect.setHeight(screen.height() - popupRect.y());
            } else {
                // Above is bigger
                popupRect.setY(0);
                popupRect.setHeight(rScreenCoords.y());
            }
        } else {
            // The popup fits above, so reposition it
            popupRect.setY(rScreenCoords.y() - popupRect.height());
        }
    }

    // Check that we don't go off the screen horizontally
    if (popupRect.x() + popupRect.width() > screen.width() + screen.x())
        popupRect.setX(screen.x() + screen.width() - popupRect.width());
    if (popupRect.x() < screen.x())
        popupRect.setX(screen.x());

    m_windowRect = popupRect;
    return;
}