1/* 2 * This file is part of the WebKit project. 3 * 4 * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies) 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 * 21 */ 22 23#include "config.h" 24#include "RenderThemeQtMobile.h" 25 26#include "CSSValueKeywords.h" 27#include "Color.h" 28#include "Document.h" 29#include "Font.h" 30#include "HTMLInputElement.h" 31#include "HTMLNames.h" 32#include "HTMLSelectElement.h" 33#include "LocalizedStrings.h" 34#include "NotImplemented.h" 35#include "Page.h" 36#include "PaintInfo.h" 37#include "QWebPageClient.h" 38#include "RenderBox.h" 39#if ENABLE(PROGRESS_ELEMENT) 40#include "RenderProgress.h" 41#endif 42#include "StyleResolver.h" 43 44#include <wtf/PassRefPtr.h> 45 46#include <QColor> 47#include <QFile> 48#include <QPainter> 49#include <QPixmapCache> 50 51namespace WebCore { 52 53using namespace HTMLNames; 54 55// Constants used by the mobile theme 56static const int arrowBoxWidth = 26; 57static const int frameWidth = 2; 58static const int checkBoxWidth = 21; 59static const int radioWidth = 21; 60static const int sliderSize = 20; 61static const int buttonHeightRatio = 1.5; 62 63static const float multipleComboDotsOffsetFactor = 1.8; 64static const float buttonPaddingLeft = 18; 65static const float buttonPaddingRight = 18; 66static const float buttonPaddingTop = 2; 67static const float buttonPaddingBottom = 3; 68static const float menuListPadding = 9; 69static const float textFieldPadding = 10; 70static const float radiusFactor = 0.36; 71static const float progressBarChunkPercentage = 0.2; 72#if ENABLE(PROGRESS_ELEMENT) 73static const int progressAnimationGranularity = 2; 74#endif 75static const float sliderGrooveBorderRatio = 0.2; 76static const QColor darkColor(40, 40, 40); 77static const QColor highlightColor(16, 128, 221); 78static const QColor buttonGradientBottom(245, 245, 245); 79static const QColor shadowColor(80, 80, 80, 160); 80 81static QHash<KeyIdentifier, CacheKey> cacheKeys; 82 83static qreal painterScale(QPainter* painter) 84{ 85 if (!painter) 86 return 1; 87 88 const QTransform& transform = painter->transform(); 89 qreal scale = 1; 90 91 if (transform.type() == QTransform::TxScale) 92 scale = qAbs(transform.m11()); 93 else if (transform.type() >= QTransform::TxRotate) { 94 const QLineF l1(0, 0, 1, 0); 95 const QLineF l2 = transform.map(l1); 96 scale = qAbs(l2.length() / l1.length()); 97 } 98 return scale; 99} 100 101uint qHash(const KeyIdentifier& id) 102{ 103 const quint32 value = id.trait1 + (id.trait2 << 1) + (uint(id.type) << 2) + (id.height << 5) + (id.width << 14) + (id.trait3 << 25); 104 const unsigned char* p = reinterpret_cast<const unsigned char*>(&value); 105 uint hash = 0; 106 for (int i = 0; i < 4; ++i) 107 hash ^= (hash << 5) + (hash >> 2) + p[i]; 108 return hash; 109} 110 111/* 112 * The octants' indices are identified below, for each point (x,y) 113 * in the first octant, we can populate the 7 others with the corresponding 114 * point. 115 * 116 * index | xpos | ypos 117 * xd --------------------------- 118 * 4 |<--->| 3 0 | xd + x | y 119 * __________________ 1 | xd + y | x 120 * / \ 2 | xd + y | -x 121 * 5 | .(c) | 2 3 | xd + x | -y 122 * 6 | | 1 4 | -xd - x | -y 123 * \__________________/ 5 | -xd - y | -x 124 * 6 | -xd - y | x 125 * 7 0 7 | -xd - x | y 126 * 127 **/ 128 129static void addPointToOctants(QVector<QPainterPath>& octants, const QPointF& center, qreal x, qreal y , int xDelta = 0) 130{ 131 ASSERT(octants.count() == 8); 132 133 for (short i = 0; i < 8; ++i) { 134 QPainterPath& octant = octants[i]; 135 QPointF pos(center); 136 // The Gray code corresponding to the octant's index helps doing the math in a more generic way. 137 const short gray = (i >> 1) ^ i; 138 const qreal xOffset = xDelta + ((gray & 1) ? y : x); 139 pos.ry() += ((gray & 2)? -1 : 1) * ((gray & 1) ? x : y); 140 pos.rx() += (i < 4) ? xOffset : -xOffset; 141 142 if (octant.elementCount()) 143 octant.lineTo(pos); 144 else // The path is empty. Initialize the start point. 145 octant.moveTo(pos); 146 } 147} 148 149static void drawControlBackground(QPainter* painter, const QPen& pen, const QRect& rect, const QBrush& brush) 150{ 151 QPen oldPen = painter->pen(); 152 QBrush oldBrush = painter->brush(); 153 painter->setRenderHint(QPainter::Antialiasing, true); 154 painter->setPen(pen); 155 painter->setBrush(brush); 156 157 static const qreal line = 1.5; 158 const QRectF paddedRect = rect.adjusted(line, line, -line, -line); 159 160 static const int n = 3; 161 const qreal invPow = 1 / double(n); 162 ASSERT(paddedRect.width() >= paddedRect.height()); 163 const int radius = paddedRect.height() / 2; 164 const int xDelta = paddedRect.width() / 2 - radius; 165 const QPointF center = paddedRect.center(); 166 qreal x = 0; 167 qreal y; 168 QVector<QPainterPath> octants(8); 169 // Stay within reasonable distance from edge values, which can cause artifacts at certain zoom levels. 170 static const float epsilon = 0.02; 171 for (y = radius - epsilon; y - epsilon > x; y -= 0.5) { 172 x = radius * pow(1 - pow(qAbs(y) / radius , n), invPow); 173 addPointToOctants(octants, center, x, y, xDelta); 174 } 175 176 QPainterPath path = octants.first(); 177 for (int i = 1; i < 8; ++i) { 178 // Due to the orientation of the arcs, we need to reverse the paths with odd indices. 179 QPainterPath subPath = (i % 2) ? octants.at(i).toReversed() : octants.at(i); 180 path.connectPath(subPath); 181 } 182 path.closeSubpath(); 183 184 painter->drawPath(path); 185 painter->setPen(oldPen); 186 painter->setBrush(oldBrush); 187} 188 189static inline QRect shrinkRectToSquare(const QRect& rect) 190{ 191 const int side = qMin(rect.height(), rect.width()); 192 return QRect(rect.topLeft(), QSize(side, side)); 193} 194 195static inline QPen borderPen(QPainter* painter = 0) 196{ 197 return QPen(darkColor, qMin(1.0, 0.4 * painterScale(painter))); 198} 199 200QSharedPointer<StylePainter> RenderThemeQtMobile::getStylePainter(const PaintInfo& pi) 201{ 202 return QSharedPointer<StylePainter>(new StylePainterMobile(this, pi)); 203} 204 205QPalette RenderThemeQtMobile::colorPalette() const 206{ 207 static QPalette lightGrayPalette(Qt::lightGray); 208 return lightGrayPalette; 209} 210 211StylePainterMobile::StylePainterMobile(RenderThemeQtMobile* theme, const PaintInfo& paintInfo) 212 : StylePainter(theme, paintInfo) 213{ 214 m_previousSmoothPixmapTransform = painter->testRenderHint(QPainter::SmoothPixmapTransform); 215 if (!m_previousSmoothPixmapTransform) 216 painter->setRenderHint(QPainter::SmoothPixmapTransform); 217} 218 219StylePainterMobile::~StylePainterMobile() 220{ 221 painter->setRenderHints(QPainter::SmoothPixmapTransform, m_previousSmoothPixmapTransform); 222} 223 224bool StylePainterMobile::findCachedControl(const KeyIdentifier& keyId, QPixmap* result) 225{ 226 static CacheKey emptyKey; 227 CacheKey key = cacheKeys.value(keyId, emptyKey); 228 if (key == emptyKey) 229 return false; 230 const bool ret = QPixmapCache::find(key, result); 231 if (!ret) 232 cacheKeys.remove(keyId); 233 return ret; 234} 235 236void StylePainterMobile::insertIntoCache(const KeyIdentifier& keyId, const QPixmap& pixmap) 237{ 238 ASSERT(keyId.type); 239 const int sizeInKiloBytes = pixmap.width() * pixmap.height() * pixmap.depth() / (8 * 1024); 240 // Don't cache pixmaps over 512 KB; 241 if (sizeInKiloBytes > 512) 242 return; 243 cacheKeys.insert(keyId, QPixmapCache::insert(pixmap)); 244} 245 246void StylePainterMobile::drawCheckableBackground(QPainter* painter, const QRect& rect, bool checked, bool enabled) const 247{ 248 QBrush brush; 249 QColor color = Qt::gray; 250 if (checked && enabled) 251 color = highlightColor; 252 253 QLinearGradient gradient; 254 gradient.setStart(rect.topLeft()); 255 gradient.setFinalStop(rect.bottomLeft()); 256 gradient.setColorAt(0.0, color); 257 gradient.setColorAt(1.0, color.lighter(130)); 258 brush = gradient; 259 260 drawControlBackground(painter, borderPen(painter), rect, brush); 261} 262 263QSize StylePainterMobile::sizeForPainterScale(const QRect& rect) const 264{ 265 qreal scale = painterScale(painter); 266 QTransform scaleTransform = QTransform::fromScale(scale, scale); 267 268 return scaleTransform.mapRect(rect).size(); 269} 270 271void StylePainterMobile::drawChecker(QPainter* painter, const QRect& rect, const QColor& color) const 272{ 273 painter->setRenderHint(QPainter::Antialiasing, true); 274 QPen pen(Qt::darkGray); 275 pen.setCosmetic(true); 276 painter->setPen(pen); 277 painter->scale(rect.width(), rect.height()); 278 QPainterPath path; 279 path.moveTo(0.18, 0.47); 280 path.lineTo(0.25, 0.4); 281 path.lineTo(0.4, 0.55); 282 path.quadTo(0.64, 0.29, 0.78, 0.2); 283 path.lineTo(0.8, 0.25); 284 path.quadTo(0.53, 0.55, 0.45, 0.75); 285 path.closeSubpath(); 286 painter->setBrush(color); 287 painter->drawPath(path); 288} 289 290QPixmap StylePainterMobile::findCheckBox(const QSize& size, bool checked, bool enabled) const 291{ 292 ASSERT(size.width() == size.height()); 293 QPixmap result; 294 KeyIdentifier id; 295 id.type = KeyIdentifier::CheckBox; 296 id.height = size.height(); 297 id.trait1 = enabled; 298 id.trait2 = checked; 299 if (!findCachedControl(id, &result)) { 300 result = QPixmap(size); 301 result.fill(Qt::transparent); 302 QPainter cachePainter(&result); 303 QRect rect(QPoint(0, 0), size); 304 drawCheckableBackground(&cachePainter, rect, checked, enabled); 305 if (checked || !enabled) 306 drawChecker(&cachePainter, rect, enabled ? Qt::white : Qt::gray); 307 insertIntoCache(id, result); 308 } 309 return result; 310} 311 312void StylePainterMobile::drawRadio(QPainter* painter, const QSize& size, bool checked, bool enabled) const 313{ 314 QRect rect(QPoint(0, 0), size); 315 316 drawCheckableBackground(painter, rect, checked, enabled); 317 const int border = size.width() / 4; 318 rect.adjust(border, border, -border, -border); 319 drawControlBackground(painter, borderPen(), rect, enabled ? Qt::white : Qt::gray); 320} 321 322QPixmap StylePainterMobile::findRadio(const QSize& size, bool checked, bool enabled) const 323{ 324 ASSERT(size.width() == size.height()); 325 QPixmap result; 326 KeyIdentifier id; 327 id.type = KeyIdentifier::Radio; 328 id.height = size.height(); 329 id.trait1 = enabled; 330 id.trait2 = checked; 331 if (!findCachedControl(id, &result)) { 332 result = QPixmap(size); 333 result.fill(Qt::transparent); 334 QPainter cachePainter(&result); 335 drawRadio(&cachePainter, size, checked, enabled); 336 insertIntoCache(id, result); 337 } 338 return result; 339} 340 341void StylePainterMobile::drawMultipleComboButton(QPainter* painter, const QSizeF& size, const QColor& color) const 342{ 343 const qreal dotDiameter = size.height(); 344 const qreal dotRadii = dotDiameter / 2; 345 346 painter->setRenderHint(QPainter::Antialiasing, true); 347 painter->setPen(color); 348 painter->setBrush(color); 349 350 for (int i = 0; i < 3; ++i) { 351 QPointF center(dotRadii + i * multipleComboDotsOffsetFactor * dotDiameter, dotRadii); 352 painter->drawEllipse(center, dotRadii, dotRadii); 353 } 354} 355 356void StylePainterMobile::drawSimpleComboButton(QPainter* painter, const QSizeF& size, const QColor& color) const 357{ 358 const qreal gap = size.height() / 5.0; 359 const qreal arrowHeight = (size.height() - gap) / 2.0; 360 const qreal right = arrowHeight * 2; 361 const qreal bottomBaseline = size.height() - arrowHeight; 362 QPolygonF upArrow, downArrow; 363 upArrow << QPointF(0, arrowHeight) << QPointF(arrowHeight, 0) << QPointF(right, arrowHeight); 364 downArrow << QPointF(0, bottomBaseline) << QPointF(arrowHeight, bottomBaseline + arrowHeight) 365 << QPointF(right, bottomBaseline); 366 367 painter->setPen(Qt::NoPen); 368 painter->setBrush(color); 369 painter->drawPolygon(upArrow); 370 painter->drawPolygon(downArrow); 371} 372 373QSizeF StylePainterMobile::getButtonImageSize(int buttonHeight, bool multiple) const 374{ 375 if (multiple) 376 return QSizeF(qreal(2 + buttonHeight * 3 * multipleComboDotsOffsetFactor/ 10.0) 377 , qreal(2 + buttonHeight / 10.0)); 378 379 const qreal height = buttonHeight / 2.5; 380 const qreal width = 4 * height / 5.0; 381 return QSizeF(2 + width, 2 + height); 382} 383 384QPixmap StylePainterMobile::findComboButton(const QSize& size, bool multiple, bool enabled) const 385{ 386 if (size.isNull()) 387 return QPixmap(); 388 QPixmap result; 389 KeyIdentifier id; 390 id.type = KeyIdentifier::ComboButton; 391 id.width = size.width(); 392 id.height = size.height(); 393 id.trait1 = multiple; 394 id.trait2 = enabled; 395 396 if (!findCachedControl(id, &result)) { 397 result = QPixmap(size); 398 const qreal border = painterScale(painter); 399 const QSizeF padding(2 * border, 2 * border); 400 const QSizeF innerSize = size - padding; 401 ASSERT(innerSize.isValid()); 402 result.fill(Qt::transparent); 403 QPainter cachePainter(&result); 404 cachePainter.translate(border, border); 405 if (multiple) 406 drawMultipleComboButton(&cachePainter, innerSize, enabled ? darkColor : Qt::lightGray); 407 else 408 drawSimpleComboButton(&cachePainter, innerSize, enabled ? darkColor : Qt::lightGray); 409 insertIntoCache(id, result); 410 } 411 return result; 412} 413 414void StylePainterMobile::drawLineEdit(const QRect& rect, bool focused, bool enabled) 415{ 416 Q_UNUSED(enabled); 417 QPixmap lineEdit = findLineEdit(sizeForPainterScale(rect), focused); 418 if (lineEdit.isNull()) 419 return; 420 painter->drawPixmap(rect, lineEdit); 421} 422 423QPixmap StylePainterMobile::findLineEdit(const QSize & size, bool focused) const 424{ 425 QPixmap result; 426 KeyIdentifier id; 427 id.type = KeyIdentifier::LineEdit; 428 id.width = size.width(); 429 id.height = size.height(); 430 id.trait1 = focused; 431 432 if (!findCachedControl(id, &result)) { 433 const int focusFrame = painterScale(painter); 434 result = QPixmap(size); 435 result.fill(Qt::transparent); 436 const QRect rect = result.rect().adjusted(focusFrame, focusFrame, -focusFrame, -focusFrame); 437 QPainter cachePainter(&result); 438 drawControlBackground(&cachePainter, borderPen(painter), rect, Qt::white); 439 440 if (focused) { 441 QPen focusPen(highlightColor, 1.2 * painterScale(painter), Qt::SolidLine); 442 drawControlBackground(&cachePainter, focusPen, rect, Qt::NoBrush); 443 } 444 insertIntoCache(id, result); 445 } 446 return result; 447} 448 449void StylePainterMobile::drawCheckBox(const QRect& rect, bool checked, bool enabled) 450{ 451 const QRect square = shrinkRectToSquare(rect); 452 QPixmap checkBox = findCheckBox(sizeForPainterScale(square), checked, enabled); 453 if (checkBox.isNull()) 454 return; 455 painter->drawPixmap(square, checkBox); 456} 457 458void StylePainterMobile::drawRadioButton(const QRect& rect, bool checked, bool enabled) 459{ 460 const QRect square = shrinkRectToSquare(rect); 461 QPixmap radio = findRadio(sizeForPainterScale(square), checked, enabled); 462 if (radio.isNull()) 463 return; 464 painter->drawPixmap(square, radio); 465} 466 467void StylePainterMobile::drawPushButton(const QRect& rect, bool sunken, bool enabled) 468{ 469 QPixmap pushButton = findPushButton(sizeForPainterScale(rect), sunken, enabled); 470 if (pushButton.isNull()) 471 return; 472 painter->drawPixmap(rect, pushButton); 473} 474 475QPixmap StylePainterMobile::findPushButton(const QSize& size, bool sunken, bool enabled) const 476{ 477 QPixmap result; 478 KeyIdentifier id; 479 id.type = KeyIdentifier::PushButton; 480 id.width = size.width(); 481 id.height = size.height(); 482 id.trait1 = sunken; 483 id.trait2 = enabled; 484 if (!findCachedControl(id, &result)) { 485 const qreal dropShadowSize = painterScale(painter); 486 result = QPixmap(size); 487 result.fill(Qt::transparent); 488 const QRect rect = QRect(0, 0, size.width(), size.height() - dropShadowSize); 489 QPainter cachePainter(&result); 490 drawControlBackground(&cachePainter, Qt::NoPen, rect.adjusted(0, dropShadowSize, 0, dropShadowSize), shadowColor); 491 492 QBrush brush; 493 if (enabled && !sunken) { 494 QLinearGradient linearGradient; 495 linearGradient.setStart(rect.bottomLeft()); 496 linearGradient.setFinalStop(rect.topLeft()); 497 linearGradient.setColorAt(0.0, buttonGradientBottom); 498 linearGradient.setColorAt(1.0, Qt::white); 499 brush = linearGradient; 500 } else if (!enabled) 501 brush = QColor(241, 242, 243); 502 else { // sunken 503 QLinearGradient linearGradient; 504 linearGradient.setStart(rect.bottomLeft()); 505 linearGradient.setFinalStop(rect.topLeft()); 506 linearGradient.setColorAt(0.0, highlightColor); 507 linearGradient.setColorAt(1.0, highlightColor.lighter()); 508 brush = linearGradient; 509 } 510 drawControlBackground(&cachePainter, borderPen(painter), rect, brush); 511 insertIntoCache(id, result); 512 } 513 return result; 514} 515 516void StylePainterMobile::drawComboBox(const QRect& rect, bool multiple, bool enabled) 517{ 518 QPixmap pushButton = findPushButton(sizeForPainterScale(rect), /*sunken = */false, enabled); 519 if (pushButton.isNull()) 520 return; 521 painter->drawPixmap(rect, pushButton); 522 QRectF targetRect(QPointF(0, 0), getButtonImageSize(rect.height() - 1, multiple)); 523 const QPointF buttonCenter(rect.right() - arrowBoxWidth / 2, rect.top() + (rect.height() - 1) / 2); 524 targetRect.moveCenter(buttonCenter); 525 QPixmap pic = findComboButton(sizeForPainterScale(targetRect.toRect()), multiple, enabled); 526 if (pic.isNull()) 527 return; 528 529 painter->drawPixmap(targetRect.toRect(), pic); 530} 531 532void StylePainterMobile::drawProgress(const QRect& rect, double progress, bool leftToRight, bool animated, bool vertical) const 533{ 534 const int horizontalBorder = (vertical ? rect.width() / 4 : 0); 535 const int verticalBorder = (vertical ? 0 : rect.height() / 4); 536 const QRect targetRect = rect.adjusted(horizontalBorder, verticalBorder, -horizontalBorder, -verticalBorder); 537 538 QPixmap result; 539 QSize imageSize = sizeForPainterScale(targetRect); 540 if (vertical) 541 qSwap(imageSize.rheight(), imageSize.rwidth()); 542 KeyIdentifier id; 543 id.type = KeyIdentifier::Progress; 544 id.width = imageSize.width(); 545 id.height = imageSize.height(); 546 id.trait1 = animated; 547 id.trait2 = (!animated && !leftToRight); 548 id.trait3 = progress * 100; 549 if (!findCachedControl(id, &result)) { 550 if (imageSize.isNull()) 551 return; 552 result = QPixmap(imageSize); 553 result.fill(Qt::transparent); 554 QPainter painter(&result); 555 painter.setRenderHint(QPainter::Antialiasing); 556 QRect progressRect(QPoint(0, 0), imageSize); 557 qreal radius = radiusFactor * progressRect.height(); 558 painter.setBrush(Qt::NoBrush); 559 painter.setPen(borderPen()); 560 progressRect.adjust(1, 1, -1, -1); 561 painter.drawRoundedRect(progressRect, radius, radius); 562 progressRect.adjust(1, 1, -1, -1); 563 if (animated) { 564 const int right = progressRect.right(); 565 const int startPos = right * (1 - progressBarChunkPercentage) * 2 * fabs(progress - 0.5); 566 progressRect.setWidth(progressBarChunkPercentage * right); 567 progressRect.moveLeft(startPos); 568 } else { 569 progressRect.setWidth(progress * progressRect.width()); 570 if (!leftToRight) 571 progressRect.moveRight(imageSize.width() - 2); 572 } 573 if (progressRect.width() > 0) { 574 QLinearGradient gradient; 575 gradient.setStart(progressRect.bottomLeft()); 576 gradient.setFinalStop(progressRect.topLeft()); 577 gradient.setColorAt(0.0, highlightColor); 578 gradient.setColorAt(1.0, highlightColor.lighter()); 579 painter.setBrush(gradient); 580 painter.setPen(Qt::NoPen); 581 radius = radiusFactor * progressRect.height(); 582 painter.drawRoundedRect(progressRect, radius, radius); 583 } 584 insertIntoCache(id, result); 585 } 586 QTransform transform; 587 transform.rotate(-90); 588 painter->drawPixmap(targetRect, vertical ? result.transformed(transform) : result); 589} 590 591void StylePainterMobile::drawSliderThumb(const QRect & rect, bool pressed) const 592{ 593 QPixmap result; 594 const QSize size = sizeForPainterScale(rect); 595 KeyIdentifier id; 596 id.type = KeyIdentifier::SliderThumb; 597 id.width = size.width(); 598 id.height = size.height(); 599 id.trait1 = pressed; 600 if (!findCachedControl(id, &result)) { 601 if (size.isNull()) 602 return; 603 result = QPixmap(size); 604 result.fill(Qt::transparent); 605 QPainter cachePainter(&result); 606 drawControlBackground(&cachePainter, borderPen(painter), QRect(QPoint(0, 0), size), pressed? Qt::lightGray : buttonGradientBottom); 607 insertIntoCache(id, result); 608 } 609 painter->drawPixmap(rect, result); 610} 611 612 613PassRefPtr<RenderTheme> RenderThemeQtMobile::create(Page* page) 614{ 615 return adoptRef(new RenderThemeQtMobile(page)); 616} 617 618RenderThemeQtMobile::RenderThemeQtMobile(Page* page) 619 : RenderThemeQt(page) 620{ 621} 622 623RenderThemeQtMobile::~RenderThemeQtMobile() 624{ 625} 626 627bool RenderThemeQtMobile::isControlStyled(const RenderStyle* style, const BorderData& border, const FillLayer& fill, const Color& backgroundColor) const 628{ 629 switch (style->appearance()) { 630 case CheckboxPart: 631 case RadioPart: 632 return false; 633 default: 634 return RenderThemeQt::isControlStyled(style, border, fill, backgroundColor); 635 } 636} 637 638int RenderThemeQtMobile::popupInternalPaddingBottom(RenderStyle* style) const 639{ 640 return 1; 641} 642 643void RenderThemeQtMobile::computeSizeBasedOnStyle(RenderStyle* renderStyle) const 644{ 645 QSize size(0, 0); 646 647 switch (renderStyle->appearance()) { 648 case TextAreaPart: 649 case SearchFieldPart: 650 case TextFieldPart: { 651 int padding = frameWidth; 652 renderStyle->setPaddingLeft(Length(padding, Fixed)); 653 renderStyle->setPaddingRight(Length(padding, Fixed)); 654 renderStyle->setPaddingTop(Length(padding, Fixed)); 655 renderStyle->setPaddingBottom(Length(padding, Fixed)); 656 break; 657 } 658 default: 659 break; 660 } 661 // If the width and height are both specified, then we have nothing to do. 662 if (!renderStyle->width().isIntrinsicOrAuto() && !renderStyle->height().isAuto()) 663 return; 664 665 switch (renderStyle->appearance()) { 666 case CheckboxPart: { 667 const int w = checkBoxWidth * renderStyle->effectiveZoom(); 668 size = QSize(w, w); 669 break; 670 } 671 case RadioPart: { 672 const int w = radioWidth * renderStyle->effectiveZoom(); 673 size = QSize(w, w); 674 break; 675 } 676 case PushButtonPart: 677 case SquareButtonPart: 678 case DefaultButtonPart: 679 case ButtonPart: 680 case MenulistPart: { 681 const int height = renderStyle->fontMetrics().height() * buttonHeightRatio * renderStyle->effectiveZoom(); 682 size = QSize(renderStyle->width().value(), height); 683 break; 684 } 685 default: 686 break; 687 } 688 689 // FIXME: Check is flawed, since it doesn't take min-width/max-width into account. 690 if (renderStyle->width().isIntrinsicOrAuto() && size.width() > 0) 691 renderStyle->setMinWidth(Length(size.width(), Fixed)); 692 if (renderStyle->height().isAuto() && size.height() > 0) 693 renderStyle->setMinHeight(Length(size.height(), Fixed)); 694} 695 696void RenderThemeQtMobile::adjustButtonStyle(StyleResolver*, RenderStyle* style, Element*) const 697{ 698 // Ditch the border. 699 style->resetBorder(); 700 701 FontDescription fontDescription = style->fontDescription(); 702 fontDescription.setIsAbsoluteSize(true); 703 704 fontDescription.setSpecifiedSize(style->fontSize()); 705 fontDescription.setComputedSize(style->fontSize()); 706 707 style->setLineHeight(RenderStyle::initialLineHeight()); 708 setButtonSize(style); 709 setButtonPadding(style); 710} 711 712void RenderThemeQtMobile::setButtonPadding(RenderStyle* style) const 713{ 714 if (!style) 715 return; 716 style->setPaddingLeft(Length(buttonPaddingLeft, Fixed)); 717 style->setPaddingRight(Length(buttonPaddingRight, Fixed)); 718 style->setPaddingTop(Length(buttonPaddingTop, Fixed)); 719 style->setPaddingBottom(Length(buttonPaddingBottom, Fixed)); 720} 721 722bool RenderThemeQtMobile::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& r) 723{ 724 StylePainterMobile p(this, i); 725 if (!p.isValid()) 726 return true; 727 728 ControlPart appearance = o->style()->appearance(); 729 if (appearance == PushButtonPart || appearance == ButtonPart) { 730 p.drawPushButton(r, isPressed(o), isEnabled(o)); 731 } else if (appearance == RadioPart) 732 p.drawRadioButton(r, isChecked(o), isEnabled(o)); 733 else if (appearance == CheckboxPart) 734 p.drawCheckBox(r, isChecked(o), isEnabled(o)); 735 736 return false; 737} 738 739void RenderThemeQtMobile::adjustTextFieldStyle(StyleResolver*, RenderStyle* style, Element*) const 740{ 741 // Resetting the style like this leads to differences like: 742 // - RenderTextControl {INPUT} at (2,2) size 168x25 [bgcolor=#FFFFFF] border: (2px inset #000000)] 743 // + RenderTextControl {INPUT} at (2,2) size 166x26 744 // in layout tests when a CSS style is applied that doesn't affect background color, border or 745 // padding. Just worth keeping in mind! 746 style->setBackgroundColor(Color::transparent); 747 style->resetBorder(); 748 style->setBorderTopWidth(frameWidth); 749 style->setBorderRightWidth(frameWidth); 750 style->setBorderBottomWidth(frameWidth); 751 style->setBorderLeftWidth(frameWidth); 752 style->resetPadding(); 753 computeSizeBasedOnStyle(style); 754 style->setPaddingLeft(Length(textFieldPadding, Fixed)); 755 style->setPaddingRight(Length(textFieldPadding, Fixed)); 756} 757 758bool RenderThemeQtMobile::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& r) 759{ 760 StylePainterMobile p(this, i); 761 if (!p.isValid()) 762 return true; 763 764 ControlPart appearance = o->style()->appearance(); 765 if (appearance != TextFieldPart 766 && appearance != SearchFieldPart 767 && appearance != TextAreaPart) 768 return true; 769 770 // Now paint the text field. 771 if (appearance == TextAreaPart) { 772 const bool previousAntialiasing = p.painter->testRenderHint(QPainter::Antialiasing); 773 p.painter->setRenderHint(QPainter::Antialiasing); 774 p.painter->setPen(borderPen()); 775 p.painter->setBrush(Qt::white); 776 const int radius = checkBoxWidth * radiusFactor; 777 p.painter->drawRoundedRect(r, radius, radius); 778 779 if (isFocused(o)) { 780 QPen focusPen(highlightColor, 1.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); 781 p.painter->setPen(focusPen); 782 p.painter->setBrush(Qt::NoBrush); 783 p.painter->drawRoundedRect(r, radius, radius); 784 } 785 p.painter->setRenderHint(QPainter::Antialiasing, previousAntialiasing); 786 } else 787 p.drawLineEdit(r, isFocused(o), isEnabled(o)); 788 return false; 789} 790 791void RenderThemeQtMobile::adjustMenuListStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const 792{ 793 RenderThemeQt::adjustMenuListStyle(styleResolver, style, e); 794 style->setPaddingLeft(Length(menuListPadding, Fixed)); 795} 796 797void RenderThemeQtMobile::setPopupPadding(RenderStyle* style) const 798{ 799 const int paddingLeft = 4; 800 const int paddingRight = style->width().isFixed() || style->width().isPercent() ? 5 : 8; 801 802 style->setPaddingLeft(Length(paddingLeft, Fixed)); 803 style->setPaddingRight(Length(paddingRight + arrowBoxWidth, Fixed)); 804 805 style->setPaddingTop(Length(2, Fixed)); 806 style->setPaddingBottom(Length(2, Fixed)); 807} 808 809bool RenderThemeQtMobile::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& r) 810{ 811 StylePainterMobile p(this, i); 812 if (!p.isValid()) 813 return true; 814 815 p.drawComboBox(r, checkMultiple(o), isEnabled(o)); 816 return false; 817} 818 819bool RenderThemeQtMobile::paintMenuListButton(RenderObject* o, const PaintInfo& i, 820 const IntRect& r) 821{ 822 StylePainterMobile p(this, i); 823 if (!p.isValid()) 824 return true; 825 826 p.drawComboBox(r, checkMultiple(o), isEnabled(o)); 827 828 return false; 829} 830 831#if ENABLE(PROGRESS_ELEMENT) 832double RenderThemeQtMobile::animationDurationForProgressBar(RenderProgress* renderProgress) const 833{ 834 if (renderProgress->isDeterminate()) 835 return 0; 836 // Our animation goes back and forth so we need to make it last twice as long 837 // and we need the numerator to be an odd number to ensure we get a progress value of 0.5. 838 return (2 * progressAnimationGranularity +1) / progressBarChunkPercentage * animationRepeatIntervalForProgressBar(renderProgress); 839} 840 841bool RenderThemeQtMobile::paintProgressBar(RenderObject* o, const PaintInfo& pi, const IntRect& r) 842{ 843 if (!o->isProgress()) 844 return true; 845 846 StylePainterMobile p(this, pi); 847 if (!p.isValid()) 848 return true; 849 850 RenderProgress* renderProgress = toRenderProgress(o); 851 const bool isRTL = (renderProgress->style()->direction() == RTL); 852 853 if (renderProgress->isDeterminate()) 854 p.drawProgress(r, renderProgress->position(), !isRTL); 855 else 856 p.drawProgress(r, renderProgress->animationProgress(), !isRTL, true); 857 858 return false; 859} 860#endif 861 862bool RenderThemeQtMobile::paintSliderTrack(RenderObject* o, const PaintInfo& pi, 863 const IntRect& r) 864{ 865 StylePainterMobile p(this, pi); 866 if (!p.isValid()) 867 return true; 868 869 HTMLInputElement* slider = static_cast<HTMLInputElement*>(o->node()); 870 871 const double min = slider->minimum(); 872 const double max = slider->maximum(); 873 const double progress = (max - min > 0) ? (slider->valueAsNumber() - min) / (max - min) : 0; 874 875 QRect rect(r); 876 const bool vertical = (o->style()->appearance() == SliderVerticalPart); 877 const int groovePadding = vertical ? r.width() * sliderGrooveBorderRatio : r.height() * sliderGrooveBorderRatio; 878 if (vertical) { 879 rect.adjust(groovePadding, 0, -groovePadding, 0); 880 // Direction is ignored on vertical sliders and we assume LTR. 881 p.drawProgress(rect, progress, true, /*animated = */ false, vertical); 882 } else { 883 rect.adjust(0, groovePadding, 0, -groovePadding); 884 p.drawProgress(rect, progress, o->style()->isLeftToRightDirection(), /*animated = */ false, vertical); 885 } 886 887 return false; 888} 889 890bool RenderThemeQtMobile::paintSliderThumb(RenderObject* o, const PaintInfo& pi, 891 const IntRect& r) 892{ 893 StylePainterMobile p(this, pi); 894 if (!p.isValid()) 895 return true; 896 897 p.drawSliderThumb(r, isPressed(o)); 898 899 return false; 900} 901 902bool RenderThemeQtMobile::checkMultiple(RenderObject* o) const 903{ 904 HTMLSelectElement* select = o ? toHTMLSelectElement(o->node()) : 0; 905 return select ? select->multiple() : false; 906} 907 908void RenderThemeQtMobile::adjustSliderThumbSize(RenderStyle* style, Element* element) const 909{ 910 const ControlPart part = style->appearance(); 911 if (part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) { 912 const int size = sliderSize * style->effectiveZoom(); 913 style->setWidth(Length(size, Fixed)); 914 style->setHeight(Length(size, Fixed)); 915 } else 916 RenderThemeQt::adjustSliderThumbSize(style, element); 917} 918 919} 920 921// vim: ts=4 sw=4 et 922