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 "CSSStyleSelector.h"
34 #include "CSSStyleSheet.h"
35 #include "CSSValueKeywords.h"
36 #include "Chrome.h"
37 #include "ChromeClientQt.h"
38 #include "Color.h"
39 #include "Document.h"
40 #include "Font.h"
41 #include "FontSelector.h"
42 #include "GraphicsContext.h"
43 #include "HTMLInputElement.h"
44 #include "HTMLMediaElement.h"
45 #include "HTMLNames.h"
46 #if USE(QT_MOBILE_THEME)
47 #include "QtMobileWebStyle.h"
48 #endif
49 #if ENABLE(VIDEO)
50 #include "MediaControlElements.h"
51 #endif
52 #include "NotImplemented.h"
53 #include "PaintInfo.h"
54 #include "Page.h"
55 #include "QWebPageClient.h"
56 #include "QtStyleOptionWebComboBox.h"
57 #include "qwebsettings.h"
58 #include "RenderBox.h"
59 #if ENABLE(PROGRESS_TAG)
60 #include "RenderProgress.h"
61 #endif
62 #include "RenderSlider.h"
63 #include "RenderTheme.h"
64 #include "ScrollbarThemeQt.h"
65 #include "TimeRanges.h"
66 #include "UserAgentStyleSheets.h"
67
68 #include <QApplication>
69 #include <QColor>
70 #include <QFile>
71 #include <QLineEdit>
72 #include <QMacStyle>
73 #include <QPainter>
74 #include <QPushButton>
75 #include <QStyleFactory>
76 #include <QStyleOptionButton>
77 #include <QStyleOptionFrameV2>
78 #if ENABLE(PROGRESS_TAG)
79 #include <QStyleOptionProgressBarV2>
80 #endif
81 #include <QStyleOptionSlider>
82 #include <QWidget>
83
84 namespace WebCore {
85
86 using namespace HTMLNames;
87
initStyleOption(QWidget * widget,QStyleOption & option)88 inline static void initStyleOption(QWidget *widget, QStyleOption& option)
89 {
90 if (widget)
91 option.initFrom(widget);
92 else {
93 /*
94 If a widget is not directly available for rendering, we fallback to default
95 value for an active widget.
96 */
97 option.state = QStyle::State_Active | QStyle::State_Enabled;
98 }
99 }
100 // These values all match Safari/Win/Chromium
101 static const float defaultControlFontPixelSize = 13;
102 static const float defaultCancelButtonSize = 9;
103 static const float minCancelButtonSize = 5;
104 static const float maxCancelButtonSize = 21;
105 static const float defaultSearchFieldResultsDecorationSize = 13;
106 static const float minSearchFieldResultsDecorationSize = 9;
107 static const float maxSearchFieldResultsDecorationSize = 30;
108 static const float defaultSearchFieldResultsButtonWidth = 18;
109
110 #if USE(QT_MOBILE_THEME)
111 namespace {
112 float buttonPaddingLeft = 18;
113 float buttonPaddingRight = 18;
114 float buttonPaddingTop = 2;
115 float buttonPaddingBottom = 3;
116 float menuListPadding = 9;
117 float textFieldPadding = 5;
118 }
119 #endif
120
StylePainter(RenderThemeQt * theme,const PaintInfo & paintInfo)121 StylePainter::StylePainter(RenderThemeQt* theme, const PaintInfo& paintInfo)
122 {
123 init(paintInfo.context ? paintInfo.context : 0, theme->qStyle());
124 }
125
StylePainter(ScrollbarThemeQt * theme,GraphicsContext * context)126 StylePainter::StylePainter(ScrollbarThemeQt* theme, GraphicsContext* context)
127 {
128 init(context, theme->style());
129 }
130
init(GraphicsContext * context,QStyle * themeStyle)131 void StylePainter::init(GraphicsContext* context, QStyle* themeStyle)
132 {
133 painter = static_cast<QPainter*>(context->platformContext());
134 widget = 0;
135 QPaintDevice* dev = 0;
136 if (painter)
137 dev = painter->device();
138 if (dev && dev->devType() == QInternal::Widget)
139 widget = static_cast<QWidget*>(dev);
140 style = themeStyle;
141
142 if (painter) {
143 // the styles often assume being called with a pristine painter where no brush is set,
144 // so reset it manually
145 oldBrush = painter->brush();
146 painter->setBrush(Qt::NoBrush);
147
148 // painting the widget with anti-aliasing will make it blurry
149 // disable it here and restore it later
150 oldAntialiasing = painter->testRenderHint(QPainter::Antialiasing);
151 painter->setRenderHint(QPainter::Antialiasing, false);
152 }
153 }
154
~StylePainter()155 StylePainter::~StylePainter()
156 {
157 if (painter) {
158 painter->setBrush(oldBrush);
159 painter->setRenderHints(QPainter::Antialiasing, oldAntialiasing);
160 }
161 }
162
create(Page * page)163 PassRefPtr<RenderTheme> RenderThemeQt::create(Page* page)
164 {
165 return adoptRef(new RenderThemeQt(page));
166 }
167
themeForPage(Page * page)168 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
169 {
170 if (page)
171 return RenderThemeQt::create(page);
172
173 static RenderTheme* fallback = RenderThemeQt::create(0).releaseRef();
174 return fallback;
175 }
176
RenderThemeQt(Page * page)177 RenderThemeQt::RenderThemeQt(Page* page)
178 : RenderTheme()
179 , m_page(page)
180 , m_lineEdit(0)
181 , m_fallbackStyle(0)
182 {
183 if (QApplication::type() == QApplication::Tty) {
184 m_buttonFontFamily = "sans-serif";
185 return;
186 }
187
188 QPushButton button;
189 button.setAttribute(Qt::WA_MacSmallSize);
190 QFont defaultButtonFont = QApplication::font(&button);
191 QFontInfo fontInfo(defaultButtonFont);
192 m_buttonFontFamily = defaultButtonFont.family();
193 #ifdef Q_WS_MAC
194 m_buttonFontPixelSize = fontInfo.pixelSize();
195 #endif
196
197 #if USE(QT_MOBILE_THEME)
198 m_fallbackStyle = new QtMobileWebStyle;
199 #else
200 m_fallbackStyle = QStyleFactory::create(QLatin1String("windows"));
201 #endif
202 }
203
~RenderThemeQt()204 RenderThemeQt::~RenderThemeQt()
205 {
206 delete m_fallbackStyle;
207 #ifndef QT_NO_LINEEDIT
208 delete m_lineEdit;
209 #endif
210 }
211
212 #if USE(QT_MOBILE_THEME)
isControlStyled(const RenderStyle * style,const BorderData & border,const FillLayer & fill,const Color & backgroundColor) const213 bool RenderThemeQt::isControlStyled(const RenderStyle* style, const BorderData& border, const FillLayer& fill, const Color& backgroundColor) const
214 {
215 switch (style->appearance()) {
216 case PushButtonPart:
217 case ButtonPart:
218 case MenulistPart:
219 case SearchFieldPart:
220 case TextFieldPart:
221 case TextAreaPart:
222 // Test the style to see if the UA border and background match.
223 return (style->border() != border
224 || *style->backgroundLayers() != fill
225 || style->visitedDependentColor(CSSPropertyBackgroundColor) != backgroundColor);
226 case CheckboxPart:
227 case RadioPart:
228 return false;
229 default:
230 return RenderTheme::isControlStyled(style, border, fill, backgroundColor);
231 }
232 }
233
popupInternalPaddingBottom(RenderStyle * style) const234 int RenderThemeQt::popupInternalPaddingBottom(RenderStyle* style) const
235 {
236 return 1;
237 }
238 #else
239 // Remove this when SearchFieldPart is style-able in RenderTheme::isControlStyled()
isControlStyled(const RenderStyle * style,const BorderData & border,const FillLayer & fill,const Color & backgroundColor) const240 bool RenderThemeQt::isControlStyled(const RenderStyle* style, const BorderData& border, const FillLayer& fill, const Color& backgroundColor) const
241 {
242 switch (style->appearance()) {
243 case SearchFieldPart:
244 // Test the style to see if the UA border and background match.
245 return (style->border() != border
246 || *style->backgroundLayers() != fill
247 || style->visitedDependentColor(CSSPropertyBackgroundColor) != backgroundColor);
248 default:
249 return RenderTheme::isControlStyled(style, border, fill, backgroundColor);
250 }
251 }
252 #endif
253
254 // for some widget painting, we need to fallback to Windows style
fallbackStyle() const255 QStyle* RenderThemeQt::fallbackStyle() const
256 {
257 return (m_fallbackStyle) ? m_fallbackStyle : QApplication::style();
258 }
259
qStyle() const260 QStyle* RenderThemeQt::qStyle() const
261 {
262 #if USE(QT_MOBILE_THEME)
263 return fallbackStyle();
264 #endif
265
266 if (m_page) {
267 QWebPageClient* pageClient = m_page->chrome()->client()->platformPageClient();
268
269 if (pageClient)
270 return pageClient->style();
271 }
272
273 return QApplication::style();
274 }
275
extraDefaultStyleSheet()276 String RenderThemeQt::extraDefaultStyleSheet()
277 {
278 String result = RenderTheme::extraDefaultStyleSheet();
279 #if ENABLE(NO_LISTBOX_RENDERING)
280 result += String(themeQtNoListboxesUserAgentStyleSheet, sizeof(themeQtNoListboxesUserAgentStyleSheet));
281 #endif
282 return result;
283 }
284
supportsHover(const RenderStyle *) const285 bool RenderThemeQt::supportsHover(const RenderStyle*) const
286 {
287 return true;
288 }
289
supportsFocusRing(const RenderStyle * style) const290 bool RenderThemeQt::supportsFocusRing(const RenderStyle* style) const
291 {
292 switch (style->appearance()) {
293 case CheckboxPart:
294 case RadioPart:
295 case PushButtonPart:
296 case SquareButtonPart:
297 case ButtonPart:
298 case ButtonBevelPart:
299 case ListboxPart:
300 case ListItemPart:
301 case MenulistPart:
302 case MenulistButtonPart:
303 case SliderHorizontalPart:
304 case SliderVerticalPart:
305 case SliderThumbHorizontalPart:
306 case SliderThumbVerticalPart:
307 case SearchFieldPart:
308 case SearchFieldResultsButtonPart:
309 case SearchFieldCancelButtonPart:
310 case TextFieldPart:
311 case TextAreaPart:
312 return true;
313 default:
314 return false;
315 }
316 }
317
baselinePosition(const RenderObject * o) const318 int RenderThemeQt::baselinePosition(const RenderObject* o) const
319 {
320 if (!o->isBox())
321 return 0;
322
323 if (o->style()->appearance() == CheckboxPart || o->style()->appearance() == RadioPart)
324 return toRenderBox(o)->marginTop() + toRenderBox(o)->height() - 2; // Same as in old khtml
325 return RenderTheme::baselinePosition(o);
326 }
327
controlSupportsTints(const RenderObject * o) const328 bool RenderThemeQt::controlSupportsTints(const RenderObject* o) const
329 {
330 if (!isEnabled(o))
331 return false;
332
333 // Checkboxes only have tint when checked.
334 if (o->style()->appearance() == CheckboxPart)
335 return isChecked(o);
336
337 // For now assume other controls have tint if enabled.
338 return true;
339 }
340
supportsControlTints() const341 bool RenderThemeQt::supportsControlTints() const
342 {
343 return true;
344 }
345
findFrameLineWidth(QStyle * style) const346 int RenderThemeQt::findFrameLineWidth(QStyle* style) const
347 {
348 if (QApplication::type()==QApplication::Tty)
349 return 1;
350
351 #ifndef QT_NO_LINEEDIT
352 if (!m_lineEdit)
353 m_lineEdit = new QLineEdit();
354 #endif
355
356 QStyleOptionFrameV2 opt;
357 QWidget* widget = 0;
358 #ifndef QT_NO_LINEEDIT
359 widget = m_lineEdit;
360 #endif
361 return style->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, widget);
362 }
363
inflateButtonRect(const QRect & originalRect,QStyle * style)364 static QRect inflateButtonRect(const QRect& originalRect, QStyle* style)
365 {
366 QStyleOptionButton option;
367 option.state |= QStyle::State_Small;
368 option.rect = originalRect;
369
370 QRect layoutRect = style->subElementRect(QStyle::SE_PushButtonLayoutItem, &option, 0);
371 if (!layoutRect.isNull()) {
372 int paddingLeft = layoutRect.left() - originalRect.left();
373 int paddingRight = originalRect.right() - layoutRect.right();
374 int paddingTop = layoutRect.top() - originalRect.top();
375 int paddingBottom = originalRect.bottom() - layoutRect.bottom();
376
377 return originalRect.adjusted(-paddingLeft, -paddingTop, paddingRight, paddingBottom);
378 }
379 return originalRect;
380 }
381
adjustRepaintRect(const RenderObject * o,IntRect & rect)382 void RenderThemeQt::adjustRepaintRect(const RenderObject* o, IntRect& rect)
383 {
384 switch (o->style()->appearance()) {
385 case CheckboxPart:
386 break;
387 case RadioPart:
388 break;
389 case PushButtonPart:
390 case ButtonPart: {
391 QRect inflatedRect = inflateButtonRect(rect, qStyle());
392 rect = IntRect(inflatedRect.x(), inflatedRect.y(), inflatedRect.width(), inflatedRect.height());
393 break;
394 }
395 case MenulistPart:
396 break;
397 default:
398 break;
399 }
400 }
401
platformActiveSelectionBackgroundColor() const402 Color RenderThemeQt::platformActiveSelectionBackgroundColor() const
403 {
404 QPalette pal = QApplication::palette();
405 setPaletteFromPageClientIfExists(pal);
406 return pal.brush(QPalette::Active, QPalette::Highlight).color();
407 }
408
platformInactiveSelectionBackgroundColor() const409 Color RenderThemeQt::platformInactiveSelectionBackgroundColor() const
410 {
411 QPalette pal = QApplication::palette();
412 setPaletteFromPageClientIfExists(pal);
413 return pal.brush(QPalette::Inactive, QPalette::Highlight).color();
414 }
415
platformActiveSelectionForegroundColor() const416 Color RenderThemeQt::platformActiveSelectionForegroundColor() const
417 {
418 QPalette pal = QApplication::palette();
419 setPaletteFromPageClientIfExists(pal);
420 return pal.brush(QPalette::Active, QPalette::HighlightedText).color();
421 }
422
platformInactiveSelectionForegroundColor() const423 Color RenderThemeQt::platformInactiveSelectionForegroundColor() const
424 {
425 QPalette pal = QApplication::palette();
426 setPaletteFromPageClientIfExists(pal);
427 return pal.brush(QPalette::Inactive, QPalette::HighlightedText).color();
428 }
429
platformFocusRingColor() const430 Color RenderThemeQt::platformFocusRingColor() const
431 {
432 QPalette pal = QApplication::palette();
433 setPaletteFromPageClientIfExists(pal);
434 return pal.brush(QPalette::Active, QPalette::Highlight).color();
435 }
436
systemFont(int,FontDescription &) const437 void RenderThemeQt::systemFont(int, FontDescription&) const
438 {
439 // no-op
440 }
441
systemColor(int cssValueId) const442 Color RenderThemeQt::systemColor(int cssValueId) const
443 {
444 QPalette pal = QApplication::palette();
445 switch (cssValueId) {
446 case CSSValueButtontext:
447 return pal.brush(QPalette::Active, QPalette::ButtonText).color();
448 case CSSValueCaptiontext:
449 return pal.brush(QPalette::Active, QPalette::Text).color();
450 default:
451 return RenderTheme::systemColor(cssValueId);
452 }
453 }
454
minimumMenuListSize(RenderStyle *) const455 int RenderThemeQt::minimumMenuListSize(RenderStyle*) const
456 {
457 if (QApplication::type() == QApplication::Tty)
458 return 1;
459
460 const QFontMetrics &fm = QApplication::fontMetrics();
461 return fm.width(QLatin1Char('x'));
462 }
463
computeSizeBasedOnStyle(RenderStyle * renderStyle) const464 void RenderThemeQt::computeSizeBasedOnStyle(RenderStyle* renderStyle) const
465 {
466 QSize size(0, 0);
467 const QFontMetrics fm(renderStyle->font().font());
468 QStyle* style = qStyle();
469
470 switch (renderStyle->appearance()) {
471 case TextAreaPart:
472 case SearchFieldPart:
473 case TextFieldPart: {
474 int padding = findFrameLineWidth(style);
475 renderStyle->setPaddingLeft(Length(padding, Fixed));
476 renderStyle->setPaddingRight(Length(padding, Fixed));
477 renderStyle->setPaddingTop(Length(padding, Fixed));
478 renderStyle->setPaddingBottom(Length(padding, Fixed));
479 break;
480 }
481 default:
482 break;
483 }
484 // If the width and height are both specified, then we have nothing to do.
485 if (!renderStyle->width().isIntrinsicOrAuto() && !renderStyle->height().isAuto())
486 return;
487
488 switch (renderStyle->appearance()) {
489 case CheckboxPart: {
490 QStyleOption styleOption;
491 styleOption.state |= QStyle::State_Small;
492 int checkBoxWidth = style->pixelMetric(QStyle::PM_IndicatorWidth, &styleOption);
493 checkBoxWidth *= renderStyle->effectiveZoom();
494 size = QSize(checkBoxWidth, checkBoxWidth);
495 break;
496 }
497 case RadioPart: {
498 QStyleOption styleOption;
499 styleOption.state |= QStyle::State_Small;
500 int radioWidth = style->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth, &styleOption);
501 radioWidth *= renderStyle->effectiveZoom();
502 size = QSize(radioWidth, radioWidth);
503 break;
504 }
505 #if !USE(QT_MOBILE_THEME)
506 case PushButtonPart:
507 case ButtonPart: {
508 QStyleOptionButton styleOption;
509 styleOption.state |= QStyle::State_Small;
510 QSize contentSize = fm.size(Qt::TextShowMnemonic, QString::fromLatin1("X"));
511 QSize pushButtonSize = style->sizeFromContents(QStyle::CT_PushButton,
512 &styleOption, contentSize, 0);
513 styleOption.rect = QRect(0, 0, pushButtonSize.width(), pushButtonSize.height());
514 QRect layoutRect = style->subElementRect(QStyle::SE_PushButtonLayoutItem,
515 &styleOption, 0);
516
517 // If the style supports layout rects we use that, and compensate accordingly
518 // in paintButton() below.
519 if (!layoutRect.isNull())
520 size.setHeight(layoutRect.height());
521 else
522 size.setHeight(pushButtonSize.height());
523
524 break;
525 }
526 case MenulistPart: {
527 QStyleOptionComboBox styleOption;
528 styleOption.state |= QStyle::State_Small;
529 int contentHeight = qMax(fm.lineSpacing(), 14) + 2;
530 QSize menuListSize = style->sizeFromContents(QStyle::CT_ComboBox,
531 &styleOption, QSize(0, contentHeight), 0);
532 size.setHeight(menuListSize.height());
533 break;
534 }
535 #endif
536 default:
537 break;
538 }
539
540 // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
541 if (renderStyle->width().isIntrinsicOrAuto() && size.width() > 0)
542 renderStyle->setMinWidth(Length(size.width(), Fixed));
543 if (renderStyle->height().isAuto() && size.height() > 0)
544 renderStyle->setMinHeight(Length(size.height(), Fixed));
545 }
546
setCheckboxSize(RenderStyle * style) const547 void RenderThemeQt::setCheckboxSize(RenderStyle* style) const
548 {
549 computeSizeBasedOnStyle(style);
550 }
551
paintCheckbox(RenderObject * o,const PaintInfo & i,const IntRect & r)552 bool RenderThemeQt::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& r)
553 {
554 return paintButton(o, i, r);
555 }
556
setRadioSize(RenderStyle * style) const557 void RenderThemeQt::setRadioSize(RenderStyle* style) const
558 {
559 computeSizeBasedOnStyle(style);
560 }
561
paintRadio(RenderObject * o,const PaintInfo & i,const IntRect & r)562 bool RenderThemeQt::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& r)
563 {
564 return paintButton(o, i, r);
565 }
566
adjustButtonStyle(CSSStyleSelector * selector,RenderStyle * style,Element *) const567 void RenderThemeQt::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element*) const
568 {
569 // Ditch the border.
570 style->resetBorder();
571
572 #ifdef Q_WS_MAC
573 if (style->appearance() == PushButtonPart) {
574 // The Mac ports ignore the specified height for <input type="button"> elements
575 // unless a border and/or background CSS property is also specified.
576 style->setHeight(Length(Auto));
577 }
578 #endif
579
580 FontDescription fontDescription = style->fontDescription();
581 fontDescription.setIsAbsoluteSize(true);
582
583 #ifdef Q_WS_MAC // Use fixed font size and family on Mac (like Safari does)
584 fontDescription.setSpecifiedSize(m_buttonFontPixelSize);
585 fontDescription.setComputedSize(m_buttonFontPixelSize);
586 #else
587 fontDescription.setSpecifiedSize(style->fontSize());
588 fontDescription.setComputedSize(style->fontSize());
589 #endif
590
591 #if !USE(QT_MOBILE_THEME)
592 FontFamily fontFamily;
593 fontFamily.setFamily(m_buttonFontFamily);
594 fontDescription.setFamily(fontFamily);
595 style->setFontDescription(fontDescription);
596 style->font().update(selector->fontSelector());
597 #endif
598 style->setLineHeight(RenderStyle::initialLineHeight());
599 setButtonSize(style);
600 setButtonPadding(style);
601 }
602
setButtonSize(RenderStyle * style) const603 void RenderThemeQt::setButtonSize(RenderStyle* style) const
604 {
605 computeSizeBasedOnStyle(style);
606 }
607
608 #if !USE(QT_MOBILE_THEME)
setButtonPadding(RenderStyle * style) const609 void RenderThemeQt::setButtonPadding(RenderStyle* style) const
610 {
611 QStyleOptionButton styleOption;
612 styleOption.state |= QStyle::State_Small;
613
614 // Fake a button rect here, since we're just computing deltas
615 QRect originalRect = QRect(0, 0, 100, 30);
616 styleOption.rect = originalRect;
617
618 // Default padding is based on the button margin pixel metric
619 int buttonMargin = qStyle()->pixelMetric(QStyle::PM_ButtonMargin, &styleOption, 0);
620 int paddingLeft = buttonMargin;
621 int paddingRight = buttonMargin;
622 int paddingTop = buttonMargin;
623 int paddingBottom = buttonMargin;
624
625 // Then check if the style uses layout margins
626 QRect layoutRect = qStyle()->subElementRect(QStyle::SE_PushButtonLayoutItem,
627 &styleOption, 0);
628 if (!layoutRect.isNull()) {
629 QRect contentsRect = qStyle()->subElementRect(QStyle::SE_PushButtonContents,
630 &styleOption, 0);
631 paddingLeft = contentsRect.left() - layoutRect.left();
632 paddingRight = layoutRect.right() - contentsRect.right();
633 paddingTop = contentsRect.top() - layoutRect.top();
634
635 // Can't use this right now because we don't have the baseline to compensate
636 // paddingBottom = layoutRect.bottom() - contentsRect.bottom();
637 }
638 style->setPaddingLeft(Length(paddingLeft, Fixed));
639 style->setPaddingRight(Length(paddingRight, Fixed));
640 style->setPaddingTop(Length(paddingTop, Fixed));
641 style->setPaddingBottom(Length(paddingBottom, Fixed));
642 }
643 #else
setButtonPadding(RenderStyle * style) const644 void RenderThemeQt::setButtonPadding(RenderStyle* style) const
645 {
646 if (!style)
647 return;
648 style->setPaddingLeft(Length(buttonPaddingLeft, Fixed));
649 style->setPaddingRight(Length(buttonPaddingRight, Fixed));
650 style->setPaddingTop(Length(buttonPaddingTop, Fixed));
651 style->setPaddingBottom(Length(buttonPaddingBottom, Fixed));
652 }
653 #endif
654
paintButton(RenderObject * o,const PaintInfo & i,const IntRect & r)655 bool RenderThemeQt::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& r)
656 {
657 StylePainter p(this, i);
658 if (!p.isValid())
659 return true;
660
661 QStyleOptionButton option;
662 initStyleOption(p.widget, option);
663 option.rect = r;
664 option.state |= QStyle::State_Small;
665
666 ControlPart appearance = initializeCommonQStyleOptions(option, o);
667 if (appearance == PushButtonPart || appearance == ButtonPart) {
668 option.rect = inflateButtonRect(option.rect, qStyle());
669 p.drawControl(QStyle::CE_PushButton, option);
670 } else if (appearance == RadioPart)
671 p.drawControl(QStyle::CE_RadioButton, option);
672 else if (appearance == CheckboxPart)
673 p.drawControl(QStyle::CE_CheckBox, option);
674
675 return false;
676 }
677
adjustTextFieldStyle(CSSStyleSelector *,RenderStyle * style,Element *) const678 void RenderThemeQt::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
679 {
680 // Resetting the style like this leads to differences like:
681 // - RenderTextControl {INPUT} at (2,2) size 168x25 [bgcolor=#FFFFFF] border: (2px inset #000000)]
682 // + RenderTextControl {INPUT} at (2,2) size 166x26
683 // in layout tests when a CSS style is applied that doesn't affect background color, border or
684 // padding. Just worth keeping in mind!
685 style->setBackgroundColor(Color::transparent);
686 style->resetBorder();
687 style->resetPadding();
688 computeSizeBasedOnStyle(style);
689 #if USE(QT_MOBILE_THEME)
690 style->setPaddingLeft(Length(textFieldPadding, Fixed));
691 style->setPaddingRight(Length(textFieldPadding, Fixed));
692 #endif
693 }
694
paintTextField(RenderObject * o,const PaintInfo & i,const IntRect & r)695 bool RenderThemeQt::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& r)
696 {
697 StylePainter p(this, i);
698 if (!p.isValid())
699 return true;
700
701 QStyleOptionFrameV2 panel;
702 initStyleOption(p.widget, panel);
703 panel.rect = r;
704 panel.lineWidth = findFrameLineWidth(qStyle());
705 #if USE(QT_MOBILE_THEME)
706 if (isPressed(o))
707 panel.state |= QStyle::State_Sunken;
708 #else
709 panel.state |= QStyle::State_Sunken;
710 #endif
711 panel.features = QStyleOptionFrameV2::None;
712
713 // Get the correct theme data for a text field
714 ControlPart appearance = initializeCommonQStyleOptions(panel, o);
715 if (appearance != TextFieldPart
716 && appearance != SearchFieldPart
717 && appearance != TextAreaPart
718 && appearance != ListboxPart)
719 return true;
720
721 // Now paint the text field.
722 p.drawPrimitive(QStyle::PE_PanelLineEdit, panel);
723 return false;
724 }
725
adjustTextAreaStyle(CSSStyleSelector * selector,RenderStyle * style,Element * element) const726 void RenderThemeQt::adjustTextAreaStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const
727 {
728 adjustTextFieldStyle(selector, style, element);
729 }
730
paintTextArea(RenderObject * o,const PaintInfo & i,const IntRect & r)731 bool RenderThemeQt::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r)
732 {
733 return paintTextField(o, i, r);
734 }
735
adjustMenuListStyle(CSSStyleSelector *,RenderStyle * style,Element *) const736 void RenderThemeQt::adjustMenuListStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
737 {
738 style->resetBorder();
739
740 // Height is locked to auto.
741 style->setHeight(Length(Auto));
742
743 // White-space is locked to pre
744 style->setWhiteSpace(PRE);
745
746 computeSizeBasedOnStyle(style);
747
748 // Add in the padding that we'd like to use.
749 setPopupPadding(style);
750 #if USE(QT_MOBILE_THEME)
751 style->setPaddingLeft(Length(menuListPadding, Fixed));
752 #endif
753 }
754
setPopupPadding(RenderStyle * style) const755 void RenderThemeQt::setPopupPadding(RenderStyle* style) const
756 {
757 const int paddingLeft = 4;
758 const int paddingRight = style->width().isFixed() || style->width().isPercent() ? 5 : 8;
759
760 style->setPaddingLeft(Length(paddingLeft, Fixed));
761
762 QStyleOptionComboBox opt;
763 int w = qStyle()->pixelMetric(QStyle::PM_ButtonIconSize, &opt, 0);
764 style->setPaddingRight(Length(paddingRight + w, Fixed));
765
766 style->setPaddingTop(Length(2, Fixed));
767 style->setPaddingBottom(Length(2, Fixed));
768 }
769
770
paintMenuList(RenderObject * o,const PaintInfo & i,const IntRect & r)771 bool RenderThemeQt::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& r)
772 {
773 StylePainter p(this, i);
774 if (!p.isValid())
775 return true;
776
777 QtStyleOptionWebComboBox opt(o);
778 initStyleOption(p.widget, opt);
779 initializeCommonQStyleOptions(opt, o);
780
781 IntRect rect = r;
782
783 #if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
784 // QMacStyle makes the combo boxes a little bit smaller to leave space for the focus rect.
785 // 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
786 // overlapped with the text. This will force QMacStyle to draw the combo box with the expected width.
787 if (qobject_cast<QMacStyle*>(p.style))
788 rect.inflateX(3);
789 #endif
790
791 const QPoint topLeft = rect.location();
792 p.painter->translate(topLeft);
793 opt.rect.moveTo(QPoint(0, 0));
794 opt.rect.setSize(rect.size());
795
796 p.drawComplexControl(QStyle::CC_ComboBox, opt);
797 p.painter->translate(-topLeft);
798 return false;
799 }
800
adjustMenuListButtonStyle(CSSStyleSelector *,RenderStyle * style,Element *) const801 void RenderThemeQt::adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
802 {
803 #if USE(QT_MOBILE_THEME)
804 // Mobile theme uses border radius.
805 #else
806 // WORKAROUND because html.css specifies -webkit-border-radius for <select> so we override it here
807 // see also http://bugs.webkit.org/show_bug.cgi?id=18399
808 style->resetBorderRadius();
809 #endif
810
811 // Height is locked to auto.
812 style->setHeight(Length(Auto));
813
814 // White-space is locked to pre
815 style->setWhiteSpace(PRE);
816
817 computeSizeBasedOnStyle(style);
818
819 // Add in the padding that we'd like to use.
820 setPopupPadding(style);
821 }
822
paintMenuListButton(RenderObject * o,const PaintInfo & i,const IntRect & r)823 bool RenderThemeQt::paintMenuListButton(RenderObject* o, const PaintInfo& i,
824 const IntRect& r)
825 {
826 StylePainter p(this, i);
827 if (!p.isValid())
828 return true;
829
830 QtStyleOptionWebComboBox option(o);
831 initStyleOption(p.widget, option);
832 initializeCommonQStyleOptions(option, o);
833 option.rect = r;
834
835 // for drawing the combo box arrow, rely only on the fallback style
836 p.style = fallbackStyle();
837 option.subControls = QStyle::SC_ComboBoxArrow;
838 p.drawComplexControl(QStyle::CC_ComboBox, option);
839
840 return false;
841 }
842
843 #if ENABLE(PROGRESS_TAG)
animationRepeatIntervalForProgressBar(RenderProgress * renderProgress) const844 double RenderThemeQt::animationRepeatIntervalForProgressBar(RenderProgress* renderProgress) const
845 {
846 if (renderProgress->position() >= 0)
847 return 0;
848
849 // FIXME: Use hard-coded value until http://bugreports.qt.nokia.com/browse/QTBUG-9171 is fixed.
850 // Use the value from windows style which is 10 fps.
851 return 0.1;
852 }
853
animationDurationForProgressBar(RenderProgress * renderProgress) const854 double RenderThemeQt::animationDurationForProgressBar(RenderProgress* renderProgress) const
855 {
856 if (renderProgress->position() >= 0)
857 return 0;
858
859 QStyleOptionProgressBarV2 option;
860 option.rect.setSize(renderProgress->size());
861 // FIXME: Until http://bugreports.qt.nokia.com/browse/QTBUG-9171 is fixed,
862 // we simulate one square animating across the progress bar.
863 return (option.rect.width() / qStyle()->pixelMetric(QStyle::PM_ProgressBarChunkWidth, &option)) * animationRepeatIntervalForProgressBar(renderProgress);
864 }
865
adjustProgressBarStyle(CSSStyleSelector *,RenderStyle * style,Element *) const866 void RenderThemeQt::adjustProgressBarStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
867 {
868 style->setBoxShadow(nullptr);
869 }
870
paintProgressBar(RenderObject * o,const PaintInfo & pi,const IntRect & r)871 bool RenderThemeQt::paintProgressBar(RenderObject* o, const PaintInfo& pi, const IntRect& r)
872 {
873 if (!o->isProgress())
874 return true;
875
876 StylePainter p(this, pi);
877 if (!p.isValid())
878 return true;
879
880 QStyleOptionProgressBarV2 option;
881 initStyleOption(p.widget, option);
882 initializeCommonQStyleOptions(option, o);
883
884 RenderProgress* renderProgress = toRenderProgress(o);
885 option.rect = r;
886 option.maximum = std::numeric_limits<int>::max();
887 option.minimum = 0;
888 option.progress = (renderProgress->position() * std::numeric_limits<int>::max());
889
890 const QPoint topLeft = r.location();
891 p.painter->translate(topLeft);
892 option.rect.moveTo(QPoint(0, 0));
893 option.rect.setSize(r.size());
894
895 if (option.progress < 0) {
896 // FIXME: Until http://bugreports.qt.nokia.com/browse/QTBUG-9171 is fixed,
897 // we simulate one square animating across the progress bar.
898 p.drawControl(QStyle::CE_ProgressBarGroove, option);
899 int chunkWidth = qStyle()->pixelMetric(QStyle::PM_ProgressBarChunkWidth, &option);
900 QColor color = (option.palette.highlight() == option.palette.background()) ? option.palette.color(QPalette::Active, QPalette::Highlight) : option.palette.color(QPalette::Highlight);
901 if (renderProgress->style()->direction() == RTL)
902 p.painter->fillRect(option.rect.right() - chunkWidth - renderProgress->animationProgress() * option.rect.width(), 0, chunkWidth, option.rect.height(), color);
903 else
904 p.painter->fillRect(renderProgress->animationProgress() * option.rect.width(), 0, chunkWidth, option.rect.height(), color);
905 } else
906 p.drawControl(QStyle::CE_ProgressBar, option);
907
908 p.painter->translate(-topLeft);
909
910 return false;
911 }
912 #endif
913
paintSliderTrack(RenderObject * o,const PaintInfo & pi,const IntRect & r)914 bool RenderThemeQt::paintSliderTrack(RenderObject* o, const PaintInfo& pi,
915 const IntRect& r)
916 {
917 StylePainter p(this, pi);
918 if (!p.isValid())
919 return true;
920
921 QStyleOptionSlider option;
922 initStyleOption(p.widget, option);
923 option.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle;
924 ControlPart appearance = initializeCommonQStyleOptions(option, o);
925
926 RenderSlider* renderSlider = toRenderSlider(o);
927 IntRect thumbRect = renderSlider->thumbRect();
928
929 option.rect = r;
930
931 int value;
932 if (appearance == SliderVerticalPart) {
933 option.maximum = r.height() - thumbRect.height();
934 value = thumbRect.y();
935 } else {
936 option.maximum = r.width() - thumbRect.width();
937 value = thumbRect.x();
938 }
939
940 value = QStyle::sliderValueFromPosition(0, option.maximum, value, option.maximum);
941
942 option.sliderValue = value;
943 option.sliderPosition = value;
944 if (appearance == SliderVerticalPart)
945 option.orientation = Qt::Vertical;
946
947 if (renderSlider->inDragMode()) {
948 option.activeSubControls = QStyle::SC_SliderHandle;
949 option.state |= QStyle::State_Sunken;
950 }
951
952 const QPoint topLeft = r.location();
953 p.painter->translate(topLeft);
954 option.rect.moveTo(QPoint(0, 0));
955 option.rect.setSize(r.size());
956
957 p.drawComplexControl(QStyle::CC_Slider, option);
958 p.painter->translate(-topLeft);
959
960 return false;
961 }
962
adjustSliderTrackStyle(CSSStyleSelector *,RenderStyle * style,Element *) const963 void RenderThemeQt::adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
964 {
965 style->setBoxShadow(nullptr);
966 }
967
paintSliderThumb(RenderObject * o,const PaintInfo & pi,const IntRect & r)968 bool RenderThemeQt::paintSliderThumb(RenderObject* o, const PaintInfo& pi,
969 const IntRect& r)
970 {
971 // We've already painted it in paintSliderTrack(), no need to do anything here.
972 return false;
973 }
974
adjustSliderThumbStyle(CSSStyleSelector *,RenderStyle * style,Element *) const975 void RenderThemeQt::adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
976 {
977 style->setBoxShadow(nullptr);
978 }
979
paintSearchField(RenderObject * o,const PaintInfo & pi,const IntRect & r)980 bool RenderThemeQt::paintSearchField(RenderObject* o, const PaintInfo& pi,
981 const IntRect& r)
982 {
983 return paintTextField(o, pi, r);
984 }
985
adjustSearchFieldStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const986 void RenderThemeQt::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style,
987 Element* e) const
988 {
989 // Resetting the style like this leads to differences like:
990 // - RenderTextControl {INPUT} at (2,2) size 168x25 [bgcolor=#FFFFFF] border: (2px inset #000000)]
991 // + RenderTextControl {INPUT} at (2,2) size 166x26
992 // in layout tests when a CSS style is applied that doesn't affect background color, border or
993 // padding. Just worth keeping in mind!
994 style->setBackgroundColor(Color::transparent);
995 style->resetBorder();
996 style->resetPadding();
997 computeSizeBasedOnStyle(style);
998 }
999
adjustSearchFieldCancelButtonStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const1000 void RenderThemeQt::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style,
1001 Element* e) const
1002 {
1003 // Logic taken from RenderThemeChromium.cpp.
1004 // Scale the button size based on the font size.
1005 float fontScale = style->fontSize() / defaultControlFontPixelSize;
1006 int cancelButtonSize = lroundf(qMin(qMax(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize));
1007 style->setWidth(Length(cancelButtonSize, Fixed));
1008 style->setHeight(Length(cancelButtonSize, Fixed));
1009 }
1010
1011 // Function taken from RenderThemeChromium.cpp
convertToPaintingRect(RenderObject * inputRenderer,const RenderObject * partRenderer,IntRect partRect,const IntRect & localOffset) const1012 IntRect RenderThemeQt::convertToPaintingRect(RenderObject* inputRenderer, const RenderObject* partRenderer, IntRect partRect, const IntRect& localOffset) const
1013 {
1014 // Compute an offset between the part renderer and the input renderer.
1015 IntSize offsetFromInputRenderer = -(partRenderer->offsetFromAncestorContainer(inputRenderer));
1016 // Move the rect into partRenderer's coords.
1017 partRect.move(offsetFromInputRenderer);
1018 // Account for the local drawing offset.
1019 partRect.move(localOffset.x(), localOffset.y());
1020
1021 return partRect;
1022 }
1023
paintSearchFieldCancelButton(RenderObject * o,const PaintInfo & pi,const IntRect & r)1024 bool RenderThemeQt::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& pi,
1025 const IntRect& r)
1026 {
1027 // Logic copied from RenderThemeChromium.cpp.
1028
1029 // Get the renderer of <input> element.
1030 Node* input = o->node()->shadowAncestorNode();
1031 if (!input->renderer()->isBox())
1032 return false;
1033 RenderBox* inputRenderBox = toRenderBox(input->renderer());
1034 IntRect inputContentBox = inputRenderBox->contentBoxRect();
1035
1036 // Make sure the scaled button stays square and will fit in its parent's box.
1037 int cancelButtonSize = qMin(inputContentBox.width(), qMin(inputContentBox.height(), r.height()));
1038 // Calculate cancel button's coordinates relative to the input element.
1039 // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will
1040 // be one pixel closer to the bottom of the field. This tends to look better with the text.
1041 IntRect cancelButtonRect(o->offsetFromAncestorContainer(inputRenderBox).width(),
1042 inputContentBox.y() + (inputContentBox.height() - cancelButtonSize + 1) / 2,
1043 cancelButtonSize, cancelButtonSize);
1044 IntRect paintingRect = convertToPaintingRect(inputRenderBox, o, cancelButtonRect, r);
1045 static Image* cancelImage = Image::loadPlatformResource("searchCancelButton").releaseRef();
1046 static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelButtonPressed").releaseRef();
1047 pi.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage,
1048 o->style()->colorSpace(), paintingRect);
1049 return false;
1050 }
1051
adjustSearchFieldDecorationStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const1052 void RenderThemeQt::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style,
1053 Element* e) const
1054 {
1055 notImplemented();
1056 RenderTheme::adjustSearchFieldDecorationStyle(selector, style, e);
1057 }
1058
paintSearchFieldDecoration(RenderObject * o,const PaintInfo & pi,const IntRect & r)1059 bool RenderThemeQt::paintSearchFieldDecoration(RenderObject* o, const PaintInfo& pi,
1060 const IntRect& r)
1061 {
1062 notImplemented();
1063 return RenderTheme::paintSearchFieldDecoration(o, pi, r);
1064 }
1065
adjustSearchFieldResultsDecorationStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const1066 void RenderThemeQt::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style,
1067 Element* e) const
1068 {
1069 notImplemented();
1070 RenderTheme::adjustSearchFieldResultsDecorationStyle(selector, style, e);
1071 }
1072
paintSearchFieldResultsDecoration(RenderObject * o,const PaintInfo & pi,const IntRect & r)1073 bool RenderThemeQt::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& pi,
1074 const IntRect& r)
1075 {
1076 notImplemented();
1077 return RenderTheme::paintSearchFieldResultsDecoration(o, pi, r);
1078 }
1079
supportsFocus(ControlPart appearance) const1080 bool RenderThemeQt::supportsFocus(ControlPart appearance) const
1081 {
1082 switch (appearance) {
1083 case PushButtonPart:
1084 case ButtonPart:
1085 case TextFieldPart:
1086 case TextAreaPart:
1087 case ListboxPart:
1088 case MenulistPart:
1089 case RadioPart:
1090 case CheckboxPart:
1091 case SliderHorizontalPart:
1092 case SliderVerticalPart:
1093 return true;
1094 default: // No for all others...
1095 return false;
1096 }
1097 }
1098
setPaletteFromPageClientIfExists(QPalette & palette) const1099 void RenderThemeQt::setPaletteFromPageClientIfExists(QPalette& palette) const
1100 {
1101 #if USE(QT_MOBILE_THEME)
1102 static QPalette lightGrayPalette(Qt::lightGray);
1103 palette = lightGrayPalette;
1104 return;
1105 #endif
1106 // If the webview has a custom palette, use it
1107 if (!m_page)
1108 return;
1109 Chrome* chrome = m_page->chrome();
1110 if (!chrome)
1111 return;
1112 ChromeClient* chromeClient = chrome->client();
1113 if (!chromeClient)
1114 return;
1115 QWebPageClient* pageClient = chromeClient->platformPageClient();
1116 if (!pageClient)
1117 return;
1118 palette = pageClient->palette();
1119 }
1120
initializeCommonQStyleOptions(QStyleOption & option,RenderObject * o) const1121 ControlPart RenderThemeQt::initializeCommonQStyleOptions(QStyleOption& option, RenderObject* o) const
1122 {
1123 // Default bits: no focus, no mouse over
1124 option.state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
1125
1126 if (isReadOnlyControl(o))
1127 // Readonly is supported on textfields.
1128 option.state |= QStyle::State_ReadOnly;
1129
1130 option.direction = Qt::LeftToRight;
1131
1132 if (isHovered(o))
1133 option.state |= QStyle::State_MouseOver;
1134
1135 setPaletteFromPageClientIfExists(option.palette);
1136
1137 if (!isEnabled(o)) {
1138 option.palette.setCurrentColorGroup(QPalette::Disabled);
1139 option.state &= ~QStyle::State_Enabled;
1140 }
1141
1142 RenderStyle* style = o->style();
1143 if (!style)
1144 return NoControlPart;
1145
1146 ControlPart result = style->appearance();
1147 if (supportsFocus(result) && isFocused(o)) {
1148 option.state |= QStyle::State_HasFocus;
1149 option.state |= QStyle::State_KeyboardFocusChange;
1150 }
1151
1152 if (style->direction() == WebCore::RTL)
1153 option.direction = Qt::RightToLeft;
1154
1155 switch (result) {
1156 case PushButtonPart:
1157 case SquareButtonPart:
1158 case ButtonPart:
1159 case ButtonBevelPart:
1160 case ListItemPart:
1161 case MenulistButtonPart:
1162 case SearchFieldResultsButtonPart:
1163 case SearchFieldCancelButtonPart: {
1164 if (isPressed(o))
1165 option.state |= QStyle::State_Sunken;
1166 else if (result == PushButtonPart || result == ButtonPart)
1167 option.state |= QStyle::State_Raised;
1168 break;
1169 }
1170 case RadioPart:
1171 case CheckboxPart:
1172 option.state |= (isChecked(o) ? QStyle::State_On : QStyle::State_Off);
1173 }
1174
1175 return result;
1176 }
1177
1178 #if ENABLE(VIDEO)
1179
extraMediaControlsStyleSheet()1180 String RenderThemeQt::extraMediaControlsStyleSheet()
1181 {
1182 String result = String(mediaControlsQtUserAgentStyleSheet, sizeof(mediaControlsQtUserAgentStyleSheet));
1183
1184 if (m_page && m_page->chrome()->requiresFullscreenForVideoPlayback())
1185 result += String(mediaControlsQtFullscreenUserAgentStyleSheet, sizeof(mediaControlsQtFullscreenUserAgentStyleSheet));
1186
1187 return result;
1188 }
1189
1190 // Helper class to transform the painter's world matrix to the object's content area, scaled to 0,0,100,100
1191 class WorldMatrixTransformer {
1192 public:
WorldMatrixTransformer(QPainter * painter,RenderObject * renderObject,const IntRect & r)1193 WorldMatrixTransformer(QPainter* painter, RenderObject* renderObject, const IntRect& r) : m_painter(painter)
1194 {
1195 RenderStyle* style = renderObject->style();
1196 m_originalTransform = m_painter->transform();
1197 m_painter->translate(r.x() + style->paddingLeft().value(), r.y() + style->paddingTop().value());
1198 m_painter->scale((r.width() - style->paddingLeft().value() - style->paddingRight().value()) / 100.0,
1199 (r.height() - style->paddingTop().value() - style->paddingBottom().value()) / 100.0);
1200 }
1201
~WorldMatrixTransformer()1202 ~WorldMatrixTransformer() { m_painter->setTransform(m_originalTransform); }
1203
1204 private:
1205 QPainter* m_painter;
1206 QTransform m_originalTransform;
1207 };
1208
mediaControlsBaselineOpacity() const1209 double RenderThemeQt::mediaControlsBaselineOpacity() const
1210 {
1211 return 0.4;
1212 }
1213
paintMediaBackground(QPainter * painter,const IntRect & r) const1214 void RenderThemeQt::paintMediaBackground(QPainter* painter, const IntRect& r) const
1215 {
1216 painter->setPen(Qt::NoPen);
1217 static QColor transparentBlack(0, 0, 0, mediaControlsBaselineOpacity() * 255);
1218 painter->setBrush(transparentBlack);
1219 painter->drawRoundedRect(r.x(), r.y(), r.width(), r.height(), 5.0, 5.0);
1220 }
1221
mediaElementCanPlay(RenderObject * o)1222 static bool mediaElementCanPlay(RenderObject* o)
1223 {
1224 HTMLMediaElement* mediaElement = toParentMediaElement(o);
1225 if (!mediaElement)
1226 return false;
1227
1228 return mediaElement->readyState() > HTMLMediaElement::HAVE_METADATA
1229 || (mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING
1230 && o->style()->appearance() == MediaPlayButtonPart && mediaElement->preload() == "none");
1231 }
1232
getMediaControlForegroundColor(RenderObject * o) const1233 QColor RenderThemeQt::getMediaControlForegroundColor(RenderObject* o) const
1234 {
1235 QColor fgColor = platformActiveSelectionBackgroundColor();
1236 if (!o)
1237 return fgColor;
1238
1239 if (o->node()->active())
1240 fgColor = fgColor.lighter();
1241
1242 if (!mediaElementCanPlay(o)) {
1243 QPalette pal = QApplication::palette();
1244 setPaletteFromPageClientIfExists(pal);
1245 fgColor = pal.brush(QPalette::Disabled, QPalette::Text).color();
1246 }
1247
1248 return fgColor;
1249 }
1250
paintMediaFullscreenButton(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)1251 bool RenderThemeQt::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1252 {
1253 HTMLMediaElement* mediaElement = toParentMediaElement(o);
1254 if (!mediaElement)
1255 return false;
1256
1257 StylePainter p(this, paintInfo);
1258 if (!p.isValid())
1259 return true;
1260
1261 p.painter->setRenderHint(QPainter::Antialiasing, true);
1262
1263 paintMediaBackground(p.painter, r);
1264
1265 WorldMatrixTransformer transformer(p.painter, o, r);
1266 const QPointF arrowPolygon[9] = { QPointF(20, 0), QPointF(100, 0), QPointF(100, 80),
1267 QPointF(80, 80), QPointF(80, 30), QPointF(10, 100), QPointF(0, 90), QPointF(70, 20), QPointF(20, 20)};
1268
1269 p.painter->setBrush(getMediaControlForegroundColor(o));
1270 p.painter->drawPolygon(arrowPolygon, 9);
1271
1272 return false;
1273 }
1274
paintMediaMuteButton(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)1275 bool RenderThemeQt::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1276 {
1277 HTMLMediaElement* mediaElement = toParentMediaElement(o);
1278 if (!mediaElement)
1279 return false;
1280
1281 StylePainter p(this, paintInfo);
1282 if (!p.isValid())
1283 return true;
1284
1285 p.painter->setRenderHint(QPainter::Antialiasing, true);
1286
1287 paintMediaBackground(p.painter, r);
1288
1289 WorldMatrixTransformer transformer(p.painter, o, r);
1290 const QPointF speakerPolygon[6] = { QPointF(20, 30), QPointF(50, 30), QPointF(80, 0),
1291 QPointF(80, 100), QPointF(50, 70), QPointF(20, 70)};
1292
1293 p.painter->setBrush(mediaElement->muted() ? Qt::darkRed : getMediaControlForegroundColor(o));
1294 p.painter->drawPolygon(speakerPolygon, 6);
1295
1296 return false;
1297 }
1298
paintMediaPlayButton(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)1299 bool RenderThemeQt::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1300 {
1301 HTMLMediaElement* mediaElement = toParentMediaElement(o);
1302 if (!mediaElement)
1303 return false;
1304
1305 StylePainter p(this, paintInfo);
1306 if (!p.isValid())
1307 return true;
1308
1309 p.painter->setRenderHint(QPainter::Antialiasing, true);
1310
1311 paintMediaBackground(p.painter, r);
1312
1313 WorldMatrixTransformer transformer(p.painter, o, r);
1314 p.painter->setBrush(getMediaControlForegroundColor(o));
1315 if (mediaElement->canPlay()) {
1316 const QPointF playPolygon[3] = { QPointF(0, 0), QPointF(100, 50), QPointF(0, 100)};
1317 p.painter->drawPolygon(playPolygon, 3);
1318 } else {
1319 p.painter->drawRect(0, 0, 30, 100);
1320 p.painter->drawRect(70, 0, 30, 100);
1321 }
1322
1323 return false;
1324 }
1325
paintMediaSeekBackButton(RenderObject *,const PaintInfo &,const IntRect &)1326 bool RenderThemeQt::paintMediaSeekBackButton(RenderObject*, const PaintInfo&, const IntRect&)
1327 {
1328 // We don't want to paint this at the moment.
1329 return false;
1330 }
1331
paintMediaSeekForwardButton(RenderObject *,const PaintInfo &,const IntRect &)1332 bool RenderThemeQt::paintMediaSeekForwardButton(RenderObject*, const PaintInfo&, const IntRect&)
1333 {
1334 // We don't want to paint this at the moment.
1335 return false;
1336 }
1337
paintMediaCurrentTime(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)1338 bool RenderThemeQt::paintMediaCurrentTime(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1339 {
1340 StylePainter p(this, paintInfo);
1341 if (!p.isValid())
1342 return true;
1343
1344 p.painter->setRenderHint(QPainter::Antialiasing, true);
1345 paintMediaBackground(p.painter, r);
1346
1347 return false;
1348 }
1349
formatMediaControlsCurrentTime(float currentTime,float duration) const1350 String RenderThemeQt::formatMediaControlsCurrentTime(float currentTime, float duration) const
1351 {
1352 return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration);
1353 }
1354
formatMediaControlsRemainingTime(float currentTime,float duration) const1355 String RenderThemeQt::formatMediaControlsRemainingTime(float currentTime, float duration) const
1356 {
1357 return String();
1358 }
1359
paintMediaVolumeSliderTrack(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)1360 bool RenderThemeQt::paintMediaVolumeSliderTrack(RenderObject *o, const PaintInfo &paintInfo, const IntRect &r)
1361 {
1362 StylePainter p(this, paintInfo);
1363 if (!p.isValid())
1364 return true;
1365
1366 p.painter->setRenderHint(QPainter::Antialiasing, true);
1367
1368 paintMediaBackground(p.painter, r);
1369
1370 if (!o->isSlider())
1371 return false;
1372
1373 IntRect b = toRenderBox(o)->contentBoxRect();
1374
1375 // Position the outer rectangle
1376 int top = r.y() + b.y();
1377 int left = r.x() + b.x();
1378 int width = b.width();
1379 int height = b.height();
1380
1381 // Get the scale color from the page client
1382 QPalette pal = QApplication::palette();
1383 setPaletteFromPageClientIfExists(pal);
1384 const QColor highlightText = pal.brush(QPalette::Active, QPalette::HighlightedText).color();
1385 const QColor scaleColor(highlightText.red(), highlightText.green(), highlightText.blue(), mediaControlsBaselineOpacity() * 255);
1386
1387 // Draw the outer rectangle
1388 p.painter->setBrush(scaleColor);
1389 p.painter->drawRect(left, top, width, height);
1390
1391 if (!o->node() || !o->node()->hasTagName(inputTag))
1392 return false;
1393
1394 HTMLInputElement* slider = static_cast<HTMLInputElement*>(o->node());
1395
1396 // Position the inner rectangle
1397 height = height * slider->valueAsNumber();
1398 top += b.height() - height;
1399
1400 // Draw the inner rectangle
1401 p.painter->setPen(Qt::NoPen);
1402 p.painter->setBrush(getMediaControlForegroundColor(o));
1403 p.painter->drawRect(left, top, width, height);
1404
1405 return false;
1406 }
1407
paintMediaVolumeSliderThumb(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)1408 bool RenderThemeQt::paintMediaVolumeSliderThumb(RenderObject *o, const PaintInfo &paintInfo, const IntRect &r)
1409 {
1410 StylePainter p(this, paintInfo);
1411 if (!p.isValid())
1412 return true;
1413
1414 // Nothing to draw here, this is all done in the track
1415 return false;
1416 }
1417
paintMediaSliderTrack(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)1418 bool RenderThemeQt::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1419 {
1420 HTMLMediaElement* mediaElement = toParentMediaElement(o);
1421 if (!mediaElement)
1422 return false;
1423
1424 StylePainter p(this, paintInfo);
1425 if (!p.isValid())
1426 return true;
1427
1428 p.painter->setRenderHint(QPainter::Antialiasing, true);
1429
1430 paintMediaBackground(p.painter, r);
1431
1432 if (MediaPlayer* player = mediaElement->player()) {
1433 // Get the buffered parts of the media
1434 PassRefPtr<TimeRanges> buffered = player->buffered();
1435 if (buffered->length() > 0 && player->duration() < std::numeric_limits<float>::infinity()) {
1436 // Set the transform and brush
1437 WorldMatrixTransformer transformer(p.painter, o, r);
1438 p.painter->setBrush(getMediaControlForegroundColor());
1439
1440 // Paint each buffered section
1441 ExceptionCode ex;
1442 for (int i = 0; i < buffered->length(); i++) {
1443 float startX = (buffered->start(i, ex) / player->duration()) * 100;
1444 float width = ((buffered->end(i, ex) / player->duration()) * 100) - startX;
1445 p.painter->drawRect(startX, 37, width, 26);
1446 }
1447 }
1448 }
1449
1450 return false;
1451 }
1452
paintMediaSliderThumb(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)1453 bool RenderThemeQt::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1454 {
1455 if (!o->parent()->isSlider())
1456 return false;
1457
1458 // We can get the HTMLMediaElement from the parent of the thumb : MediaControlTimelineElement.
1459 HTMLMediaElement* mediaElement = toParentMediaElement(o->parent());
1460 if (!mediaElement)
1461 return false;
1462
1463 StylePainter p(this, paintInfo);
1464 if (!p.isValid())
1465 return true;
1466
1467 p.painter->setRenderHint(QPainter::Antialiasing, true);
1468
1469 p.painter->setPen(Qt::NoPen);
1470 p.painter->setBrush(getMediaControlForegroundColor(o->parent()));
1471 p.painter->drawRect(r.x(), r.y(), r.width(), r.height());
1472
1473 return false;
1474 }
1475 #endif
1476
adjustSliderThumbSize(RenderObject * o) const1477 void RenderThemeQt::adjustSliderThumbSize(RenderObject* o) const
1478 {
1479 ControlPart part = o->style()->appearance();
1480
1481 if (part == MediaSliderThumbPart) {
1482 RenderStyle* parentStyle = o->parent()->style();
1483 Q_ASSERT(parentStyle);
1484
1485 int parentHeight = parentStyle->height().value();
1486 o->style()->setWidth(Length(parentHeight / 3, Fixed));
1487 o->style()->setHeight(Length(parentHeight, Fixed));
1488 } else if (part == MediaVolumeSliderThumbPart) {
1489 RenderStyle* parentStyle = o->parent()->style();
1490 Q_ASSERT(parentStyle);
1491
1492 int parentWidth = parentStyle->width().value();
1493 o->style()->setHeight(Length(parentWidth / 3, Fixed));
1494 o->style()->setWidth(Length(parentWidth, Fixed));
1495 } else if (part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) {
1496 QStyleOptionSlider option;
1497 if (part == SliderThumbVerticalPart)
1498 option.orientation = Qt::Vertical;
1499
1500 QStyle* style = qStyle();
1501
1502 int width = style->pixelMetric(QStyle::PM_SliderLength, &option);
1503 int height = style->pixelMetric(QStyle::PM_SliderThickness, &option);
1504 o->style()->setWidth(Length(width, Fixed));
1505 o->style()->setHeight(Length(height, Fixed));
1506 }
1507 }
1508
caretBlinkInterval() const1509 double RenderThemeQt::caretBlinkInterval() const
1510 {
1511 return QApplication::cursorFlashTime() / 1000.0 / 2.0;
1512 }
1513
1514 }
1515
1516 // vim: ts=4 sw=4 et
1517