From 1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Wed, 17 Dec 2008 18:05:15 -0800 Subject: Code drop from //branches/cupcake/...@124589 --- WebCore/platform/qt/RenderThemeQt.cpp | 773 ++++++++++++++++++++++++---------- 1 file changed, 561 insertions(+), 212 deletions(-) (limited to 'WebCore/platform/qt/RenderThemeQt.cpp') diff --git a/WebCore/platform/qt/RenderThemeQt.cpp b/WebCore/platform/qt/RenderThemeQt.cpp index dbf080f..2a33e45 100644 --- a/WebCore/platform/qt/RenderThemeQt.cpp +++ b/WebCore/platform/qt/RenderThemeQt.cpp @@ -1,6 +1,8 @@ /* * This file is part of the WebKit project. * + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * * Copyright (C) 2006 Zack Rusin * 2006 Dirk Mueller * 2006 Nikolas Zimmermann @@ -34,21 +36,71 @@ #include #include #include -#include +#include #include #include +#include +#include #include #include #include "Color.h" +#include "CSSStyleSheet.h" #include "Document.h" #include "Page.h" #include "Font.h" #include "RenderTheme.h" #include "GraphicsContext.h" +#include "HTMLMediaElement.h" +#include "HTMLNames.h" namespace WebCore { +using namespace HTMLNames; + + +StylePainter::StylePainter(const RenderObject::PaintInfo& paintInfo) +{ + init(paintInfo.context ? paintInfo.context : 0); +} + +StylePainter::StylePainter(GraphicsContext* context) +{ + init(context); +} + +void StylePainter::init(GraphicsContext* context) +{ + painter = static_cast(context->platformContext()); + widget = 0; + QPaintDevice* dev = 0; + if (painter) + dev = painter->device(); + if (dev && dev->devType() == QInternal::Widget) + widget = static_cast(dev); + style = (widget ? widget->style() : QApplication::style()); + + if (painter) { + // the styles often assume being called with a pristine painter where no brush is set, + // so reset it manually + oldBrush = painter->brush(); + painter->setBrush(Qt::NoBrush); + + // painting the widget with anti-aliasing will make it blurry + // disable it here and restore it later + oldAntialiasing = painter->testRenderHint(QPainter::Antialiasing); + painter->setRenderHint(QPainter::Antialiasing, false); + } +} + +StylePainter::~StylePainter() +{ + if (painter) { + painter->setBrush(oldBrush); + painter->setRenderHints(QPainter::Antialiasing, oldAntialiasing); + } +} + RenderTheme* theme() { static RenderThemeQt rt; @@ -58,6 +110,33 @@ RenderTheme* theme() RenderThemeQt::RenderThemeQt() : RenderTheme() { + QPushButton button; + button.setAttribute(Qt::WA_MacSmallSize); + QFont defaultButtonFont = QApplication::font(&button); + QFontInfo fontInfo(defaultButtonFont); + m_buttonFontFamily = defaultButtonFont.family(); +#ifdef Q_WS_MAC + m_buttonFontPixelSize = fontInfo.pixelSize(); +#endif + + m_fallbackStyle = 0; +} + +RenderThemeQt::~RenderThemeQt() +{ + delete m_fallbackStyle; +} + +// for some widget painting, we need to fallback to Windows style +QStyle* RenderThemeQt::fallbackStyle() +{ + if(!m_fallbackStyle) + m_fallbackStyle = QStyleFactory::create(QLatin1String("windows")); + + if(!m_fallbackStyle) + m_fallbackStyle = QApplication::style(); + + return m_fallbackStyle; } bool RenderThemeQt::supportsHover(const RenderStyle*) const @@ -67,13 +146,13 @@ bool RenderThemeQt::supportsHover(const RenderStyle*) const bool RenderThemeQt::supportsFocusRing(const RenderStyle* style) const { - return supportsFocus(style->appearance()); + return true; // Qt provides this through the style } -short RenderThemeQt::baselinePosition(const RenderObject* o) const +int RenderThemeQt::baselinePosition(const RenderObject* o) const { - if (o->style()->appearance() == CheckboxAppearance || - o->style()->appearance() == RadioAppearance) + if (o->style()->appearance() == CheckboxPart || + o->style()->appearance() == RadioPart) return o->marginTop() + o->height() - 2; // Same as in old khtml return RenderTheme::baselinePosition(o); } @@ -84,7 +163,7 @@ bool RenderThemeQt::controlSupportsTints(const RenderObject* o) const return false; // Checkboxes only have tint when checked. - if (o->style()->appearance() == CheckboxAppearance) + if (o->style()->appearance() == CheckboxPart) return isChecked(o); // For now assume other controls have tint if enabled. @@ -96,20 +175,42 @@ bool RenderThemeQt::supportsControlTints() const return true; } +static QRect inflateButtonRect(const QRect& originalRect) +{ + QStyleOptionButton option; + option.state |= QStyle::State_Small; + option.rect = originalRect; + + QRect layoutRect = QApplication::style()->subElementRect(QStyle::SE_PushButtonLayoutItem, + &option, 0); + if (!layoutRect.isNull()) { + int paddingLeft = layoutRect.left() - originalRect.left(); + int paddingRight = originalRect.right() - layoutRect.right(); + int paddingTop = layoutRect.top() - originalRect.top(); + int paddingBottom = originalRect.bottom() - layoutRect.bottom(); + + return originalRect.adjusted(-paddingLeft, -paddingTop, paddingRight, paddingBottom); + } else { + return originalRect; + } +} + void RenderThemeQt::adjustRepaintRect(const RenderObject* o, IntRect& r) { switch (o->style()->appearance()) { - case CheckboxAppearance: { + case CheckboxPart: { break; } - case RadioAppearance: { + case RadioPart: { break; } - case PushButtonAppearance: - case ButtonAppearance: { + case PushButtonPart: + case ButtonPart: { + QRect inflatedRect = inflateButtonRect(r); + r = IntRect(inflatedRect.x(), inflatedRect.y(), inflatedRect.width(), inflatedRect.height()); break; } - case MenulistAppearance: { + case MenulistPart: { break; } default: @@ -118,19 +219,17 @@ void RenderThemeQt::adjustRepaintRect(const RenderObject* o, IntRect& r) } bool RenderThemeQt::isControlStyled(const RenderStyle* style, const BorderData& border, - const BackgroundLayer& background, const Color& backgroundColor) const + const FillLayer& background, const Color& backgroundColor) const { - if (style->appearance() == TextFieldAppearance || style->appearance() == TextAreaAppearance) + if (style->appearance() == TextFieldPart + || style->appearance() == TextAreaPart + || style->appearance() == ListboxPart) { return style->border() != border; + } return RenderTheme::isControlStyled(style, border, background, backgroundColor); } -void RenderThemeQt::paintResizeControl(GraphicsContext*, const IntRect&) -{ -} - - Color RenderThemeQt::platformActiveSelectionBackgroundColor() const { QPalette pal = QApplication::palette(); @@ -166,43 +265,111 @@ int RenderThemeQt::minimumMenuListSize(RenderStyle*) const return 7 * fm.width(QLatin1Char('x')); } -void RenderThemeQt::adjustSliderThumbSize(RenderObject* o) const +static void computeSizeBasedOnStyle(RenderStyle* renderStyle) { - RenderTheme::adjustSliderThumbSize(o); -} + // If the width and height are both specified, then we have nothing to do. + if (!renderStyle->width().isIntrinsicOrAuto() && !renderStyle->height().isAuto()) + return; -bool RenderThemeQt::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) -{ - return paintButton(o, i, r); + QSize size(0, 0); + const QFontMetrics fm(renderStyle->font().font()); + QStyle* applicationStyle = QApplication::style(); + + switch (renderStyle->appearance()) { + case CheckboxPart: { + QStyleOption styleOption; + styleOption.state |= QStyle::State_Small; + int checkBoxWidth = applicationStyle->pixelMetric(QStyle::PM_IndicatorWidth, + &styleOption); + size = QSize(checkBoxWidth, checkBoxWidth); + break; + } + case RadioPart: { + QStyleOption styleOption; + styleOption.state |= QStyle::State_Small; + int radioWidth = applicationStyle->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth, + &styleOption); + size = QSize(radioWidth, radioWidth); + break; + } + case PushButtonPart: + case ButtonPart: { + QStyleOptionButton styleOption; + styleOption.state |= QStyle::State_Small; + QSize contentSize = fm.size(Qt::TextShowMnemonic, QString::fromLatin1("X")); + QSize pushButtonSize = applicationStyle->sizeFromContents(QStyle::CT_PushButton, + &styleOption, + contentSize, + 0); + styleOption.rect = QRect(0, 0, pushButtonSize.width(), pushButtonSize.height()); + QRect layoutRect = applicationStyle->subElementRect(QStyle::SE_PushButtonLayoutItem, + &styleOption, + 0); + // If the style supports layout rects we use that, and + // compensate accordingly in paintButton() below. + if (!layoutRect.isNull()) { + size.setHeight(layoutRect.height()); + } else { + size.setHeight(pushButtonSize.height()); + } + + break; + } + case MenulistPart: { + QStyleOptionComboBox styleOption; + styleOption.state |= QStyle::State_Small; + int contentHeight = qMax(fm.lineSpacing(), 14) + 2; + QSize menuListSize = applicationStyle->sizeFromContents(QStyle::CT_ComboBox, + &styleOption, + QSize(0, contentHeight), + 0); + size.setHeight(menuListSize.height()); + break; + } + case TextFieldPart: { + const int verticalMargin = 1; + const int horizontalMargin = 2; + int h = qMax(fm.lineSpacing(), 14) + 2*verticalMargin; + int w = fm.width(QLatin1Char('x')) * 17 + 2*horizontalMargin; + QStyleOptionFrameV2 opt; + opt.lineWidth = applicationStyle->pixelMetric(QStyle::PM_DefaultFrameWidth, + &opt, 0); + QSize sz = applicationStyle->sizeFromContents(QStyle::CT_LineEdit, + &opt, + QSize(w, h).expandedTo(QApplication::globalStrut()), + 0); + size.setHeight(sz.height()); + break; + } + default: + break; + } + + // FIXME: Check is flawed, since it doesn't take min-width/max-width into account. + if (renderStyle->width().isIntrinsicOrAuto() && size.width() > 0) + renderStyle->setWidth(Length(size.width(), Fixed)); + if (renderStyle->height().isAuto() && size.height() > 0) + renderStyle->setHeight(Length(size.height(), Fixed)); } void RenderThemeQt::setCheckboxSize(RenderStyle* style) const { - // If the width and height are both specified, then we have nothing to do. - if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) - return; - - // FIXME: A hard-coded size of 13 is used. This is wrong but necessary for now. It matches Firefox. - // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for - // the higher DPI. Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's - // metrics. - const int ff = 13; - if (style->width().isIntrinsicOrAuto()) - style->setWidth(Length(ff, Fixed)); - - if (style->height().isAuto()) - style->setHeight(Length(ff, Fixed)); + computeSizeBasedOnStyle(style); } -bool RenderThemeQt::paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) +bool RenderThemeQt::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { return paintButton(o, i, r); } void RenderThemeQt::setRadioSize(RenderStyle* style) const { - // This is the same as checkboxes. - setCheckboxSize(style); + computeSizeBasedOnStyle(style); +} + +bool RenderThemeQt::paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) +{ + return paintButton(o, i, r); } void RenderThemeQt::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const @@ -216,72 +383,140 @@ void RenderThemeQt::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* s // White-space is locked to pre style->setWhiteSpace(PRE); - setButtonSize(style); + FontDescription fontDescription = style->fontDescription(); + fontDescription.setIsAbsoluteSize(true); + +#ifdef Q_WS_MAC // Use fixed font size and family on Mac (like Safari does) + fontDescription.setSpecifiedSize(m_buttonFontPixelSize); + fontDescription.setComputedSize(m_buttonFontPixelSize); +#else + fontDescription.setSpecifiedSize(style->fontSize()); + fontDescription.setComputedSize(style->fontSize()); +#endif + FontFamily fontFamily; + fontFamily.setFamily(m_buttonFontFamily); + fontDescription.setFamily(fontFamily); + style->setFontDescription(fontDescription); + style->setLineHeight(RenderStyle::initialLineHeight()); + + setButtonSize(style); setButtonPadding(style); + + style->setColor(QApplication::palette().text().color()); } -bool RenderThemeQt::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) +void RenderThemeQt::setButtonSize(RenderStyle* style) const { - QStyle* style = 0; - QPainter* painter = 0; - QWidget* widget = 0; + computeSizeBasedOnStyle(style); +} - if (!getStylePainterAndWidgetFromPaintInfo(i, style, painter, widget)) - return true; +void RenderThemeQt::setButtonPadding(RenderStyle* style) const +{ + QStyleOptionButton styleOption; + styleOption.state |= QStyle::State_Small; + + // Fake a button rect here, since we're just computing deltas + QRect originalRect = QRect(0, 0, 100, 30); + styleOption.rect = originalRect; + + // Default padding is based on the button margin pixel metric + int buttonMargin = QApplication::style()->pixelMetric(QStyle::PM_ButtonMargin, + &styleOption, 0); + int paddingLeft = buttonMargin; + int paddingRight = buttonMargin; + int paddingTop = 1; + int paddingBottom = 0; + + // Then check if the style uses layout margins + QRect layoutRect = QApplication::style()->subElementRect(QStyle::SE_PushButtonLayoutItem, + &styleOption, 0); + if (!layoutRect.isNull()) { + QRect contentsRect = QApplication::style()->subElementRect(QStyle::SE_PushButtonContents, + &styleOption, 0); + paddingLeft = contentsRect.left() - layoutRect.left(); + paddingRight = layoutRect.right() - contentsRect.right(); + paddingTop = contentsRect.top() - layoutRect.top(); + + // Can't use this right now because we don't have the baseline to compensate + // paddingBottom = layoutRect.bottom() - contentsRect.bottom(); + } - QStyleOptionButton option; - if (widget) - option.initFrom(widget); - option.rect = r; + style->setPaddingLeft(Length(paddingLeft, Fixed)); + style->setPaddingRight(Length(paddingRight, Fixed)); + style->setPaddingTop(Length(paddingTop, Fixed)); + style->setPaddingBottom(Length(paddingBottom, Fixed)); +} - // Get the correct theme data for a button - EAppearance appearance = applyTheme(option, o); +bool RenderThemeQt::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) +{ + StylePainter p(i); + if (!p.isValid()) + return true; - if(appearance == PushButtonAppearance || appearance == ButtonAppearance) - style->drawControl(QStyle::CE_PushButton, &option, painter); - else if(appearance == RadioAppearance) - style->drawPrimitive(QStyle::PE_IndicatorRadioButton, &option, painter, widget); - else if(appearance == CheckboxAppearance) - style->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter, widget); + QStyleOptionButton option; + if (p.widget) + option.initFrom(p.widget); + + option.rect = r; + option.state |= QStyle::State_Small; + + ControlPart appearance = applyTheme(option, o); + if(appearance == PushButtonPart || appearance == ButtonPart) { + option.rect = inflateButtonRect(option.rect); + p.drawControl(QStyle::CE_PushButton, option); + } else if(appearance == RadioPart) { + p.drawControl(QStyle::CE_RadioButton, option); + } else if(appearance == CheckboxPart) { + p.drawControl(QStyle::CE_CheckBox, option); + } return false; } -void RenderThemeQt::setButtonSize(RenderStyle* style) const +void RenderThemeQt::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const { - setPrimitiveSize(style); + style->setBackgroundColor(Color::transparent); + style->setColor(QApplication::palette().text().color()); } bool RenderThemeQt::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { - QStyle* style = 0; - QPainter* painter = 0; - QWidget* widget = 0; - - if (!getStylePainterAndWidgetFromPaintInfo(i, style, painter, widget)) + StylePainter p(i); + if (!p.isValid()) return true; QStyleOptionFrameV2 panel; - if (widget) - panel.initFrom(widget); + if (p.widget) + panel.initFrom(p.widget); + panel.rect = r; + panel.lineWidth = p.style->pixelMetric(QStyle::PM_DefaultFrameWidth, &panel, p.widget); panel.state |= QStyle::State_Sunken; panel.features = QStyleOptionFrameV2::None; - // Get the correct theme data for a button - EAppearance appearance = applyTheme(panel, o); - Q_ASSERT(appearance == TextFieldAppearance || appearance == SearchFieldAppearance); + // Get the correct theme data for a text field + ControlPart appearance = applyTheme(panel, o); + if (appearance != TextFieldPart + && appearance != SearchFieldPart + && appearance != TextAreaPart + && appearance != ListboxPart) + return true; // Now paint the text field. - style->drawPrimitive(QStyle::PE_PanelLineEdit, &panel, painter, widget); - style->drawPrimitive(QStyle::PE_FrameLineEdit, &panel, painter, widget); - + p.drawPrimitive(QStyle::PE_PanelLineEdit, panel); + return false; } -void RenderThemeQt::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +void RenderThemeQt::adjustTextAreaStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const +{ + adjustTextFieldStyle(selector, style, element); +} + +bool RenderThemeQt::paintTextArea(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { + return paintTextField(o, i, r); } void RenderThemeQt::adjustMenuListStyle(CSSStyleSelector*, RenderStyle* style, Element*) const @@ -294,55 +529,91 @@ void RenderThemeQt::adjustMenuListStyle(CSSStyleSelector*, RenderStyle* style, E // White-space is locked to pre style->setWhiteSpace(PRE); - setPrimitiveSize(style); + computeSizeBasedOnStyle(style); // Add in the padding that we'd like to use. setPopupPadding(style); - // Our font is locked to the appropriate system font size for the control. To clarify, we first use the CSS-specified font to figure out - // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate - // system font for the control size instead. - //setFontFromControlSize(selector, style); + style->setColor(QApplication::palette().text().color()); } -bool RenderThemeQt::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) +void RenderThemeQt::setPopupPadding(RenderStyle* style) const { - QStyle* style = 0; - QPainter* painter = 0; - QWidget* widget = 0; + const int padding = 8; + style->setPaddingLeft(Length(padding, Fixed)); - if (!getStylePainterAndWidgetFromPaintInfo(i, style, painter, widget)) + QStyleOptionComboBox opt; + int w = QApplication::style()->pixelMetric(QStyle::PM_ButtonIconSize, &opt, 0); + style->setPaddingRight(Length(padding + w, Fixed)); + + style->setPaddingTop(Length(2, Fixed)); + style->setPaddingBottom(Length(0, Fixed)); +} + + +bool RenderThemeQt::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) +{ + StylePainter p(i); + if (!p.isValid()) return true; QStyleOptionComboBox opt; - if (widget) - opt.initFrom(widget); - EAppearance appearance = applyTheme(opt, o); + if (p.widget) + opt.initFrom(p.widget); + ControlPart appearance = applyTheme(opt, o); + const QPoint topLeft = r.topLeft(); - painter->translate(topLeft); + p.painter->translate(topLeft); opt.rect.moveTo(QPoint(0,0)); opt.rect.setSize(r.size()); opt.frame = false; - style->drawComplexControl(QStyle::CC_ComboBox, &opt, painter, widget); - painter->translate(-topLeft); + p.drawComplexControl(QStyle::CC_ComboBox, opt); + p.painter->translate(-topLeft); return false; } - -bool RenderThemeQt::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& pi, - const IntRect& r) +void RenderThemeQt::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, + Element* e) const { - notImplemented(); - return RenderTheme::paintMenuListButton(o, pi, r); + // WORKAROUND because html4.css specifies -webkit-border-radius for