/* * 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 * Copyright (C) 2008 Holger Hans Peter Freyther * * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "config.h" #include "RenderThemeQt.h" #include "CSSStyleSelector.h" #include "CSSStyleSheet.h" #include "CSSValueKeywords.h" #include "Chrome.h" #include "ChromeClientQt.h" #include "Color.h" #include "Document.h" #include "Font.h" #include "FontSelector.h" #include "GraphicsContext.h" #include "HTMLInputElement.h" #include "HTMLMediaElement.h" #include "HTMLNames.h" #if USE(QT_MOBILE_THEME) #include "QtMobileWebStyle.h" #endif #if ENABLE(VIDEO) #include "MediaControlElements.h" #endif #include "NotImplemented.h" #include "PaintInfo.h" #include "Page.h" #include "QWebPageClient.h" #include "QtStyleOptionWebComboBox.h" #include "qwebsettings.h" #include "RenderBox.h" #if ENABLE(PROGRESS_TAG) #include "RenderProgress.h" #endif #include "RenderSlider.h" #include "RenderTheme.h" #include "ScrollbarThemeQt.h" #include "TimeRanges.h" #include "UserAgentStyleSheets.h" #include #include #include #include #include #include #include #include #include #include #if ENABLE(PROGRESS_TAG) #include #endif #include #include namespace WebCore { using namespace HTMLNames; inline static void initStyleOption(QWidget *widget, QStyleOption& option) { if (widget) option.initFrom(widget); else { /* If a widget is not directly available for rendering, we fallback to default value for an active widget. */ option.state = QStyle::State_Active | QStyle::State_Enabled; } } // These values all match Safari/Win/Chromium static const float defaultControlFontPixelSize = 13; static const float defaultCancelButtonSize = 9; static const float minCancelButtonSize = 5; static const float maxCancelButtonSize = 21; static const float defaultSearchFieldResultsDecorationSize = 13; static const float minSearchFieldResultsDecorationSize = 9; static const float maxSearchFieldResultsDecorationSize = 30; static const float defaultSearchFieldResultsButtonWidth = 18; #if USE(QT_MOBILE_THEME) namespace { float buttonPaddingLeft = 18; float buttonPaddingRight = 18; float buttonPaddingTop = 2; float buttonPaddingBottom = 3; float menuListPadding = 9; float textFieldPadding = 5; } #endif StylePainter::StylePainter(RenderThemeQt* theme, const PaintInfo& paintInfo) { init(paintInfo.context ? paintInfo.context : 0, theme->qStyle()); } StylePainter::StylePainter(ScrollbarThemeQt* theme, GraphicsContext* context) { init(context, theme->style()); } void StylePainter::init(GraphicsContext* context, QStyle* themeStyle) { 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 = themeStyle; 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); } } PassRefPtr RenderThemeQt::create(Page* page) { return adoptRef(new RenderThemeQt(page)); } PassRefPtr RenderTheme::themeForPage(Page* page) { if (page) return RenderThemeQt::create(page); static RenderTheme* fallback = RenderThemeQt::create(0).releaseRef(); return fallback; } RenderThemeQt::RenderThemeQt(Page* page) : RenderTheme() , m_page(page) , m_lineEdit(0) { 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 #if USE(QT_MOBILE_THEME) m_fallbackStyle = new QtMobileWebStyle; #else m_fallbackStyle = QStyleFactory::create(QLatin1String("windows")); #endif } RenderThemeQt::~RenderThemeQt() { delete m_fallbackStyle; #ifndef QT_NO_LINEEDIT delete m_lineEdit; #endif } #if USE(QT_MOBILE_THEME) bool RenderThemeQt::isControlStyled(const RenderStyle* style, const BorderData& border, const FillLayer& fill, const Color& backgroundColor) const { switch (style->appearance()) { case PushButtonPart: case ButtonPart: case MenulistPart: case SearchFieldPart: case TextFieldPart: case TextAreaPart: // Test the style to see if the UA border and background match. return (style->border() != border || *style->backgroundLayers() != fill || style->visitedDependentColor(CSSPropertyBackgroundColor) != backgroundColor); case CheckboxPart: case RadioPart: return false; default: return RenderTheme::isControlStyled(style, border, fill, backgroundColor); } } int RenderThemeQt::popupInternalPaddingBottom(RenderStyle* style) const { return 1; } #else // Remove this when SearchFieldPart is style-able in RenderTheme::isControlStyled() bool RenderThemeQt::isControlStyled(const RenderStyle* style, const BorderData& border, const FillLayer& fill, const Color& backgroundColor) const { switch (style->appearance()) { case SearchFieldPart: // Test the style to see if the UA border and background match. return (style->border() != border || *style->backgroundLayers() != fill || style->visitedDependentColor(CSSPropertyBackgroundColor) != backgroundColor); default: return RenderTheme::isControlStyled(style, border, fill, backgroundColor); } } #endif // for some widget painting, we need to fallback to Windows style QStyle* RenderThemeQt::fallbackStyle() const { return (m_fallbackStyle) ? m_fallbackStyle : QApplication::style(); } QStyle* RenderThemeQt::qStyle() const { #if USE(QT_MOBILE_THEME) return fallbackStyle(); #endif if (m_page) { QWebPageClient* pageClient = m_page->chrome()->client()->platformPageClient(); if (pageClient) return pageClient->style(); } return QApplication::style(); } String RenderThemeQt::extraDefaultStyleSheet() { String result = RenderTheme::extraDefaultStyleSheet(); #if ENABLE(NO_LISTBOX_RENDERING) result += String(themeQtNoListboxesUserAgentStyleSheet, sizeof(themeQtNoListboxesUserAgentStyleSheet)); #endif return result; } bool RenderThemeQt::supportsHover(const RenderStyle*) const { return true; } bool RenderThemeQt::supportsFocusRing(const RenderStyle* style) const { switch (style->appearance()) { case CheckboxPart: case RadioPart: case PushButtonPart: case SquareButtonPart: case ButtonPart: case ButtonBevelPart: case ListboxPart: case ListItemPart: case MenulistPart: case MenulistButtonPart: case SliderHorizontalPart: case SliderVerticalPart: case SliderThumbHorizontalPart: case SliderThumbVerticalPart: case SearchFieldPart: case SearchFieldResultsButtonPart: case SearchFieldCancelButtonPart: case TextFieldPart: case TextAreaPart: return true; default: return false; } } int RenderThemeQt::baselinePosition(const RenderObject* o) const { if (!o->isBox()) return 0; if (o->style()->appearance() == CheckboxPart || o->style()->appearance() == RadioPart) return toRenderBox(o)->marginTop() + toRenderBox(o)->height() - 2; // Same as in old khtml return RenderTheme::baselinePosition(o); } bool RenderThemeQt::controlSupportsTints(const RenderObject* o) const { if (!isEnabled(o)) return false; // Checkboxes only have tint when checked. if (o->style()->appearance() == CheckboxPart) return isChecked(o); // For now assume other controls have tint if enabled. return true; } bool RenderThemeQt::supportsControlTints() const { return true; } int RenderThemeQt::findFrameLineWidth(QStyle* style) const { #ifndef QT_NO_LINEEDIT if (!m_lineEdit) m_lineEdit = new QLineEdit(); #endif QStyleOptionFrameV2 opt; QWidget* widget = 0; #ifndef QT_NO_LINEEDIT widget = m_lineEdit; #endif return style->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, widget); } static QRect inflateButtonRect(const QRect& originalRect, QStyle* style) { QStyleOptionButton option; option.state |= QStyle::State_Small; option.rect = originalRect; QRect layoutRect = 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); } return originalRect; } void RenderThemeQt::adjustRepaintRect(const RenderObject* o, IntRect& rect) { switch (o->style()->appearance()) { case CheckboxPart: break; case RadioPart: break; case PushButtonPart: case ButtonPart: { QRect inflatedRect = inflateButtonRect(rect, qStyle()); rect = IntRect(inflatedRect.x(), inflatedRect.y(), inflatedRect.width(), inflatedRect.height()); break; } case MenulistPart: break; default: break; } } Color RenderThemeQt::platformActiveSelectionBackgroundColor() const { QPalette pal = QApplication::palette(); setPaletteFromPageClientIfExists(pal); return pal.brush(QPalette::Active, QPalette::Highlight).color(); } Color RenderThemeQt::platformInactiveSelectionBackgroundColor() const { QPalette pal = QApplication::palette(); setPaletteFromPageClientIfExists(pal); return pal.brush(QPalette::Inactive, QPalette::Highlight).color(); } Color RenderThemeQt::platformActiveSelectionForegroundColor() const { QPalette pal = QApplication::palette(); setPaletteFromPageClientIfExists(pal); return pal.brush(QPalette::Active, QPalette::HighlightedText).color(); } Color RenderThemeQt::platformInactiveSelectionForegroundColor() const { QPalette pal = QApplication::palette(); setPaletteFromPageClientIfExists(pal); return pal.brush(QPalette::Inactive, QPalette::HighlightedText).color(); } Color RenderThemeQt::platformFocusRingColor() const { QPalette pal = QApplication::palette(); setPaletteFromPageClientIfExists(pal); return pal.brush(QPalette::Active, QPalette::Highlight).color(); } void RenderThemeQt::systemFont(int, FontDescription&) const { // no-op } Color RenderThemeQt::systemColor(int cssValueId) const { QPalette pal = QApplication::palette(); switch (cssValueId) { case CSSValueButtontext: return pal.brush(QPalette::Active, QPalette::ButtonText).color(); case CSSValueCaptiontext: return pal.brush(QPalette::Active, QPalette::Text).color(); default: return RenderTheme::systemColor(cssValueId); } } int RenderThemeQt::minimumMenuListSize(RenderStyle*) const { const QFontMetrics &fm = QApplication::fontMetrics(); return 7 * fm.width(QLatin1Char('x')); } void RenderThemeQt::computeSizeBasedOnStyle(RenderStyle* renderStyle) const { QSize size(0, 0); const QFontMetrics fm(renderStyle->font().font()); QStyle* style = qStyle(); switch (renderStyle->appearance()) { case TextAreaPart: case SearchFieldPart: case TextFieldPart: { int padding = findFrameLineWidth(style); renderStyle->setPaddingLeft(Length(padding, Fixed)); renderStyle->setPaddingRight(Length(padding, Fixed)); renderStyle->setPaddingTop(Length(padding, Fixed)); renderStyle->setPaddingBottom(Length(padding, Fixed)); break; } default: break; } // If the width and height are both specified, then we have nothing to do. if (!renderStyle->width().isIntrinsicOrAuto() && !renderStyle->height().isAuto()) return; switch (renderStyle->appearance()) { case CheckboxPart: { QStyleOption styleOption; styleOption.state |= QStyle::State_Small; int checkBoxWidth = style->pixelMetric(QStyle::PM_IndicatorWidth, &styleOption); checkBoxWidth *= renderStyle->effectiveZoom(); size = QSize(checkBoxWidth, checkBoxWidth); break; } case RadioPart: { QStyleOption styleOption; styleOption.state |= QStyle::State_Small; int radioWidth = style->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth, &styleOption); radioWidth *= renderStyle->effectiveZoom(); size = QSize(radioWidth, radioWidth); break; } #if !USE(QT_MOBILE_THEME) case PushButtonPart: case ButtonPart: { QStyleOptionButton styleOption; styleOption.state |= QStyle::State_Small; QSize contentSize = fm.size(Qt::TextShowMnemonic, QString::fromLatin1("X")); QSize pushButtonSize = style->sizeFromContents(QStyle::CT_PushButton, &styleOption, contentSize, 0); styleOption.rect = QRect(0, 0, pushButtonSize.width(), pushButtonSize.height()); QRect layoutRect = style->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 = style->sizeFromContents(QStyle::CT_ComboBox, &styleOption, QSize(0, contentHeight), 0); size.setHeight(menuListSize.height()); break; } #endif 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 { computeSizeBasedOnStyle(style); } bool RenderThemeQt::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& r) { return paintButton(o, i, r); } void RenderThemeQt::setRadioSize(RenderStyle* style) const { computeSizeBasedOnStyle(style); } bool RenderThemeQt::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& r) { return paintButton(o, i, r); } void RenderThemeQt::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element*) const { // Ditch the border. style->resetBorder(); #ifdef Q_WS_MAC if (style->appearance() == PushButtonPart) { // The Mac ports ignore the specified height for elements // unless a border and/or background CSS property is also specified. style->setHeight(Length(Auto)); } #endif 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 #if !USE(QT_MOBILE_THEME) FontFamily fontFamily; fontFamily.setFamily(m_buttonFontFamily); fontDescription.setFamily(fontFamily); style->setFontDescription(fontDescription); style->font().update(selector->fontSelector()); #endif style->setLineHeight(RenderStyle::initialLineHeight()); setButtonSize(style); setButtonPadding(style); } void RenderThemeQt::setButtonSize(RenderStyle* style) const { computeSizeBasedOnStyle(style); } #if !USE(QT_MOBILE_THEME) 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 = qStyle()->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 = qStyle()->subElementRect(QStyle::SE_PushButtonLayoutItem, &styleOption, 0); if (!layoutRect.isNull()) { QRect contentsRect = qStyle()->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(); } style->setPaddingLeft(Length(paddingLeft, Fixed)); style->setPaddingRight(Length(paddingRight, Fixed)); style->setPaddingTop(Length(paddingTop, Fixed)); style->setPaddingBottom(Length(paddingBottom, Fixed)); } #else void RenderThemeQt::setButtonPadding(RenderStyle* style) const { if (!style) return; style->setPaddingLeft(Length(buttonPaddingLeft, Fixed)); style->setPaddingRight(Length(buttonPaddingRight, Fixed)); style->setPaddingTop(Length(buttonPaddingTop, Fixed)); style->setPaddingBottom(Length(buttonPaddingBottom, Fixed)); } #endif bool RenderThemeQt::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& r) { StylePainter p(this, i); if (!p.isValid()) return true; QStyleOptionButton option; initStyleOption(p.widget, option); option.rect = r; option.state |= QStyle::State_Small; ControlPart appearance = initializeCommonQStyleOptions(option, o); if (appearance == PushButtonPart || appearance == ButtonPart) { option.rect = inflateButtonRect(option.rect, qStyle()); 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::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const { // Resetting the style like this leads to differences like: // - RenderTextControl {INPUT} at (2,2) size 168x25 [bgcolor=#FFFFFF] border: (2px inset #000000)] // + RenderTextControl {INPUT} at (2,2) size 166x26 // in layout tests when a CSS style is applied that doesn't affect background color, border or // padding. Just worth keeping in mind! style->setBackgroundColor(Color::transparent); style->resetBorder(); style->resetPadding(); computeSizeBasedOnStyle(style); #if USE(QT_MOBILE_THEME) style->setPaddingLeft(Length(textFieldPadding, Fixed)); style->setPaddingRight(Length(textFieldPadding, Fixed)); #endif } bool RenderThemeQt::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& r) { StylePainter p(this, i); if (!p.isValid()) return true; QStyleOptionFrameV2 panel; initStyleOption(p.widget, panel); panel.rect = r; panel.lineWidth = findFrameLineWidth(qStyle()); #if USE(QT_MOBILE_THEME) if (isPressed(o)) panel.state |= QStyle::State_Sunken; #else panel.state |= QStyle::State_Sunken; #endif panel.features = QStyleOptionFrameV2::None; // Get the correct theme data for a text field ControlPart appearance = initializeCommonQStyleOptions(panel, o); if (appearance != TextFieldPart && appearance != SearchFieldPart && appearance != TextAreaPart && appearance != ListboxPart) return true; // Now paint the text field. p.drawPrimitive(QStyle::PE_PanelLineEdit, panel); return false; } void RenderThemeQt::adjustTextAreaStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const { adjustTextFieldStyle(selector, style, element); } bool RenderThemeQt::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r) { return paintTextField(o, i, r); } void RenderThemeQt::adjustMenuListStyle(CSSStyleSelector*, RenderStyle* style, Element*) const { style->resetBorder(); // Height is locked to auto. style->setHeight(Length(Auto)); // White-space is locked to pre style->setWhiteSpace(PRE); computeSizeBasedOnStyle(style); // Add in the padding that we'd like to use. setPopupPadding(style); #if USE(QT_MOBILE_THEME) style->setPaddingLeft(Length(menuListPadding, Fixed)); #endif } void RenderThemeQt::setPopupPadding(RenderStyle* style) const { const int paddingLeft = 4; const int paddingRight = style->width().isFixed() || style->width().isPercent() ? 5 : 8; style->setPaddingLeft(Length(paddingLeft, Fixed)); QStyleOptionComboBox opt; int w = qStyle()->pixelMetric(QStyle::PM_ButtonIconSize, &opt, 0); style->setPaddingRight(Length(paddingRight + w, Fixed)); style->setPaddingTop(Length(2, Fixed)); style->setPaddingBottom(Length(2, Fixed)); } bool RenderThemeQt::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& r) { StylePainter p(this, i); if (!p.isValid()) return true; QtStyleOptionWebComboBox opt(o); initStyleOption(p.widget, opt); initializeCommonQStyleOptions(opt, o); IntRect rect = r; #if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) // QMacStyle makes the combo boxes a little bit smaller to leave space for the focus rect. // Because of it, the combo button is drawn at a point to the left of where it was expect to be and may end up // overlapped with the text. This will force QMacStyle to draw the combo box with the expected width. if (qobject_cast(p.style)) rect.inflateX(3); #endif const QPoint topLeft = rect.location(); p.painter->translate(topLeft); opt.rect.moveTo(QPoint(0, 0)); opt.rect.setSize(rect.size()); p.drawComplexControl(QStyle::CC_ComboBox, opt); p.painter->translate(-topLeft); return false; } void RenderThemeQt::adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const { #if USE(QT_MOBILE_THEME) // Mobile theme uses border radius. #else // WORKAROUND because html.css specifies -webkit-border-radius for element. Node* input = o->node()->shadowAncestorNode(); if (!input->renderer()->isBox()) return false; RenderBox* inputRenderBox = toRenderBox(input->renderer()); IntRect inputContentBox = inputRenderBox->contentBoxRect(); // Make sure the scaled button stays square and will fit in its parent's box. int cancelButtonSize = qMin(inputContentBox.width(), qMin(inputContentBox.height(), r.height())); // Calculate cancel button's coordinates relative to the input element. // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will // be one pixel closer to the bottom of the field. This tends to look better with the text. IntRect cancelButtonRect(o->offsetFromAncestorContainer(inputRenderBox).width(), inputContentBox.y() + (inputContentBox.height() - cancelButtonSize + 1) / 2, cancelButtonSize, cancelButtonSize); IntRect paintingRect = convertToPaintingRect(inputRenderBox, o, cancelButtonRect, r); static Image* cancelImage = Image::loadPlatformResource("searchCancelButton").releaseRef(); static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelButtonPressed").releaseRef(); pi.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage, o->style()->colorSpace(), paintingRect); return false; } void RenderThemeQt::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const { notImplemented(); RenderTheme::adjustSearchFieldDecorationStyle(selector, style, e); } bool RenderThemeQt::paintSearchFieldDecoration(RenderObject* o, const PaintInfo& pi, const IntRect& r) { notImplemented(); return RenderTheme::paintSearchFieldDecoration(o, pi, r); } void RenderThemeQt::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const { notImplemented(); RenderTheme::adjustSearchFieldResultsDecorationStyle(selector, style, e); } bool RenderThemeQt::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& pi, const IntRect& r) { notImplemented(); return RenderTheme::paintSearchFieldResultsDecoration(o, pi, r); } bool RenderThemeQt::supportsFocus(ControlPart appearance) const { switch (appearance) { case PushButtonPart: case ButtonPart: case TextFieldPart: case TextAreaPart: case ListboxPart: case MenulistPart: case RadioPart: case CheckboxPart: case SliderHorizontalPart: case SliderVerticalPart: return true; default: // No for all others... return false; } } void RenderThemeQt::setPaletteFromPageClientIfExists(QPalette& palette) const { #if USE(QT_MOBILE_THEME) static QPalette lightGrayPalette(Qt::lightGray); palette = lightGrayPalette; return; #endif // If the webview has a custom palette, use it if (!m_page) return; Chrome* chrome = m_page->chrome(); if (!chrome) return; ChromeClient* chromeClient = chrome->client(); if (!chromeClient) return; QWebPageClient* pageClient = chromeClient->platformPageClient(); if (!pageClient) return; palette = pageClient->palette(); } ControlPart RenderThemeQt::initializeCommonQStyleOptions(QStyleOption& option, RenderObject* o) const { // Default bits: no focus, no mouse over option.state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver); if (isReadOnlyControl(o)) // Readonly is supported on textfields. option.state |= QStyle::State_ReadOnly; option.direction = Qt::LeftToRight; if (isHovered(o)) option.state |= QStyle::State_MouseOver; setPaletteFromPageClientIfExists(option.palette); if (!isEnabled(o)) { option.palette.setCurrentColorGroup(QPalette::Disabled); option.state &= ~QStyle::State_Enabled; } RenderStyle* style = o->style(); if (!style) return NoControlPart; ControlPart result = style->appearance(); if (supportsFocus(result) && isFocused(o)) { option.state |= QStyle::State_HasFocus; option.state |= QStyle::State_KeyboardFocusChange; } if (style->direction() == WebCore::RTL) option.direction = Qt::RightToLeft; switch (result) { case PushButtonPart: case SquareButtonPart: case ButtonPart: case ButtonBevelPart: case ListItemPart: case MenulistButtonPart: case SearchFieldResultsButtonPart: case SearchFieldCancelButtonPart: { if (isPressed(o)) option.state |= QStyle::State_Sunken; else if (result == PushButtonPart || result == ButtonPart) option.state |= QStyle::State_Raised; break; } case RadioPart: case CheckboxPart: option.state |= (isChecked(o) ? QStyle::State_On : QStyle::State_Off); } return result; } #if ENABLE(VIDEO) String RenderThemeQt::extraMediaControlsStyleSheet() { String result = String(mediaControlsQtUserAgentStyleSheet, sizeof(mediaControlsQtUserAgentStyleSheet)); if (m_page && m_page->chrome()->requiresFullscreenForVideoPlayback()) result += String(mediaControlsQtFullscreenUserAgentStyleSheet, sizeof(mediaControlsQtFullscreenUserAgentStyleSheet)); return result; } // Helper class to transform the painter's world matrix to the object's content area, scaled to 0,0,100,100 class WorldMatrixTransformer { public: WorldMatrixTransformer(QPainter* painter, RenderObject* renderObject, const IntRect& r) : m_painter(painter) { RenderStyle* style = renderObject->style(); m_originalTransform = m_painter->transform(); m_painter->translate(r.x() + style->paddingLeft().value(), r.y() + style->paddingTop().value()); m_painter->scale((r.width() - style->paddingLeft().value() - style->paddingRight().value()) / 100.0, (r.height() - style->paddingTop().value() - style->paddingBottom().value()) / 100.0); } ~WorldMatrixTransformer() { m_painter->setTransform(m_originalTransform); } private: QPainter* m_painter; QTransform m_originalTransform; }; double RenderThemeQt::mediaControlsBaselineOpacity() const { return 0.4; } void RenderThemeQt::paintMediaBackground(QPainter* painter, const IntRect& r) const { painter->setPen(Qt::NoPen); static QColor transparentBlack(0, 0, 0, mediaControlsBaselineOpacity() * 255); painter->setBrush(transparentBlack); painter->drawRoundedRect(r.x(), r.y(), r.width(), r.height(), 5.0, 5.0); } QColor RenderThemeQt::getMediaControlForegroundColor(RenderObject* o) const { QColor fgColor = platformActiveSelectionBackgroundColor(); if (o && o->node()->active()) fgColor = fgColor.lighter(); return fgColor; } bool RenderThemeQt::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) { HTMLMediaElement* mediaElement = toParentMediaElement(o); if (!mediaElement) return false; StylePainter p(this, paintInfo); if (!p.isValid()) return true; p.painter->setRenderHint(QPainter::Antialiasing, true); paintMediaBackground(p.painter, r); WorldMatrixTransformer transformer(p.painter, o, r); const QPointF arrowPolygon[9] = { QPointF(20, 0), QPointF(100, 0), QPointF(100, 80), QPointF(80, 80), QPointF(80, 30), QPointF(10, 100), QPointF(0, 90), QPointF(70, 20), QPointF(20, 20)}; p.painter->setBrush(getMediaControlForegroundColor(o)); p.painter->drawPolygon(arrowPolygon, 9); return false; } bool RenderThemeQt::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) { HTMLMediaElement* mediaElement = toParentMediaElement(o); if (!mediaElement) return false; StylePainter p(this, paintInfo); if (!p.isValid()) return true; p.painter->setRenderHint(QPainter::Antialiasing, true); paintMediaBackground(p.painter, r); WorldMatrixTransformer transformer(p.painter, o, r); const QPointF speakerPolygon[6] = { QPointF(20, 30), QPointF(50, 30), QPointF(80, 0), QPointF(80, 100), QPointF(50, 70), QPointF(20, 70)}; p.painter->setBrush(mediaElement->muted() ? Qt::darkRed : getMediaControlForegroundColor(o)); p.painter->drawPolygon(speakerPolygon, 6); return false; } bool RenderThemeQt::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) { HTMLMediaElement* mediaElement = toParentMediaElement(o); if (!mediaElement) return false; StylePainter p(this, paintInfo); if (!p.isValid()) return true; p.painter->setRenderHint(QPainter::Antialiasing, true); paintMediaBackground(p.painter, r); WorldMatrixTransformer transformer(p.painter, o, r); p.painter->setBrush(getMediaControlForegroundColor(o)); if (mediaElement->canPlay()) { const QPointF playPolygon[3] = { QPointF(0, 0), QPointF(100, 50), QPointF(0, 100)}; p.painter->drawPolygon(playPolygon, 3); } else { p.painter->drawRect(0, 0, 30, 100); p.painter->drawRect(70, 0, 30, 100); } return false; } bool RenderThemeQt::paintMediaSeekBackButton(RenderObject*, const PaintInfo&, const IntRect&) { // We don't want to paint this at the moment. return false; } bool RenderThemeQt::paintMediaSeekForwardButton(RenderObject*, const PaintInfo&, const IntRect&) { // We don't want to paint this at the moment. return false; } bool RenderThemeQt::paintMediaCurrentTime(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) { StylePainter p(this, paintInfo); if (!p.isValid()) return true; p.painter->setRenderHint(QPainter::Antialiasing, true); paintMediaBackground(p.painter, r); return false; } String RenderThemeQt::formatMediaControlsCurrentTime(float currentTime, float duration) const { return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration); } String RenderThemeQt::formatMediaControlsRemainingTime(float currentTime, float duration) const { return String(); } bool RenderThemeQt::paintMediaVolumeSliderTrack(RenderObject *o, const PaintInfo &paintInfo, const IntRect &r) { StylePainter p(this, paintInfo); if (!p.isValid()) return true; p.painter->setRenderHint(QPainter::Antialiasing, true); paintMediaBackground(p.painter, r); if (!o->isSlider()) return false; IntRect b = toRenderBox(o)->contentBoxRect(); // Position the outer rectangle int top = r.y() + b.y(); int left = r.x() + b.x(); int width = b.width(); int height = b.height(); // Get the scale color from the page client QPalette pal = QApplication::palette(); setPaletteFromPageClientIfExists(pal); const QColor highlightText = pal.brush(QPalette::Active, QPalette::HighlightedText).color(); const QColor scaleColor(highlightText.red(), highlightText.green(), highlightText.blue(), mediaControlsBaselineOpacity() * 255); // Draw the outer rectangle p.painter->setBrush(scaleColor); p.painter->drawRect(left, top, width, height); if (!o->node() || !o->node()->hasTagName(inputTag)) return false; HTMLInputElement* slider = static_cast(o->node()); // Position the inner rectangle height = height * slider->valueAsNumber(); top += b.height() - height; // Draw the inner rectangle p.painter->setPen(Qt::NoPen); p.painter->setBrush(getMediaControlForegroundColor(o)); p.painter->drawRect(left, top, width, height); return false; } bool RenderThemeQt::paintMediaVolumeSliderThumb(RenderObject *o, const PaintInfo &paintInfo, const IntRect &r) { StylePainter p(this, paintInfo); if (!p.isValid()) return true; // Nothing to draw here, this is all done in the track return false; } bool RenderThemeQt::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) { HTMLMediaElement* mediaElement = toParentMediaElement(o); if (!mediaElement) return false; StylePainter p(this, paintInfo); if (!p.isValid()) return true; p.painter->setRenderHint(QPainter::Antialiasing, true); paintMediaBackground(p.painter, r); if (MediaPlayer* player = mediaElement->player()) { // Get the buffered parts of the media PassRefPtr buffered = player->buffered(); if (buffered->length() > 0 && player->duration() < std::numeric_limits::infinity()) { // Set the transform and brush WorldMatrixTransformer transformer(p.painter, o, r); p.painter->setBrush(getMediaControlForegroundColor()); // Paint each buffered section ExceptionCode ex; for (int i = 0; i < buffered->length(); i++) { float startX = (buffered->start(i, ex) / player->duration()) * 100; float width = ((buffered->end(i, ex) / player->duration()) * 100) - startX; p.painter->drawRect(startX, 37, width, 26); } } } return false; } bool RenderThemeQt::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) { StylePainter p(this, paintInfo); if (!p.isValid()) return true; p.painter->setRenderHint(QPainter::Antialiasing, true); p.painter->setPen(Qt::NoPen); p.painter->setBrush(getMediaControlForegroundColor(o)); p.painter->drawRect(r.x(), r.y(), r.width(), r.height()); return false; } #endif void RenderThemeQt::adjustSliderThumbSize(RenderObject* o) const { ControlPart part = o->style()->appearance(); if (part == MediaSliderThumbPart) { RenderStyle* parentStyle = o->parent()->style(); Q_ASSERT(parentStyle); int parentHeight = parentStyle->height().value(); o->style()->setWidth(Length(parentHeight / 3, Fixed)); o->style()->setHeight(Length(parentHeight, Fixed)); } else if (part == MediaVolumeSliderThumbPart) { RenderStyle* parentStyle = o->parent()->style(); Q_ASSERT(parentStyle); int parentWidth = parentStyle->width().value(); o->style()->setHeight(Length(parentWidth / 3, Fixed)); o->style()->setWidth(Length(parentWidth, Fixed)); } else if (part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) { QStyleOptionSlider option; if (part == SliderThumbVerticalPart) option.orientation = Qt::Vertical; QStyle* style = qStyle(); int width = style->pixelMetric(QStyle::PM_SliderLength, &option); int height = style->pixelMetric(QStyle::PM_SliderThickness, &option); o->style()->setWidth(Length(width, Fixed)); o->style()->setHeight(Length(height, Fixed)); } } double RenderThemeQt::caretBlinkInterval() const { return QApplication::cursorFlashTime() / 1000.0 / 2.0; } } // vim: ts=4 sw=4 et