/* * This file is part of the theme implementation for form controls in WebCore. * * Copyright (C) 2015 The Qt Company Ltd. * Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies). * * 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 "QStyleFacadeImp.h" #include #include #include #include #include #include #include #include using namespace WebCore; namespace WebKit { static QStyle::State convertToQStyleState(QStyleFacade::State state) { QStyle::State result; #define CONVERT_STATE(ProxiedState, Value) \ if (state & QStyleFacade::ProxiedState) \ result |= QStyle::ProxiedState; FOR_EACH_MAPPED_STATE(CONVERT_STATE, ) #undef CONVERT_STATE return result; } static QStyle::SubControl convertToQStyleSubControl(QStyleFacade::SubControl sc) { #define CONVERT_SUBCONTROL(F, Value) \ case QStyleFacade::F: return QStyle::F switch (sc) { FOR_EACH_SUBCONTROL(CONVERT_SUBCONTROL, SEMICOLON); } Q_UNREACHABLE(); return QStyle::SC_None; #undef CONVERT_SUBCONTROL } static void initGenericStyleOption(QStyleOption* option, QWidget* widget, const QStyleFacadeOption& facadeOption) { if (widget) option->init(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; option->rect = facadeOption.rect; option->state = convertToQStyleState(facadeOption.state); if (facadeOption.direction != Qt::LayoutDirectionAuto) option->direction = facadeOption.direction; option->palette = facadeOption.palette; } static void initSpecificStyleOption(QStyleOption*, const QStyleFacadeOption&) { } static void initSpecificStyleOption(QStyleOptionSlider* sliderOption, const QStyleFacadeOption& facadeOption) { sliderOption->orientation = facadeOption.slider.orientation; sliderOption->upsideDown = facadeOption.slider.upsideDown; sliderOption->minimum = facadeOption.slider.minimum; sliderOption->maximum = facadeOption.slider.maximum; sliderOption->sliderPosition = facadeOption.slider.position; sliderOption->sliderValue = facadeOption.slider.value; sliderOption->singleStep = facadeOption.slider.singleStep; sliderOption->pageStep = facadeOption.slider.pageStep; sliderOption->activeSubControls = convertToQStyleSubControl(facadeOption.slider.activeSubControls); } template class MappedStyleOption : public StyleOption { public: MappedStyleOption(QWidget* widget, const QStyleFacadeOption& facadeOption) { initGenericStyleOption(this, widget, facadeOption); initSpecificStyleOption(this, facadeOption); } }; static QStyle::PixelMetric convertPixelMetric(QStyleFacade::PixelMetric state) { #define CONVERT_METRIC(Metric) \ case QStyleFacade::Metric: return QStyle::Metric switch (state) { FOR_EACH_MAPPED_METRIC(CONVERT_METRIC, SEMICOLON); } Q_UNREACHABLE(); return QStyle::PM_CustomBase; #undef CONVERT_METRIC } static QStyleFacade::SubControl convertToQStyleFacadeSubControl(QStyle::SubControl sc) { #define CONVERT_SUBCONTROL(F, Value) \ case QStyle::F: return QStyleFacade::F switch (sc) { FOR_EACH_SUBCONTROL(CONVERT_SUBCONTROL, SEMICOLON); } Q_UNREACHABLE(); return QStyleFacade::SC_None; #undef CONVERT_SUBCONTROL } QStyleFacadeImp::QStyleFacadeImp(QWebPageAdapter* page) : m_page(page) , m_style(0) { m_fallbackStyle = QStyleFactory::create(QLatin1String("windows")); m_ownFallbackStyle = true; if (!m_fallbackStyle) { m_fallbackStyle = QApplication::style(); m_ownFallbackStyle = false; } } QStyleFacadeImp::~QStyleFacadeImp() { if (m_ownFallbackStyle) delete m_fallbackStyle; } QRect QStyleFacadeImp::buttonSubElementRect(QStyleFacade::ButtonSubElement buttonElement, State state, const QRect& originalRect) const { QStyleOptionButton option; option.state = convertToQStyleState(state); option.rect = originalRect; QStyle::SubElement subElement = QStyle::SE_CustomBase; switch (buttonElement) { case CheckBoxIndicator: subElement = QStyle::SE_CheckBoxIndicator; break; case RadioButtonIndicator: subElement = QStyle::SE_RadioButtonIndicator; break; case PushButtonLayoutItem: subElement = QStyle::SE_PushButtonLayoutItem; break; case PushButtonContents: subElement = QStyle::SE_PushButtonContents; break; default: Q_UNREACHABLE(); } return style()->subElementRect(subElement, &option); } int QStyleFacadeImp::findFrameLineWidth() const { if (!m_lineEdit) m_lineEdit.reset(new QLineEdit()); return style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, m_lineEdit.data()); } int QStyleFacadeImp::simplePixelMetric(QStyleFacade::PixelMetric metric, State state) const { QStyleOption opt; opt.state = convertToQStyleState(state); return style()->pixelMetric(convertPixelMetric(metric), &opt, 0); } int QStyleFacadeImp::buttonMargin(State state, const QRect& originalRect) const { QStyleOptionButton styleOption; styleOption.state = convertToQStyleState(state); styleOption.rect = originalRect; return style()->pixelMetric(QStyle::PM_ButtonMargin, &styleOption, 0); } int QStyleFacadeImp::sliderLength(Qt::Orientation orientation) const { QStyleOptionSlider opt; opt.orientation = orientation; return style()->pixelMetric(QStyle::PM_SliderLength, &opt); } int QStyleFacadeImp::sliderThickness(Qt::Orientation orientation) const { QStyleOptionSlider opt; opt.orientation = orientation; return style()->pixelMetric(QStyle::PM_SliderThickness, &opt); } int QStyleFacadeImp::progressBarChunkWidth(const QSize& size) const { QStyleOptionProgressBar option; option.rect.setSize(size); // FIXME: Until http://bugreports.qt.nokia.com/browse/QTBUG-9171 is fixed, // we simulate one square animating across the progress bar. return style()->pixelMetric(QStyle::PM_ProgressBarChunkWidth, &option); } void QStyleFacadeImp::getButtonMetrics(QString *buttonFontFamily, int *buttonFontPixelSize) const { QPushButton button; QFont defaultButtonFont = QApplication::font(&button); *buttonFontFamily = defaultButtonFont.family(); *buttonFontPixelSize = 0; #ifdef Q_OS_MACOS button.setAttribute(Qt::WA_MacSmallSize); QFontInfo fontInfo(defaultButtonFont); *buttonFontPixelSize = fontInfo.pixelSize(); #endif } QSize QStyleFacadeImp::comboBoxSizeFromContents(State state, const QSize& contentsSize) const { QStyleOptionComboBox opt; opt.state = convertToQStyleState(state); return style()->sizeFromContents(QStyle::CT_ComboBox, &opt, contentsSize); } QSize QStyleFacadeImp::pushButtonSizeFromContents(State state, const QSize& contentsSize) const { QStyleOptionButton opt; opt.state = convertToQStyleState(state); return style()->sizeFromContents(QStyle::CT_PushButton, &opt, contentsSize); } void QStyleFacadeImp::paintButton(QPainter* painter, QStyleFacade::ButtonType type, const QStyleFacadeOption &proxyOption) { QWidget* widget = qobject_cast(widgetForPainter(painter)); MappedStyleOption option(widget, proxyOption); if (option.styleObject) option.styleObject->setProperty("_q_no_animation", true); if (type == PushButton) style()->drawControl(QStyle::CE_PushButton, &option, painter, widget); else if (type == RadioButton) style()->drawControl(QStyle::CE_RadioButton, &option, painter, widget); else if (type == CheckBox) style()->drawControl(QStyle::CE_CheckBox, &option, painter, widget); } void QStyleFacadeImp::paintTextField(QPainter *painter, const QStyleFacadeOption &proxyOption) { QWidget* widget = qobject_cast(widgetForPainter(painter)); MappedStyleOption panel(widget, proxyOption); panel.lineWidth = findFrameLineWidth(); panel.features = QStyleOptionFrame::None; style()->drawPrimitive(QStyle::PE_PanelLineEdit, &panel, painter, widget); } void QStyleFacadeImp::paintComboBox(QPainter *painter, const QStyleFacadeOption &proxyOption) { QWidget* widget = qobject_cast(widgetForPainter(painter)); MappedStyleOption opt(widget, proxyOption); QRect rect = opt.rect; #if defined(Q_OS_MACOS) && !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 (m_style->inherits("QMacStyle")) { rect.setX(rect.x() - 3); rect.setWidth(rect.width() + 2 * 3); } #endif painter->translate(rect.topLeft()); opt.rect.moveTo(QPoint(0, 0)); opt.rect.setSize(rect.size()); style()->drawComplexControl(QStyle::CC_ComboBox, &opt, painter, widget); painter->translate(-rect.topLeft()); } void QStyleFacadeImp::paintComboBoxArrow(QPainter *painter, const QStyleFacadeOption &proxyOption) { QWidget* widget = qobject_cast(widgetForPainter(painter)); MappedStyleOption opt(widget, proxyOption); opt.subControls = QStyle::SC_ComboBoxArrow; // for drawing the combo box arrow, rely only on the fallback style m_fallbackStyle->drawComplexControl(QStyle::CC_ComboBox, &opt, painter, widget); } void QStyleFacadeImp::paintSliderTrack(QPainter* painter, const QStyleFacadeOption& proxyOption) { QWidget* widget = qobject_cast(widgetForPainter(painter)); MappedStyleOption option(widget, proxyOption); option.subControls = QStyle::SC_SliderGroove; option.upsideDown = proxyOption.slider.upsideDown; option.minimum = proxyOption.slider.minimum; option.maximum = proxyOption.slider.maximum; option.sliderPosition = proxyOption.slider.position; option.orientation = proxyOption.slider.orientation; style()->drawComplexControl(QStyle::CC_Slider, &option, painter, widget); if (option.state & QStyle::State_HasFocus) { QStyleOptionFocusRect focusOption; focusOption.rect = option.rect; style()->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOption, painter, widget); } } void QStyleFacadeImp::paintSliderThumb(QPainter* painter, const QStyleFacadeOption& proxyOption) { QWidget* widget = qobject_cast(widgetForPainter(painter)); MappedStyleOption option(widget, proxyOption); option.subControls = QStyle::SC_SliderHandle; if (option.state & QStyle::State_Sunken) option.activeSubControls = QStyle::SC_SliderHandle; style()->drawComplexControl(QStyle::CC_Slider, &option, painter, widget); } void QStyleFacadeImp::paintInnerSpinButton(QPainter* painter, const QStyleFacadeOption& proxyOption, bool spinBoxUp) { QWidget* widget = qobject_cast(widgetForPainter(painter)); MappedStyleOption option(widget, proxyOption); option.subControls = QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown; if (!(option.state & QStyle::State_ReadOnly)) { if (option.state & QStyle::State_Enabled) option.stepEnabled = QAbstractSpinBox::StepUpEnabled | QAbstractSpinBox::StepDownEnabled; if (option.state & QStyle::State_Sunken) { if (spinBoxUp) option.activeSubControls = QStyle::SC_SpinBoxUp; else option.activeSubControls = QStyle::SC_SpinBoxDown; } } QRect buttonRect = option.rect; // Default to moving the buttons a little bit within the editor frame. int inflateX = -2; int inflateY = -2; #if defined(Q_OS_MACOS) && !defined(QT_NO_STYLE_MAC) // QMacStyle will position the aqua buttons flush to the right. // This will move them more within the control for better style, a la // Chromium look & feel. if (m_style->inherits("QMacStyle")) { inflateX = -4; // Render mini aqua spin buttons for QMacStyle to fit nicely into // the editor area, like Chromium. option.state |= QStyle::State_Mini; } #endif buttonRect.setX(buttonRect.x() - inflateX); buttonRect.setWidth(buttonRect.width() + 2 * inflateX); buttonRect.setY(buttonRect.y() - inflateY); buttonRect.setHeight(buttonRect.height() + 2 * inflateY); option.rect = buttonRect; style()->drawComplexControl(QStyle::CC_SpinBox, &option, painter, widget); } void QStyleFacadeImp::paintProgressBar(QPainter* painter, const QStyleFacadeOption& proxyOption, double progress, double animationProgress) { QWidget* widget = qobject_cast(widgetForPainter(painter)); MappedStyleOption option(widget, proxyOption); option.maximum = std::numeric_limits::max(); option.minimum = 0; option.progress = progress * std::numeric_limits::max(); const QPoint topLeft = option.rect.topLeft(); painter->translate(topLeft); option.rect.moveTo(QPoint(0, 0)); if (progress < 0) { // FIXME: Until http://bugreports.qt.nokia.com/browse/QTBUG-9171 is fixed, // we simulate one square animating across the progress bar. style()->drawControl(QStyle::CE_ProgressBarGroove, &option, painter, widget); int chunkWidth = style()->pixelMetric(QStyle::PM_ProgressBarChunkWidth, &option); QColor color = (option.palette.highlight() == option.palette.background()) ? option.palette.color(QPalette::Active, QPalette::Highlight) : option.palette.color(QPalette::Highlight); if (option.direction == Qt::RightToLeft) painter->fillRect(option.rect.right() - chunkWidth - animationProgress * option.rect.width(), 0, chunkWidth, option.rect.height(), color); else painter->fillRect(animationProgress * option.rect.width(), 0, chunkWidth, option.rect.height(), color); } else style()->drawControl(QStyle::CE_ProgressBar, &option, painter, widget); painter->translate(-topLeft); } int QStyleFacadeImp::scrollBarExtent(bool mini) { QStyleOptionSlider o; o.orientation = Qt::Vertical; o.state &= ~QStyle::State_Horizontal; if (mini) o.state |= QStyle::State_Mini; return style()->pixelMetric(QStyle::PM_ScrollBarExtent, &o, 0); } bool QStyleFacadeImp::scrollBarMiddleClickAbsolutePositionStyleHint() const { return style()->styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition); } void QStyleFacadeImp::paintScrollCorner(QPainter* painter, const QRect& rect) { QWidget* widget = qobject_cast(widgetForPainter(painter)); QStyleOption option; option.rect = rect; style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &option, painter, widget); } QStyleFacade::SubControl QStyleFacadeImp::hitTestScrollBar(const QStyleFacadeOption &proxyOption, const QPoint &pos) { MappedStyleOption opt(0, proxyOption); QStyle::SubControl sc = style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, pos, 0); return convertToQStyleFacadeSubControl(sc); } QRect QStyleFacadeImp::scrollBarSubControlRect(const QStyleFacadeOption &proxyOption, QStyleFacade::SubControl subControl) { MappedStyleOption opt(0, proxyOption); return style()->subControlRect(QStyle::CC_ScrollBar, &opt, convertToQStyleSubControl(subControl), 0); } void QStyleFacadeImp::paintScrollBar(QPainter *painter, const QStyleFacadeOption &proxyOption) { QWidget* widget = qobject_cast(widgetForPainter(painter)); MappedStyleOption opt(widget, proxyOption); if (m_style->inherits("QMacStyle")) { // FIXME: Disable transient scrollbar animations on OSX to avoid hiding the whole webview with the scrollbar fade out animation. opt.styleObject = 0; } painter->fillRect(opt.rect, opt.palette.background()); const QPoint topLeft = opt.rect.topLeft(); painter->translate(topLeft); opt.rect.moveTo(QPoint(0, 0)); style()->drawComplexControl(QStyle::CC_ScrollBar, &opt, painter, widget); opt.rect.moveTo(topLeft); } QObject* QStyleFacadeImp::widgetForPainter(QPainter* painter) { QPaintDevice* dev = 0; if (painter) dev = painter->device(); if (dev && dev->devType() == QInternal::Widget) return static_cast(dev); return 0; } QStyle* QStyleFacadeImp::style() const { if (m_style) return m_style; if (m_page) { if (QWebPageClient* pageClient = m_page->client.data()) m_style = pageClient->style(); } if (!m_style) m_style = QApplication::style(); return m_style; } }