1/* 2 * This file is part of the WebKit project. 3 * 4 * Copyright (C) 2008 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 "RenderThemeQt.h" 32 33#include "CSSValueKeywords.h" 34#include "Chrome.h" 35#include "ChromeClient.h" 36#include "Color.h" 37#include "ExceptionCodePlaceholder.h" 38#include "FileList.h" 39#include "Font.h" 40#include "FontSelector.h" 41#include "GraphicsContext.h" 42#include "HTMLInputElement.h" 43#include "HTMLMediaElement.h" 44#include "HTMLNames.h" 45#include "LocalizedStrings.h" 46#if ENABLE(VIDEO) 47#include "MediaControlElements.h" 48#endif 49#include "NotImplemented.h" 50#include "Page.h" 51#include "PaintInfo.h" 52#include "RenderBox.h" 53#if ENABLE(PROGRESS_ELEMENT) 54#include "RenderProgress.h" 55#endif 56#include "RenderTheme.h" 57#include "RenderThemeQtMobile.h" 58#include "ScrollbarTheme.h" 59#include "StyleResolver.h" 60#include "TimeRanges.h" 61#include "UserAgentStyleSheets.h" 62#include <wtf/text/StringBuilder.h> 63 64#include <QColor> 65#include <QFile> 66#include <QFontMetrics> 67#include <QGuiApplication> 68 69#include <QStyleHints> 70 71namespace WebCore { 72 73using namespace HTMLNames; 74 75// These values all match Safari/Win/Chromium 76static const float defaultControlFontPixelSize = 13; 77static const float defaultCancelButtonSize = 9; 78static const float minCancelButtonSize = 5; 79static const float maxCancelButtonSize = 21; 80static const float defaultSearchFieldResultsDecorationSize = 13; 81static const float minSearchFieldResultsDecorationSize = 9; 82static const float maxSearchFieldResultsDecorationSize = 30; 83static const float defaultSearchFieldResultsButtonWidth = 18; 84 85static QtThemeFactoryFunction themeFactory; 86static ScrollbarTheme* scrollbarTheme; 87 88RenderThemeQt::RenderThemeQt(Page* page) 89 : RenderTheme() 90 , m_page(page) 91{ 92 m_buttonFontFamily = QGuiApplication::font().family(); 93} 94 95void RenderThemeQt::setCustomTheme(QtThemeFactoryFunction factory, ScrollbarTheme* customScrollbarTheme) 96{ 97 themeFactory = factory; 98 scrollbarTheme = customScrollbarTheme; 99} 100 101ScrollbarTheme* RenderThemeQt::customScrollbarTheme() 102{ 103 return scrollbarTheme; 104} 105 106static PassRefPtr<RenderTheme> createTheme(Page* page) 107{ 108 if (themeFactory) 109 return themeFactory(page); 110 return RenderThemeQtMobile::create(page); 111} 112 113PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) 114{ 115 if (page) 116 return createTheme(page); 117 static RenderTheme* fallback = createTheme(0).leakRef(); 118 return fallback; 119} 120 121// Remove this when SearchFieldPart is style-able in RenderTheme::isControlStyled() 122bool RenderThemeQt::isControlStyled(const RenderStyle* style, const BorderData& border, const FillLayer& fill, const Color& backgroundColor) const 123{ 124 switch (style->appearance()) { 125 case SearchFieldPart: 126 // Test the style to see if the UA border and background match. 127 return (style->border() != border 128 || *style->backgroundLayers() != fill 129 || style->visitedDependentColor(CSSPropertyBackgroundColor) != backgroundColor); 130 default: 131 return RenderTheme::isControlStyled(style, border, fill, backgroundColor); 132 } 133} 134 135String RenderThemeQt::extraDefaultStyleSheet() 136{ 137 StringBuilder result; 138 result.append(RenderTheme::extraDefaultStyleSheet()); 139 // When no theme factory is provided we default to using our platform independent "Mobile Qt" theme, 140 // which requires the following stylesheets. 141 if (!themeFactory) { 142 result.append(String(themeQtNoListboxesUserAgentStyleSheet, sizeof(themeQtNoListboxesUserAgentStyleSheet))); 143 result.append(String(mobileThemeQtUserAgentStyleSheet, sizeof(mobileThemeQtUserAgentStyleSheet))); 144 } 145 return result.toString(); 146} 147 148bool RenderThemeQt::supportsHover(const RenderStyle*) const 149{ 150 return true; 151} 152 153bool RenderThemeQt::supportsFocusRing(const RenderStyle* style) const 154{ 155 switch (style->appearance()) { 156 case CheckboxPart: 157 case RadioPart: 158 case PushButtonPart: 159 case SquareButtonPart: 160 case ButtonPart: 161 case ButtonBevelPart: 162 case ListboxPart: 163 case ListItemPart: 164 case MenulistPart: 165 case MenulistButtonPart: 166 case SliderHorizontalPart: 167 case SliderVerticalPart: 168 case SliderThumbHorizontalPart: 169 case SliderThumbVerticalPart: 170 case SearchFieldPart: 171 case SearchFieldResultsButtonPart: 172 case SearchFieldCancelButtonPart: 173 case TextFieldPart: 174 case TextAreaPart: 175 return true; 176 default: 177 return false; 178 } 179} 180 181int RenderThemeQt::baselinePosition(const RenderObject* o) const 182{ 183 if (!o->isBox()) 184 return 0; 185 186 if (o->style()->appearance() == CheckboxPart || o->style()->appearance() == RadioPart) 187 return toRenderBox(o)->marginTop() + toRenderBox(o)->height() - 2; // Same as in old khtml 188 return RenderTheme::baselinePosition(o); 189} 190 191bool RenderThemeQt::controlSupportsTints(const RenderObject* o) const 192{ 193 if (!isEnabled(o)) 194 return false; 195 196 // Checkboxes only have tint when checked. 197 if (o->style()->appearance() == CheckboxPart) 198 return isChecked(o); 199 200 // For now assume other controls have tint if enabled. 201 return true; 202} 203 204bool RenderThemeQt::supportsControlTints() const 205{ 206 return true; 207} 208 209QRect RenderThemeQt::inflateButtonRect(const QRect& originalRect) const 210{ 211 return originalRect; 212} 213 214void RenderThemeQt::adjustRepaintRect(const RenderObject* o, IntRect& rect) 215{ 216 switch (o->style()->appearance()) { 217 case CheckboxPart: 218 break; 219 case RadioPart: 220 break; 221 case PushButtonPart: 222 case ButtonPart: { 223 QRect inflatedRect = inflateButtonRect(rect); 224 rect = IntRect(inflatedRect.x(), inflatedRect.y(), inflatedRect.width(), inflatedRect.height()); 225 break; 226 } 227 case MenulistPart: 228 break; 229 default: 230 break; 231 } 232} 233 234Color RenderThemeQt::platformActiveSelectionBackgroundColor() const 235{ 236 return colorPalette().brush(QPalette::Active, QPalette::Highlight).color(); 237} 238 239Color RenderThemeQt::platformInactiveSelectionBackgroundColor() const 240{ 241 return colorPalette().brush(QPalette::Inactive, QPalette::Highlight).color(); 242} 243 244Color RenderThemeQt::platformActiveSelectionForegroundColor() const 245{ 246 return colorPalette().brush(QPalette::Active, QPalette::HighlightedText).color(); 247} 248 249Color RenderThemeQt::platformInactiveSelectionForegroundColor() const 250{ 251 return colorPalette().brush(QPalette::Inactive, QPalette::HighlightedText).color(); 252} 253 254Color RenderThemeQt::platformFocusRingColor() const 255{ 256 return colorPalette().brush(QPalette::Active, QPalette::Highlight).color(); 257} 258 259void RenderThemeQt::systemFont(int, FontDescription&) const 260{ 261 // no-op 262} 263 264Color RenderThemeQt::systemColor(int cssValueId) const 265{ 266 QPalette pal = colorPalette(); 267 switch (cssValueId) { 268 case CSSValueButtontext: 269 return pal.brush(QPalette::Active, QPalette::ButtonText).color(); 270 case CSSValueCaptiontext: 271 return pal.brush(QPalette::Active, QPalette::Text).color(); 272 default: 273 return RenderTheme::systemColor(cssValueId); 274 } 275} 276 277int RenderThemeQt::minimumMenuListSize(RenderStyle*) const 278{ 279 // FIXME: Later we need a way to query the UI process for the dpi 280 const QFontMetrics fm(QGuiApplication::font()); 281 return fm.width(QLatin1Char('x')); 282} 283 284void RenderThemeQt::setCheckboxSize(RenderStyle* style) const 285{ 286 computeSizeBasedOnStyle(style); 287} 288 289bool RenderThemeQt::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& r) 290{ 291 return paintButton(o, i, r); 292} 293 294void RenderThemeQt::setRadioSize(RenderStyle* style) const 295{ 296 computeSizeBasedOnStyle(style); 297} 298 299bool RenderThemeQt::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& r) 300{ 301 return paintButton(o, i, r); 302} 303 304void RenderThemeQt::setButtonSize(RenderStyle* style) const 305{ 306 computeSizeBasedOnStyle(style); 307} 308 309void RenderThemeQt::adjustTextFieldStyle(StyleResolver*, RenderStyle* style, Element*) const 310{ 311 // Resetting the style like this leads to differences like: 312 // - RenderTextControl {INPUT} at (2,2) size 168x25 [bgcolor=#FFFFFF] border: (2px inset #000000)] 313 // + RenderTextControl {INPUT} at (2,2) size 166x26 314 // in layout tests when a CSS style is applied that doesn't affect background color, border or 315 // padding. Just worth keeping in mind! 316 style->setBackgroundColor(Color::transparent); 317 style->resetBorder(); 318 style->resetPadding(); 319 computeSizeBasedOnStyle(style); 320} 321 322void RenderThemeQt::adjustTextAreaStyle(StyleResolver* selector, RenderStyle* style, Element* element) const 323{ 324 adjustTextFieldStyle(selector, style, element); 325} 326 327bool RenderThemeQt::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r) 328{ 329 return paintTextField(o, i, r); 330} 331 332void RenderThemeQt::adjustMenuListStyle(StyleResolver*, RenderStyle* style, Element*) const 333{ 334 style->resetBorder(); 335 336 // Height is locked to auto. 337 style->setHeight(Length(Auto)); 338 339 // White-space is locked to pre 340 style->setWhiteSpace(PRE); 341 342 computeSizeBasedOnStyle(style); 343 344 // Add in the padding that we'd like to use. 345 setPopupPadding(style); 346} 347 348void RenderThemeQt::adjustMenuListButtonStyle(StyleResolver*, RenderStyle* style, Element*) const 349{ 350 // Height is locked to auto. 351 style->setHeight(Length(Auto)); 352 353 // White-space is locked to pre 354 style->setWhiteSpace(PRE); 355 356 computeSizeBasedOnStyle(style); 357 358 // Add in the padding that we'd like to use. 359 setPopupPadding(style); 360} 361 362#if ENABLE(PROGRESS_ELEMENT) 363double RenderThemeQt::animationRepeatIntervalForProgressBar(RenderProgress* renderProgress) const 364{ 365 if (renderProgress->position() >= 0) 366 return 0; 367 368 // FIXME: Use hard-coded value until http://bugreports.qt.nokia.com/browse/QTBUG-9171 is fixed. 369 // Use the value from windows style which is 10 fps. 370 return 0.1; 371} 372 373void RenderThemeQt::adjustProgressBarStyle(StyleResolver*, RenderStyle* style, Element*) const 374{ 375 style->setBoxShadow(nullptr); 376} 377#endif 378 379void RenderThemeQt::adjustSliderTrackStyle(StyleResolver*, RenderStyle* style, Element*) const 380{ 381 style->setBoxShadow(nullptr); 382} 383 384void RenderThemeQt::adjustSliderThumbStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const 385{ 386 RenderTheme::adjustSliderThumbStyle(styleResolver, style, element); 387 style->setBoxShadow(nullptr); 388} 389 390#if ENABLE(DATALIST_ELEMENT) 391IntSize RenderThemeQt::sliderTickSize() const 392{ 393 // FIXME: We need to set this to the size of one tick mark. 394 return IntSize(0, 0); 395} 396 397int RenderThemeQt::sliderTickOffsetFromTrackCenter() const 398{ 399 // FIXME: We need to set this to the position of the tick marks. 400 return 0; 401} 402#endif 403 404bool RenderThemeQt::paintSearchField(RenderObject* o, const PaintInfo& pi, 405 const IntRect& r) 406{ 407 return paintTextField(o, pi, r); 408} 409 410void RenderThemeQt::adjustSearchFieldStyle(StyleResolver*, RenderStyle* style, Element*) const 411{ 412 // Resetting the style like this leads to differences like: 413 // - RenderTextControl {INPUT} at (2,2) size 168x25 [bgcolor=#FFFFFF] border: (2px inset #000000)] 414 // + RenderTextControl {INPUT} at (2,2) size 166x26 415 // in layout tests when a CSS style is applied that doesn't affect background color, border or 416 // padding. Just worth keeping in mind! 417 style->setBackgroundColor(Color::transparent); 418 style->resetBorder(); 419 style->resetPadding(); 420 computeSizeBasedOnStyle(style); 421} 422 423void RenderThemeQt::adjustSearchFieldCancelButtonStyle(StyleResolver*, RenderStyle* style, Element*) const 424{ 425 // Logic taken from RenderThemeChromium.cpp. 426 // Scale the button size based on the font size. 427 float fontScale = style->fontSize() / defaultControlFontPixelSize; 428 int cancelButtonSize = lroundf(qMin(qMax(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize)); 429 style->setWidth(Length(cancelButtonSize, Fixed)); 430 style->setHeight(Length(cancelButtonSize, Fixed)); 431} 432 433// Function taken from RenderThemeChromium.cpp 434IntRect RenderThemeQt::convertToPaintingRect(RenderObject* inputRenderer, const RenderObject* partRenderer, IntRect partRect, const IntRect& localOffset) const 435{ 436 // Compute an offset between the part renderer and the input renderer. 437 IntSize offsetFromInputRenderer = -roundedIntSize(partRenderer->offsetFromAncestorContainer(inputRenderer)); 438 // Move the rect into partRenderer's coords. 439 partRect.move(offsetFromInputRenderer); 440 // Account for the local drawing offset. 441 partRect.move(localOffset.x(), localOffset.y()); 442 443 return partRect; 444} 445 446QPalette RenderThemeQt::colorPalette() const 447{ 448 return QGuiApplication::palette(); 449} 450 451bool RenderThemeQt::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& pi, 452 const IntRect& r) 453{ 454 // Logic copied from RenderThemeChromium.cpp. 455 456 // Get the renderer of <input> element. 457 Node* input = o->node()->shadowHost(); 458 if (!input) 459 input = o->node(); 460 if (!input->renderer()->isBox()) 461 return false; 462 RenderBox* inputRenderBox = toRenderBox(input->renderer()); 463 IntRect inputContentBox = pixelSnappedIntRect(inputRenderBox->contentBoxRect()); 464 465 // Make sure the scaled button stays square and will fit in its parent's box. 466 int cancelButtonSize = qMin(inputContentBox.width(), qMin(inputContentBox.height(), r.height())); 467 // Calculate cancel button's coordinates relative to the input element. 468 // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will 469 // be one pixel closer to the bottom of the field. This tends to look better with the text. 470 IntRect cancelButtonRect(o->offsetFromAncestorContainer(inputRenderBox).width(), 471 inputContentBox.y() + (inputContentBox.height() - cancelButtonSize + 1) / 2, 472 cancelButtonSize, cancelButtonSize); 473 IntRect paintingRect = convertToPaintingRect(inputRenderBox, o, cancelButtonRect, r); 474 static Image* cancelImage = Image::loadPlatformResource("searchCancelButton").leakRef(); 475 static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelButtonPressed").leakRef(); 476 pi.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage, 477 o->style()->colorSpace(), paintingRect); 478 return false; 479} 480 481void RenderThemeQt::adjustSearchFieldDecorationStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const 482{ 483 notImplemented(); 484 RenderTheme::adjustSearchFieldDecorationStyle(styleResolver, style, e); 485} 486 487bool RenderThemeQt::paintSearchFieldDecoration(RenderObject* o, const PaintInfo& pi, 488 const IntRect& r) 489{ 490 notImplemented(); 491 return RenderTheme::paintSearchFieldDecoration(o, pi, r); 492} 493 494void RenderThemeQt::adjustSearchFieldResultsDecorationStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const 495{ 496 notImplemented(); 497 RenderTheme::adjustSearchFieldResultsDecorationStyle(styleResolver, style, e); 498} 499 500bool RenderThemeQt::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& pi, 501 const IntRect& r) 502{ 503 notImplemented(); 504 return RenderTheme::paintSearchFieldResultsDecoration(o, pi, r); 505} 506 507#ifndef QT_NO_SPINBOX 508void RenderThemeQt::adjustInnerSpinButtonStyle(StyleResolver*, RenderStyle* style, Element*) const 509{ 510 // Use the same width as our native scrollbar 511 int width = ScrollbarTheme::theme()->scrollbarThickness(); 512 style->setWidth(Length(width, Fixed)); 513 style->setMinWidth(Length(width, Fixed)); 514} 515#endif 516 517bool RenderThemeQt::supportsFocus(ControlPart appearance) const 518{ 519 switch (appearance) { 520 case PushButtonPart: 521 case ButtonPart: 522 case TextFieldPart: 523 case TextAreaPart: 524 case ListboxPart: 525 case MenulistPart: 526 case RadioPart: 527 case CheckboxPart: 528 case SliderHorizontalPart: 529 case SliderVerticalPart: 530 return true; 531 default: // No for all others... 532 return false; 533 } 534} 535 536#if ENABLE(VIDEO) 537 538String RenderThemeQt::extraMediaControlsStyleSheet() 539{ 540 String result = String(mediaControlsQtUserAgentStyleSheet, sizeof(mediaControlsQtUserAgentStyleSheet)); 541 542 if (m_page && m_page->chrome().requiresFullscreenForVideoPlayback()) 543 result.append(String(mediaControlsQtFullscreenUserAgentStyleSheet, sizeof(mediaControlsQtFullscreenUserAgentStyleSheet))); 544 545 return result; 546} 547 548// Helper class to transform the painter's world matrix to the object's content area, scaled to 0,0,100,100 549class WorldMatrixTransformer { 550public: 551 WorldMatrixTransformer(QPainter* painter, RenderObject* renderObject, const IntRect& r) : m_painter(painter) 552 { 553 RenderStyle* style = renderObject->style(); 554 m_originalTransform = m_painter->transform(); 555 m_painter->translate(r.x() + style->paddingLeft().value(), r.y() + style->paddingTop().value()); 556 m_painter->scale((r.width() - style->paddingLeft().value() - style->paddingRight().value()) / 100.0, 557 (r.height() - style->paddingTop().value() - style->paddingBottom().value()) / 100.0); 558 } 559 560 ~WorldMatrixTransformer() { m_painter->setTransform(m_originalTransform); } 561 562private: 563 QPainter* m_painter; 564 QTransform m_originalTransform; 565}; 566 567double RenderThemeQt::mediaControlsBaselineOpacity() const 568{ 569 return 0.4; 570} 571 572void RenderThemeQt::paintMediaBackground(QPainter* painter, const IntRect& r) const 573{ 574 painter->setPen(Qt::NoPen); 575 static QColor transparentBlack(0, 0, 0, mediaControlsBaselineOpacity() * 255); 576 painter->setBrush(transparentBlack); 577 painter->drawRoundedRect(r.x(), r.y(), r.width(), r.height(), 5.0, 5.0); 578} 579 580static bool mediaElementCanPlay(RenderObject* o) 581{ 582 HTMLMediaElement* mediaElement = toParentMediaElement(o); 583 if (!mediaElement) 584 return false; 585 586 return mediaElement->readyState() > HTMLMediaElement::HAVE_METADATA 587 || (mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING 588 && o->style()->appearance() == MediaPlayButtonPart && mediaElement->preload() == "none"); 589} 590 591QColor RenderThemeQt::getMediaControlForegroundColor(RenderObject* o) const 592{ 593 QColor fgColor = platformActiveSelectionBackgroundColor(); 594 if (!o) 595 return fgColor; 596 597 if (o->node() && o->node()->isElementNode() && toElement(o->node())->active()) 598 fgColor = fgColor.lighter(); 599 600 if (!mediaElementCanPlay(o)) 601 fgColor = colorPalette().brush(QPalette::Disabled, QPalette::Text).color(); 602 603 return fgColor; 604} 605 606bool RenderThemeQt::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 607{ 608 HTMLMediaElement* mediaElement = toParentMediaElement(o); 609 if (!mediaElement) 610 return false; 611 612 QSharedPointer<StylePainter> p = getStylePainter(paintInfo); 613 if (p.isNull() || !p->isValid()) 614 return true; 615 616 p->painter->setRenderHint(QPainter::Antialiasing, true); 617 618 paintMediaBackground(p->painter, r); 619 620 WorldMatrixTransformer transformer(p->painter, o, r); 621 const QPointF arrowPolygon[9] = { QPointF(20, 0), QPointF(100, 0), QPointF(100, 80), 622 QPointF(80, 80), QPointF(80, 30), QPointF(10, 100), QPointF(0, 90), QPointF(70, 20), QPointF(20, 20)}; 623 624 p->painter->setBrush(getMediaControlForegroundColor(o)); 625 p->painter->drawPolygon(arrowPolygon, 9); 626 627 return false; 628} 629 630bool RenderThemeQt::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 631{ 632 HTMLMediaElement* mediaElement = toParentMediaElement(o); 633 if (!mediaElement) 634 return false; 635 636 QSharedPointer<StylePainter> p = getStylePainter(paintInfo); 637 if (p.isNull() || !p->isValid()) 638 return true; 639 640 p->painter->setRenderHint(QPainter::Antialiasing, true); 641 642 paintMediaBackground(p->painter, r); 643 644 WorldMatrixTransformer transformer(p->painter, o, r); 645 const QPointF speakerPolygon[6] = { QPointF(20, 30), QPointF(50, 30), QPointF(80, 0), 646 QPointF(80, 100), QPointF(50, 70), QPointF(20, 70)}; 647 648 p->painter->setBrush(mediaElement->muted() ? Qt::darkRed : getMediaControlForegroundColor(o)); 649 p->painter->drawPolygon(speakerPolygon, 6); 650 651 return false; 652} 653 654bool RenderThemeQt::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 655{ 656 HTMLMediaElement* mediaElement = toParentMediaElement(o); 657 if (!mediaElement) 658 return false; 659 660 QSharedPointer<StylePainter> p = getStylePainter(paintInfo); 661 if (p.isNull() || !p->isValid()) 662 return true; 663 664 p->painter->setRenderHint(QPainter::Antialiasing, true); 665 666 paintMediaBackground(p->painter, r); 667 668 WorldMatrixTransformer transformer(p->painter, o, r); 669 p->painter->setBrush(getMediaControlForegroundColor(o)); 670 if (mediaElement->canPlay()) { 671 const QPointF playPolygon[3] = { QPointF(0, 0), QPointF(100, 50), QPointF(0, 100)}; 672 p->painter->drawPolygon(playPolygon, 3); 673 } else { 674 p->painter->drawRect(0, 0, 30, 100); 675 p->painter->drawRect(70, 0, 30, 100); 676 } 677 678 return false; 679} 680 681bool RenderThemeQt::paintMediaSeekBackButton(RenderObject*, const PaintInfo&, const IntRect&) 682{ 683 // We don't want to paint this at the moment. 684 return false; 685} 686 687bool RenderThemeQt::paintMediaSeekForwardButton(RenderObject*, const PaintInfo&, const IntRect&) 688{ 689 // We don't want to paint this at the moment. 690 return false; 691} 692 693bool RenderThemeQt::paintMediaCurrentTime(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 694{ 695 QSharedPointer<StylePainter> p = getStylePainter(paintInfo); 696 if (p.isNull() || !p->isValid()) 697 return true; 698 699 p->painter->setRenderHint(QPainter::Antialiasing, true); 700 paintMediaBackground(p->painter, r); 701 702 return false; 703} 704 705String RenderThemeQt::formatMediaControlsCurrentTime(float currentTime, float duration) const 706{ 707 return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration); 708} 709 710String RenderThemeQt::formatMediaControlsRemainingTime(float currentTime, float duration) const 711{ 712 return String(); 713} 714 715bool RenderThemeQt::paintMediaVolumeSliderTrack(RenderObject *o, const PaintInfo &paintInfo, const IntRect &r) 716{ 717 QSharedPointer<StylePainter> p = getStylePainter(paintInfo); 718 if (p.isNull() || !p->isValid()) 719 return true; 720 721 p->painter->setRenderHint(QPainter::Antialiasing, true); 722 723 paintMediaBackground(p->painter, r); 724 725 if (!o->isSlider()) 726 return false; 727 728 IntRect b = pixelSnappedIntRect(toRenderBox(o)->contentBoxRect()); 729 730 // Position the outer rectangle 731 int top = r.y() + b.y(); 732 int left = r.x() + b.x(); 733 int width = b.width(); 734 int height = b.height(); 735 736 QPalette pal = colorPalette(); 737 const QColor highlightText = pal.brush(QPalette::Active, QPalette::HighlightedText).color(); 738 const QColor scaleColor(highlightText.red(), highlightText.green(), highlightText.blue(), mediaControlsBaselineOpacity() * 255); 739 740 // Draw the outer rectangle 741 p->painter->setBrush(scaleColor); 742 p->painter->drawRect(left, top, width, height); 743 744 if (!o->node() || !o->node()->hasTagName(inputTag)) 745 return false; 746 747 HTMLInputElement* slider = static_cast<HTMLInputElement*>(o->node()); 748 749 // Position the inner rectangle 750 height = height * slider->valueAsNumber(); 751 top += b.height() - height; 752 753 // Draw the inner rectangle 754 p->painter->setPen(Qt::NoPen); 755 p->painter->setBrush(getMediaControlForegroundColor(o)); 756 p->painter->drawRect(left, top, width, height); 757 758 return false; 759} 760 761bool RenderThemeQt::paintMediaVolumeSliderThumb(RenderObject *o, const PaintInfo &paintInfo, const IntRect &r) 762{ 763 QSharedPointer<StylePainter> p = getStylePainter(paintInfo); 764 if (p.isNull() || !p->isValid()) 765 return true; 766 767 // Nothing to draw here, this is all done in the track 768 return false; 769} 770 771bool RenderThemeQt::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 772{ 773 HTMLMediaElement* mediaElement = toParentMediaElement(o); 774 if (!mediaElement) 775 return false; 776 777 QSharedPointer<StylePainter> p = getStylePainter(paintInfo); 778 if (p.isNull() || !p->isValid()) 779 return true; 780 781 p->painter->setRenderHint(QPainter::Antialiasing, true); 782 783 paintMediaBackground(p->painter, r); 784 785 if (MediaPlayer* player = mediaElement->player()) { 786 // Get the buffered parts of the media 787 RefPtr<TimeRanges> buffered = player->buffered(); 788 if (buffered->length() > 0 && player->duration() < std::numeric_limits<float>::infinity()) { 789 // Set the transform and brush 790 WorldMatrixTransformer transformer(p->painter, o, r); 791 p->painter->setBrush(getMediaControlForegroundColor()); 792 793 // Paint each buffered section 794 for (int i = 0; i < buffered->length(); i++) { 795 float startX = (buffered->start(i, IGNORE_EXCEPTION) / player->duration()) * 100; 796 float width = ((buffered->end(i, IGNORE_EXCEPTION) / player->duration()) * 100) - startX; 797 p->painter->drawRect(startX, 37, width, 26); 798 } 799 } 800 } 801 802 return false; 803} 804 805bool RenderThemeQt::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 806{ 807 ASSERT(o->node()); 808 Node* hostNode = o->node()->shadowHost(); 809 if (!hostNode) 810 hostNode = o->node(); 811 HTMLMediaElement* mediaElement = toParentMediaElement(hostNode); 812 if (!mediaElement) 813 return false; 814 815 QSharedPointer<StylePainter> p = getStylePainter(paintInfo); 816 if (p.isNull() || !p->isValid()) 817 return true; 818 819 p->painter->setRenderHint(QPainter::Antialiasing, true); 820 821 p->painter->setPen(Qt::NoPen); 822 p->painter->setBrush(getMediaControlForegroundColor(hostNode->renderer())); 823 p->painter->drawRect(r.x(), r.y(), r.width(), r.height()); 824 825 return false; 826} 827#endif 828 829void RenderThemeQt::adjustSliderThumbSize(RenderStyle* style, Element*) const 830{ 831 // timelineThumbHeight should match the height property of -webkit-media-controls-timeline in mediaControlsQt.css. 832 const int timelineThumbHeight = 12; 833 const int timelineThumbWidth = timelineThumbHeight / 3; 834 // volumeThumbWidth should match the width property of -webkit-media-controls-volume-slider in mediaControlsQt.css. 835 const int volumeThumbWidth = 12; 836 const int volumeThumbHeight = volumeThumbWidth / 3; 837 ControlPart part = style->appearance(); 838 839 if (part == MediaSliderThumbPart) { 840 style->setWidth(Length(timelineThumbWidth, Fixed)); 841 style->setHeight(Length(timelineThumbHeight, Fixed)); 842 } else if (part == MediaVolumeSliderThumbPart) { 843 style->setHeight(Length(volumeThumbHeight, Fixed)); 844 style->setWidth(Length(volumeThumbWidth, Fixed)); 845 } 846} 847 848double RenderThemeQt::caretBlinkInterval() const 849{ 850 return static_cast<QGuiApplication*>(qApp)->styleHints()->cursorFlashTime() / 1000.0 / 2.0; 851} 852 853String RenderThemeQt::fileListNameForWidth(const FileList* fileList, const Font& font, int width, bool multipleFilesAllowed) const 854{ 855 UNUSED_PARAM(multipleFilesAllowed); 856 if (width <= 0) 857 return String(); 858 859 String string; 860 if (fileList->isEmpty()) 861 string = fileButtonNoFileSelectedLabel(); 862 else if (fileList->length() == 1) { 863 String fname = fileList->item(0)->path(); 864 QFontMetrics fm(font.syntheticFont()); 865 string = fm.elidedText(fname, Qt::ElideLeft, width); 866 } else { 867 int n = fileList->length(); 868 string = QCoreApplication::translate("QWebPage", "%n file(s)", 869 "number of chosen file", 870 n); 871 } 872 873 return string; 874} 875 876StylePainter::StylePainter(RenderThemeQt* theme, const PaintInfo& paintInfo) 877 : painter(0) 878{ 879 Q_UNUSED(theme); 880 ASSERT(paintInfo.context); 881 init(paintInfo.context); 882} 883 884StylePainter::StylePainter() 885 : painter(0) 886{ 887} 888 889void StylePainter::init(GraphicsContext* context) 890{ 891 painter = static_cast<QPainter*>(context->platformContext()); 892 893 if (painter) { 894 // the styles often assume being called with a pristine painter where no brush is set, 895 // so reset it manually 896 m_previousBrush = painter->brush(); 897 painter->setBrush(Qt::NoBrush); 898 899 // painting the widget with anti-aliasing will make it blurry 900 // disable it here and restore it later 901 m_previousAntialiasing = painter->testRenderHint(QPainter::Antialiasing); 902 painter->setRenderHint(QPainter::Antialiasing, false); 903 } 904} 905 906StylePainter::~StylePainter() 907{ 908 if (painter) { 909 painter->setBrush(m_previousBrush); 910 painter->setRenderHints(QPainter::Antialiasing, m_previousAntialiasing); 911 } 912} 913 914} 915 916// vim: ts=4 sw=4 et 917