1/* 2 * This file is part of the WebKit project. 3 * 4 * Copyright (C) 2008-2012 Nokia Corporation and/or its subsidiary(-ies) 5 * 6 * Copyright (C) 2006 Zack Rusin <zack@kde.org> 7 * 2006 Dirk Mueller <mueller@kde.org> 8 * 2006 Nikolas Zimmermann <zimmermann@kde.org> 9 * Copyright (C) 2008 Holger Hans Peter Freyther 10 * 11 * All rights reserved. 12 * 13 * This library is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU Library General Public 15 * License as published by the Free Software Foundation; either 16 * version 2 of the License, or (at your option) any later version. 17 * 18 * This library is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21 * Library General Public License for more details. 22 * 23 * You should have received a copy of the GNU Library General Public License 24 * along with this library; see the file COPYING.LIB. If not, write to 25 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 26 * Boston, MA 02110-1301, USA. 27 * 28 */ 29 30#include "config.h" 31#include "RenderThemeQStyle.h" 32 33#include "CSSFontSelector.h" 34#include "CSSValueKeywords.h" 35#include "Chrome.h" 36#include "ChromeClient.h" 37#include "Color.h" 38#include "Document.h" 39#include "Font.h" 40#include "FontSelector.h" 41#include "GraphicsContext.h" 42#include "HTMLInputElement.h" 43#include "HTMLNames.h" 44#include "LocalizedStrings.h" 45#include "NotImplemented.h" 46#include "Page.h" 47#include "PaintInfo.h" 48#include "QWebPageClient.h" 49#include "RenderBox.h" 50#if ENABLE(PROGRESS_ELEMENT) 51#include "RenderProgress.h" 52#endif 53#include "RenderSlider.h" 54#include "ScrollbarThemeQStyle.h" 55#include "SliderThumbElement.h" 56#include "StyleResolver.h" 57#include "UserAgentStyleSheets.h" 58 59#include <QPainter> 60 61namespace WebCore { 62 63using namespace HTMLNames; 64 65QSharedPointer<StylePainter> RenderThemeQStyle::getStylePainter(const PaintInfo& paintInfo) 66{ 67 return QSharedPointer<StylePainter>(new StylePainterQStyle(this, paintInfo, /*RenderObject*/0)); 68} 69 70StylePainterQStyle::StylePainterQStyle(RenderThemeQStyle* theme, const PaintInfo& paintInfo, RenderObject* renderObject) 71 : StylePainter(theme, paintInfo) 72 , qStyle(theme->qStyle()) 73 , appearance(NoControlPart) 74{ 75 init(paintInfo.context ? paintInfo.context : 0); 76 if (renderObject) 77 appearance = theme->initializeCommonQStyleOptions(styleOption, renderObject); 78} 79 80StylePainterQStyle::StylePainterQStyle(ScrollbarThemeQStyle* theme, GraphicsContext* context) 81 : StylePainter() 82 , qStyle(theme->qStyle()) 83 , appearance(NoControlPart) 84{ 85 init(context); 86} 87 88void StylePainterQStyle::init(GraphicsContext* context) 89{ 90 painter = static_cast<QPainter*>(context->platformContext()); 91 if (QObject* widget = qStyle->widgetForPainter(painter)) { 92 styleOption.palette = widget->property("palette").value<QPalette>(); 93 styleOption.rect = widget->property("rect").value<QRect>(); 94 styleOption.direction = static_cast<Qt::LayoutDirection>(widget->property("layoutDirection").toInt()); 95 } 96 97 StylePainter::init(context); 98} 99 100PassRefPtr<RenderTheme> RenderThemeQStyle::create(Page* page) 101{ 102 return adoptRef(new RenderThemeQStyle(page)); 103} 104 105static QtStyleFactoryFunction styleFactoryFunction; 106 107void RenderThemeQStyle::setStyleFactoryFunction(QtStyleFactoryFunction function) 108{ 109 styleFactoryFunction = function; 110} 111 112QtStyleFactoryFunction RenderThemeQStyle::styleFactory() 113{ 114 return styleFactoryFunction; 115} 116 117RenderThemeQStyle::RenderThemeQStyle(Page* page) 118 : RenderThemeQt(page) 119 , m_qStyle(adoptPtr(styleFactoryFunction(page))) 120{ 121 int buttonPixelSize = 0; 122 m_qStyle->getButtonMetrics(&m_buttonFontFamily, &buttonPixelSize); 123#ifdef Q_WS_MAC 124 m_buttonFontPixelSize = buttonPixelSize; 125#endif 126} 127 128RenderThemeQStyle::~RenderThemeQStyle() 129{ 130} 131 132void RenderThemeQStyle::setPaletteFromPageClientIfExists(QPalette& palette) const 133{ 134 if (!m_page) 135 return; 136 137 ChromeClient* chromeClient = m_page->chrome().client(); 138 if (!chromeClient) 139 return; 140 141 if (QWebPageClient* pageClient = chromeClient->platformPageClient()) 142 palette = pageClient->palette(); 143} 144 145QRect RenderThemeQStyle::inflateButtonRect(const QRect& originalRect) const 146{ 147 QRect layoutRect = m_qStyle->buttonSubElementRect(QStyleFacade::PushButtonLayoutItem, QStyleFacade::State_Small, originalRect); 148 if (!layoutRect.isNull()) { 149 int paddingLeft = layoutRect.left() - originalRect.left(); 150 int paddingRight = originalRect.right() - layoutRect.right(); 151 int paddingTop = layoutRect.top() - originalRect.top(); 152 int paddingBottom = originalRect.bottom() - layoutRect.bottom(); 153 154 return originalRect.adjusted(-paddingLeft, -paddingTop, paddingRight, paddingBottom); 155 } 156 return originalRect; 157} 158 159void RenderThemeQStyle::computeSizeBasedOnStyle(RenderStyle* renderStyle) const 160{ 161 QSize size(0, 0); 162 const QFontMetrics fm(renderStyle->font().syntheticFont()); 163 164 switch (renderStyle->appearance()) { 165 case TextAreaPart: 166 case SearchFieldPart: 167 case TextFieldPart: { 168 int padding = m_qStyle->findFrameLineWidth(); 169 renderStyle->setPaddingLeft(Length(padding, Fixed)); 170 renderStyle->setPaddingRight(Length(padding, Fixed)); 171 renderStyle->setPaddingTop(Length(padding, Fixed)); 172 renderStyle->setPaddingBottom(Length(padding, Fixed)); 173 break; 174 } 175 default: 176 break; 177 } 178 // If the width and height are both specified, then we have nothing to do. 179 if (!renderStyle->width().isIntrinsicOrAuto() && !renderStyle->height().isAuto()) 180 return; 181 182 switch (renderStyle->appearance()) { 183 case CheckboxPart: { 184 int checkBoxWidth = m_qStyle->simplePixelMetric(QStyleFacade::PM_IndicatorWidth, QStyleFacade::State_Small); 185 checkBoxWidth *= renderStyle->effectiveZoom(); 186 size = QSize(checkBoxWidth, checkBoxWidth); 187 break; 188 } 189 case RadioPart: { 190 int radioWidth = m_qStyle->simplePixelMetric(QStyleFacade::PM_ExclusiveIndicatorWidth, QStyleFacade::State_Small); 191 radioWidth *= renderStyle->effectiveZoom(); 192 size = QSize(radioWidth, radioWidth); 193 break; 194 } 195 case PushButtonPart: 196 case ButtonPart: { 197 QSize contentSize = fm.size(Qt::TextShowMnemonic, QString::fromLatin1("X")); 198 QSize pushButtonSize = m_qStyle->pushButtonSizeFromContents(QStyleFacade::State_Small, contentSize); 199 QRect layoutRect = m_qStyle->buttonSubElementRect(QStyleFacade::PushButtonLayoutItem, QStyleFacade::State_Small, QRect(0, 0, pushButtonSize.width(), pushButtonSize.height())); 200 201 // If the style supports layout rects we use that, and compensate accordingly 202 // in paintButton() below. 203 if (!layoutRect.isNull()) 204 size.setHeight(layoutRect.height()); 205 else 206 size.setHeight(pushButtonSize.height()); 207 208 break; 209 } 210 case MenulistPart: { 211 int contentHeight = qMax(fm.lineSpacing(), 14) + 2; 212 QSize menuListSize = m_qStyle->comboBoxSizeFromContents(QStyleFacade::State_Small, QSize(0, contentHeight)); 213 size.setHeight(menuListSize.height()); 214 break; 215 } 216 default: 217 break; 218 } 219 220 // FIXME: Check is flawed, since it doesn't take min-width/max-width into account. 221 if (renderStyle->width().isIntrinsicOrAuto() && size.width() > 0) 222 renderStyle->setMinWidth(Length(size.width(), Fixed)); 223 if (renderStyle->height().isAuto() && size.height() > 0) 224 renderStyle->setMinHeight(Length(size.height(), Fixed)); 225} 226 227 228 229void RenderThemeQStyle::adjustButtonStyle(StyleResolver* styleResolver, RenderStyle* style, Element*) const 230{ 231 // Ditch the border. 232 style->resetBorder(); 233 234#ifdef Q_WS_MAC 235 if (style->appearance() == PushButtonPart) { 236 // The Mac ports ignore the specified height for <input type="button"> elements 237 // unless a border and/or background CSS property is also specified. 238 style->setHeight(Length(Auto)); 239 } 240#endif 241 242 FontDescription fontDescription = style->fontDescription(); 243 fontDescription.setIsAbsoluteSize(true); 244 245#ifdef Q_WS_MAC // Use fixed font size and family on Mac (like Safari does) 246 fontDescription.setSpecifiedSize(m_buttonFontPixelSize); 247 fontDescription.setComputedSize(m_buttonFontPixelSize); 248#else 249 fontDescription.setSpecifiedSize(style->fontSize()); 250 fontDescription.setComputedSize(style->fontSize()); 251#endif 252 253 Vector<AtomicString, 1> families; 254 families.append(m_buttonFontFamily); 255 fontDescription.setFamilies(families); 256 style->setFontDescription(fontDescription); 257 style->font().update(styleResolver->fontSelector()); 258 style->setLineHeight(RenderStyle::initialLineHeight()); 259 setButtonSize(style); 260 setButtonPadding(style); 261} 262 263void RenderThemeQStyle::setButtonPadding(RenderStyle* style) const 264{ 265 // Fake a button rect here, since we're just computing deltas 266 QRect originalRect = QRect(0, 0, 100, 30); 267 268 // Default padding is based on the button margin pixel metric 269 int buttonMargin = m_qStyle->buttonMargin(QStyleFacade::State_Small, originalRect); 270 int paddingLeft = buttonMargin; 271 int paddingRight = buttonMargin; 272 int paddingTop = buttonMargin; 273 int paddingBottom = buttonMargin; 274 275 // Then check if the style uses layout margins 276 QRect layoutRect = m_qStyle->buttonSubElementRect(QStyleFacade::PushButtonLayoutItem, QStyleFacade::State_Small, originalRect); 277 if (!layoutRect.isNull()) { 278 QRect contentsRect = m_qStyle->buttonSubElementRect(QStyleFacade::PushButtonContents, QStyleFacade::State_Small, originalRect); 279 paddingLeft = contentsRect.left() - layoutRect.left(); 280 paddingRight = layoutRect.right() - contentsRect.right(); 281 paddingTop = contentsRect.top() - layoutRect.top(); 282 283 // Can't use this right now because we don't have the baseline to compensate 284 // paddingBottom = layoutRect.bottom() - contentsRect.bottom(); 285 } 286 style->setPaddingLeft(Length(paddingLeft, Fixed)); 287 style->setPaddingRight(Length(paddingRight, Fixed)); 288 style->setPaddingTop(Length(paddingTop, Fixed)); 289 style->setPaddingBottom(Length(paddingBottom, Fixed)); 290} 291 292bool RenderThemeQStyle::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& r) 293{ 294 StylePainterQStyle p(this, i, o); 295 if (!p.isValid()) 296 return true; 297 298 p.styleOption.rect = r; 299 p.styleOption.state |= QStyleFacade::State_Small; 300 301 if (p.appearance == PushButtonPart || p.appearance == ButtonPart) { 302 p.styleOption.rect = inflateButtonRect(p.styleOption.rect); 303 p.paintButton(QStyleFacade::PushButton); 304 } else if (p.appearance == RadioPart) 305 p.paintButton(QStyleFacade::RadioButton); 306 else if (p.appearance == CheckboxPart) 307 p.paintButton(QStyleFacade::CheckBox); 308 309 return false; 310} 311 312bool RenderThemeQStyle::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& r) 313{ 314 StylePainterQStyle p(this, i, o); 315 if (!p.isValid()) 316 return true; 317 318 p.styleOption.rect = r; 319 p.styleOption.state |= QStyleFacade::State_Sunken; 320 321 // Get the correct theme data for a text field 322 if (p.appearance != TextFieldPart 323 && p.appearance != SearchFieldPart 324 && p.appearance != TextAreaPart 325 && p.appearance != ListboxPart) 326 return true; 327 328 // Now paint the text field. 329 p.paintTextField(); 330 return false; 331} 332 333void RenderThemeQStyle::adjustTextAreaStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const 334{ 335 adjustTextFieldStyle(styleResolver, style, element); 336} 337 338bool RenderThemeQStyle::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r) 339{ 340 return paintTextField(o, i, r); 341} 342 343void RenderThemeQStyle::setPopupPadding(RenderStyle* style) const 344{ 345 const int paddingLeft = 4; 346 const int paddingRight = style->width().isFixed() || style->width().isPercent() ? 5 : 8; 347 348 style->setPaddingLeft(Length(paddingLeft, Fixed)); 349 350 int w = m_qStyle->simplePixelMetric(QStyleFacade::PM_ButtonIconSize); 351 style->setPaddingRight(Length(paddingRight + w, Fixed)); 352 353 style->setPaddingTop(Length(2, Fixed)); 354 style->setPaddingBottom(Length(2, Fixed)); 355} 356 357QPalette RenderThemeQStyle::colorPalette() const 358{ 359 QPalette palette = RenderThemeQt::colorPalette(); 360 setPaletteFromPageClientIfExists(palette); 361 return palette; 362} 363 364bool RenderThemeQStyle::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& r) 365{ 366 StylePainterQStyle p(this, i, o); 367 if (!p.isValid()) 368 return true; 369 370 p.styleOption.rect = r; 371 p.paintComboBox(); 372 return false; 373} 374 375void RenderThemeQStyle::adjustMenuListButtonStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const 376{ 377 // WORKAROUND because html.css specifies -webkit-border-radius for <select> so we override it here 378 // see also http://bugs.webkit.org/show_bug.cgi?id=18399 379 style->resetBorderRadius(); 380 381 RenderThemeQt::adjustMenuListButtonStyle(styleResolver, style, e); 382} 383 384bool RenderThemeQStyle::paintMenuListButton(RenderObject* o, const PaintInfo& i, const IntRect& r) 385{ 386 StylePainterQStyle p(this, i, o); 387 if (!p.isValid()) 388 return true; 389 390 p.styleOption.rect = r; 391 p.paintComboBoxArrow(); 392 return false; 393} 394 395#if ENABLE(PROGRESS_ELEMENT) 396double RenderThemeQStyle::animationDurationForProgressBar(RenderProgress* renderProgress) const 397{ 398 if (renderProgress->position() >= 0) 399 return 0; 400 401 IntSize size = renderProgress->pixelSnappedSize(); 402 // FIXME: Until http://bugreports.qt.nokia.com/browse/QTBUG-9171 is fixed, 403 // we simulate one square animating across the progress bar. 404 return (size.width() / m_qStyle->progressBarChunkWidth(size)) * animationRepeatIntervalForProgressBar(renderProgress); 405} 406 407bool RenderThemeQStyle::paintProgressBar(RenderObject* o, const PaintInfo& pi, const IntRect& r) 408{ 409 if (!o->isProgress()) 410 return true; 411 412 StylePainterQStyle p(this, pi, o); 413 if (!p.isValid()) 414 return true; 415 416 p.styleOption.rect = r; 417 RenderProgress* renderProgress = toRenderProgress(o); 418 p.paintProgressBar(renderProgress->position(), renderProgress->animationProgress()); 419 return false; 420} 421#endif 422 423bool RenderThemeQStyle::paintSliderTrack(RenderObject* o, const PaintInfo& pi, const IntRect& r) 424{ 425 StylePainterQStyle p(this, pi, o); 426 if (!p.isValid()) 427 return true; 428 429 const QPoint topLeft = r.location(); 430 p.painter->translate(topLeft); 431 432 p.styleOption.rect = r; 433 p.styleOption.rect.moveTo(QPoint(0, 0)); 434 435 if (p.appearance == SliderVerticalPart) 436 p.styleOption.slider.orientation = Qt::Vertical; 437 438 if (isPressed(o)) 439 p.styleOption.state |= QStyleFacade::State_Sunken; 440 441 // some styles need this to show a highlight on one side of the groove 442 HTMLInputElement* slider = o->node()->toInputElement(); 443 if (slider) { 444 p.styleOption.slider.upsideDown = (p.appearance == SliderHorizontalPart) && !o->style()->isLeftToRightDirection(); 445 // Use the width as a multiplier in case the slider values are <= 1 446 const int width = r.width() > 0 ? r.width() : 100; 447 p.styleOption.slider.maximum = slider->maximum() * width; 448 p.styleOption.slider.minimum = slider->minimum() * width; 449 if (!p.styleOption.slider.upsideDown) 450 p.styleOption.slider.position = slider->valueAsNumber() * width; 451 else 452 p.styleOption.slider.position = p.styleOption.slider.minimum + p.styleOption.slider.maximum - slider->valueAsNumber() * width; 453 } 454 455 p.paintSliderTrack(); 456 457 p.painter->translate(-topLeft); 458 return false; 459} 460 461void RenderThemeQStyle::adjustSliderTrackStyle(StyleResolver*, RenderStyle* style, Element*) const 462{ 463 style->setBoxShadow(nullptr); 464} 465 466bool RenderThemeQStyle::paintSliderThumb(RenderObject* o, const PaintInfo& pi, const IntRect& r) 467{ 468 StylePainterQStyle p(this, pi, o); 469 if (!p.isValid()) 470 return true; 471 472 const QPoint topLeft = r.location(); 473 p.painter->translate(topLeft); 474 475 p.styleOption.rect = r; 476 p.styleOption.rect.moveTo(QPoint(0, 0)); 477 p.styleOption.slider.orientation = Qt::Horizontal; 478 if (p.appearance == SliderThumbVerticalPart) 479 p.styleOption.slider.orientation = Qt::Vertical; 480 if (isPressed(o)) 481 p.styleOption.state |= QStyleFacade::State_Sunken; 482 483 p.paintSliderThumb(); 484 485 p.painter->translate(-topLeft); 486 return false; 487} 488 489void RenderThemeQStyle::adjustSliderThumbStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const 490{ 491 RenderTheme::adjustSliderThumbStyle(styleResolver, style, element); 492 style->setBoxShadow(nullptr); 493} 494 495bool RenderThemeQStyle::paintSearchField(RenderObject* o, const PaintInfo& pi, const IntRect& r) 496{ 497 return paintTextField(o, pi, r); 498} 499 500void RenderThemeQStyle::adjustSearchFieldDecorationStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const 501{ 502 notImplemented(); 503 RenderTheme::adjustSearchFieldDecorationStyle(styleResolver, style, e); 504} 505 506bool RenderThemeQStyle::paintSearchFieldDecoration(RenderObject* o, const PaintInfo& pi, const IntRect& r) 507{ 508 notImplemented(); 509 return RenderTheme::paintSearchFieldDecoration(o, pi, r); 510} 511 512void RenderThemeQStyle::adjustSearchFieldResultsDecorationStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const 513{ 514 notImplemented(); 515 RenderTheme::adjustSearchFieldResultsDecorationStyle(styleResolver, style, e); 516} 517 518bool RenderThemeQStyle::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& pi, const IntRect& r) 519{ 520 notImplemented(); 521 return RenderTheme::paintSearchFieldResultsDecoration(o, pi, r); 522} 523 524#ifndef QT_NO_SPINBOX 525 526bool RenderThemeQStyle::paintInnerSpinButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect) 527{ 528 StylePainterQStyle p(this, paintInfo, o); 529 if (!p.isValid()) 530 return true; 531 532 p.styleOption.rect = rect; 533 p.paintInnerSpinButton(isSpinUpButtonPartPressed(o)); 534 return false; 535} 536#endif 537 538ControlPart RenderThemeQStyle::initializeCommonQStyleOptions(QStyleFacadeOption &option, RenderObject* o) const 539{ 540 // Default bits: no focus, no mouse over, enabled 541 option.state &= ~(QStyleFacade::State_HasFocus | QStyleFacade::State_MouseOver); 542 option.state |= QStyleFacade::State_Enabled; 543 544 if (isReadOnlyControl(o)) 545 // Readonly is supported on textfields. 546 option.state |= QStyleFacade::State_ReadOnly; 547 548 option.direction = Qt::LeftToRight; 549 550 if (isHovered(o)) 551 option.state |= QStyleFacade::State_MouseOver; 552 553 setPaletteFromPageClientIfExists(option.palette); 554 555 if (!isEnabled(o)) { 556 option.palette.setCurrentColorGroup(QPalette::Disabled); 557 option.state &= ~QStyleFacade::State_Enabled; 558 } 559 560 RenderStyle* style = o->style(); 561 if (!style) 562 return NoControlPart; 563 564 ControlPart result = style->appearance(); 565 if (supportsFocus(result) && isFocused(o)) { 566 option.state |= QStyleFacade::State_HasFocus; 567 option.state |= QStyleFacade::State_KeyboardFocusChange; 568 } 569 570 if (style->direction() == WebCore::RTL) 571 option.direction = Qt::RightToLeft; 572 573 switch (result) { 574 case PushButtonPart: 575 case SquareButtonPart: 576 case ButtonPart: 577 case ButtonBevelPart: 578 case ListItemPart: 579 case MenulistButtonPart: 580 case InnerSpinButtonPart: 581 case SearchFieldResultsButtonPart: 582 case SearchFieldCancelButtonPart: { 583 if (isPressed(o)) 584 option.state |= QStyleFacade::State_Sunken; 585 else if (result == PushButtonPart || result == ButtonPart) 586 option.state |= QStyleFacade::State_Raised; 587 break; 588 } 589 case RadioPart: 590 case CheckboxPart: 591 option.state |= (isChecked(o) ? QStyleFacade::State_On : QStyleFacade::State_Off); 592 } 593 594 return result; 595} 596 597void RenderThemeQStyle::adjustSliderThumbSize(RenderStyle* style, Element* element) const 598{ 599 const ControlPart part = style->appearance(); 600 if (part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) { 601 Qt::Orientation orientation = Qt::Horizontal; 602 if (part == SliderThumbVerticalPart) 603 orientation = Qt::Vertical; 604 605 int length = m_qStyle->sliderLength(orientation); 606 int thickness = m_qStyle->sliderThickness(orientation); 607 if (orientation == Qt::Vertical) { 608 style->setWidth(Length(thickness, Fixed)); 609 style->setHeight(Length(length, Fixed)); 610 } else { 611 style->setWidth(Length(length, Fixed)); 612 style->setHeight(Length(thickness, Fixed)); 613 } 614 } else 615 RenderThemeQt::adjustSliderThumbSize(style, element); 616} 617 618} 619 620// vim: ts=4 sw=4 et 621