1/* 2 * Copyright (C) 2007 Apple Inc. 3 * Copyright (C) 2007 Alp Toker <alp@atoker.com> 4 * Copyright (C) 2008 Collabora Ltd. 5 * Copyright (C) 2009 Kenneth Rohde Christiansen 6 * Copyright (C) 2010 Igalia S.L. 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 * 23 */ 24 25#include "config.h" 26#include "RenderThemeGtk.h" 27 28#include "CSSValueKeywords.h" 29#include "GOwnPtr.h" 30#include "Gradient.h" 31#include "GraphicsContext.h" 32#include "GtkVersioning.h" 33#include "HTMLMediaElement.h" 34#include "HTMLNames.h" 35#include "MediaControlElements.h" 36#include "PaintInfo.h" 37#include "PlatformContextCairo.h" 38#include "RenderBox.h" 39#include "RenderObject.h" 40#include "TimeRanges.h" 41#include "UserAgentStyleSheets.h" 42#include <gdk/gdk.h> 43#include <gtk/gtk.h> 44 45#if ENABLE(PROGRESS_TAG) 46#include "RenderProgress.h" 47#endif 48 49namespace WebCore { 50 51using namespace HTMLNames; 52 53#if ENABLE(VIDEO) 54static HTMLMediaElement* getMediaElementFromRenderObject(RenderObject* o) 55{ 56 Node* node = o->node(); 57 Node* mediaNode = node ? node->shadowAncestorNode() : 0; 58 if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) 59 return 0; 60 61 return static_cast<HTMLMediaElement*>(mediaNode); 62} 63 64static GtkIconSize getMediaButtonIconSize(int mediaIconSize) 65{ 66 GtkIconSize iconSize = gtk_icon_size_from_name("webkit-media-button-size"); 67 if (!iconSize) 68 iconSize = gtk_icon_size_register("webkit-media-button-size", mediaIconSize, mediaIconSize); 69 return iconSize; 70} 71 72void RenderThemeGtk::initMediaButtons() 73{ 74 static bool iconsInitialized = false; 75 76 if (iconsInitialized) 77 return; 78 79 GRefPtr<GtkIconFactory> iconFactory = adoptGRef(gtk_icon_factory_new()); 80 GtkIconSource* iconSource = gtk_icon_source_new(); 81 const char* icons[] = { "audio-volume-high", "audio-volume-muted" }; 82 83 gtk_icon_factory_add_default(iconFactory.get()); 84 85 for (size_t i = 0; i < G_N_ELEMENTS(icons); ++i) { 86 gtk_icon_source_set_icon_name(iconSource, icons[i]); 87 GtkIconSet* iconSet = gtk_icon_set_new(); 88 gtk_icon_set_add_source(iconSet, iconSource); 89 gtk_icon_factory_add(iconFactory.get(), icons[i], iconSet); 90 gtk_icon_set_unref(iconSet); 91 } 92 93 gtk_icon_source_free(iconSource); 94 95 iconsInitialized = true; 96} 97#endif 98 99PassRefPtr<RenderTheme> RenderThemeGtk::create() 100{ 101 return adoptRef(new RenderThemeGtk()); 102} 103 104PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) 105{ 106 static RenderTheme* rt = RenderThemeGtk::create().releaseRef(); 107 return rt; 108} 109 110RenderThemeGtk::RenderThemeGtk() 111 : m_panelColor(Color::white) 112 , m_sliderColor(Color::white) 113 , m_sliderThumbColor(Color::white) 114 , m_mediaIconSize(16) 115 , m_mediaSliderHeight(14) 116 , m_mediaSliderThumbWidth(12) 117 , m_mediaSliderThumbHeight(12) 118{ 119 platformInit(); 120#if ENABLE(VIDEO) 121 initMediaColors(); 122 initMediaButtons(); 123#endif 124} 125 126static bool supportsFocus(ControlPart appearance) 127{ 128 switch (appearance) { 129 case PushButtonPart: 130 case ButtonPart: 131 case TextFieldPart: 132 case TextAreaPart: 133 case SearchFieldPart: 134 case MenulistPart: 135 case RadioPart: 136 case CheckboxPart: 137 case SliderHorizontalPart: 138 case SliderVerticalPart: 139 return true; 140 default: 141 return false; 142 } 143} 144 145bool RenderThemeGtk::supportsFocusRing(const RenderStyle* style) const 146{ 147 return supportsFocus(style->appearance()); 148} 149 150bool RenderThemeGtk::controlSupportsTints(const RenderObject* o) const 151{ 152 return isEnabled(o); 153} 154 155int RenderThemeGtk::baselinePosition(const RenderObject* o) const 156{ 157 if (!o->isBox()) 158 return 0; 159 160 // FIXME: This strategy is possibly incorrect for the GTK+ port. 161 if (o->style()->appearance() == CheckboxPart 162 || o->style()->appearance() == RadioPart) { 163 const RenderBox* box = toRenderBox(o); 164 return box->marginTop() + box->height() - 2; 165 } 166 167 return RenderTheme::baselinePosition(o); 168} 169 170// This is used in RenderThemeGtk2 and RenderThemeGtk3. Normally, it would be in 171// the RenderThemeGtk header (perhaps as a static method), but we want to avoid 172// having to include GTK+ headers only for the GtkTextDirection enum. 173GtkTextDirection gtkTextDirection(TextDirection direction) 174{ 175 switch (direction) { 176 case RTL: 177 return GTK_TEXT_DIR_RTL; 178 case LTR: 179 return GTK_TEXT_DIR_LTR; 180 default: 181 return GTK_TEXT_DIR_NONE; 182 } 183} 184 185static GtkStateType gtkIconState(RenderTheme* theme, RenderObject* renderObject) 186{ 187 if (!theme->isEnabled(renderObject)) 188 return GTK_STATE_INSENSITIVE; 189 if (theme->isPressed(renderObject)) 190 return GTK_STATE_ACTIVE; 191 if (theme->isHovered(renderObject)) 192 return GTK_STATE_PRELIGHT; 193 194 return GTK_STATE_NORMAL; 195} 196 197void RenderThemeGtk::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const 198{ 199 // Some layout tests check explicitly that buttons ignore line-height. 200 if (style->appearance() == PushButtonPart) 201 style->setLineHeight(RenderStyle::initialLineHeight()); 202} 203 204void RenderThemeGtk::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 205{ 206 // The tests check explicitly that select menu buttons ignore line height. 207 style->setLineHeight(RenderStyle::initialLineHeight()); 208 209 // We cannot give a proper rendering when border radius is active, unfortunately. 210 style->resetBorderRadius(); 211} 212 213void RenderThemeGtk::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 214{ 215 adjustMenuListStyle(selector, style, e); 216} 217 218bool RenderThemeGtk::paintMenuListButton(RenderObject* object, const PaintInfo& info, const IntRect& rect) 219{ 220 return paintMenuList(object, info, rect); 221} 222 223bool RenderThemeGtk::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r) 224{ 225 return paintTextField(o, i, r); 226} 227 228static void paintGdkPixbuf(GraphicsContext* context, const GdkPixbuf* icon, const IntRect& iconRect) 229{ 230 IntSize iconSize(gdk_pixbuf_get_width(icon), gdk_pixbuf_get_height(icon)); 231 if (iconRect.size() != iconSize) { 232 // We could use cairo_scale() here but cairo/pixman downscale quality is quite bad. 233 GRefPtr<GdkPixbuf> scaledIcon = gdk_pixbuf_scale_simple(icon, iconRect.width(), iconRect.height(), 234 GDK_INTERP_BILINEAR); 235 icon = scaledIcon.get(); 236 } 237 238 cairo_t* cr = context->platformContext()->cr(); 239 cairo_save(cr); 240 gdk_cairo_set_source_pixbuf(cr, icon, iconRect.x(), iconRect.y()); 241 cairo_paint(cr); 242 cairo_restore(cr); 243} 244 245// Defined in GTK+ (gtk/gtkiconfactory.c) 246static const gint gtkIconSizeMenu = 16; 247static const gint gtkIconSizeSmallToolbar = 18; 248static const gint gtkIconSizeButton = 20; 249static const gint gtkIconSizeLargeToolbar = 24; 250static const gint gtkIconSizeDnd = 32; 251static const gint gtkIconSizeDialog = 48; 252 253static GtkIconSize getIconSizeForPixelSize(gint pixelSize) 254{ 255 if (pixelSize < gtkIconSizeSmallToolbar) 256 return GTK_ICON_SIZE_MENU; 257 if (pixelSize >= gtkIconSizeSmallToolbar && pixelSize < gtkIconSizeButton) 258 return GTK_ICON_SIZE_SMALL_TOOLBAR; 259 if (pixelSize >= gtkIconSizeButton && pixelSize < gtkIconSizeLargeToolbar) 260 return GTK_ICON_SIZE_BUTTON; 261 if (pixelSize >= gtkIconSizeLargeToolbar && pixelSize < gtkIconSizeDnd) 262 return GTK_ICON_SIZE_LARGE_TOOLBAR; 263 if (pixelSize >= gtkIconSizeDnd && pixelSize < gtkIconSizeDialog) 264 return GTK_ICON_SIZE_DND; 265 266 return GTK_ICON_SIZE_DIALOG; 267} 268 269void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 270{ 271 adjustSearchFieldCancelButtonStyle(selector, style, e); 272} 273 274bool RenderThemeGtk::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) 275{ 276 return paintSearchFieldResultsDecoration(o, i, rect); 277} 278 279static void adjustSearchFieldIconStyle(RenderStyle* style) 280{ 281 style->resetBorder(); 282 style->resetPadding(); 283 284 // Get the icon size based on the font size. 285 int fontSize = style->fontSize(); 286 if (fontSize < gtkIconSizeMenu) { 287 style->setWidth(Length(fontSize, Fixed)); 288 style->setHeight(Length(fontSize, Fixed)); 289 return; 290 } 291 gint width = 0, height = 0; 292 gtk_icon_size_lookup(getIconSizeForPixelSize(fontSize), &width, &height); 293 style->setWidth(Length(width, Fixed)); 294 style->setHeight(Length(height, Fixed)); 295} 296 297void RenderThemeGtk::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 298{ 299 adjustSearchFieldIconStyle(style); 300} 301 302static IntRect centerRectVerticallyInParentInputElement(RenderObject* renderObject, const IntRect& rect) 303{ 304 // Get the renderer of <input> element. 305 Node* input = renderObject->node()->shadowAncestorNode(); 306 if (!input->renderer()->isBox()) 307 return IntRect(); 308 309 // If possible center the y-coordinate of the rect vertically in the parent input element. 310 // We also add one pixel here to ensure that the y coordinate is rounded up for box heights 311 // that are even, which looks in relation to the box text. 312 IntRect inputContentBox = toRenderBox(input->renderer())->absoluteContentBox(); 313 314 // Make sure the scaled decoration stays square and will fit in its parent's box. 315 int iconSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), rect.height())); 316 IntRect scaledRect(rect.x(), inputContentBox.y() + (inputContentBox.height() - iconSize + 1) / 2, iconSize, iconSize); 317 return scaledRect; 318} 319 320bool RenderThemeGtk::paintSearchFieldResultsDecoration(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 321{ 322 IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect); 323 if (iconRect.isEmpty()) 324 return false; 325 326 GRefPtr<GdkPixbuf> icon = getStockIcon(GTK_TYPE_ENTRY, GTK_STOCK_FIND, 327 gtkTextDirection(renderObject->style()->direction()), 328 gtkIconState(this, renderObject), 329 getIconSizeForPixelSize(rect.height())); 330 paintGdkPixbuf(paintInfo.context, icon.get(), iconRect); 331 return false; 332} 333 334void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 335{ 336 adjustSearchFieldIconStyle(style); 337} 338 339bool RenderThemeGtk::paintSearchFieldCancelButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 340{ 341 IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect); 342 if (iconRect.isEmpty()) 343 return false; 344 345 GRefPtr<GdkPixbuf> icon = getStockIcon(GTK_TYPE_ENTRY, GTK_STOCK_CLEAR, 346 gtkTextDirection(renderObject->style()->direction()), 347 gtkIconState(this, renderObject), 348 getIconSizeForPixelSize(rect.height())); 349 paintGdkPixbuf(paintInfo.context, icon.get(), iconRect); 350 return false; 351} 352 353void RenderThemeGtk::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 354{ 355 // We cannot give a proper rendering when border radius is active, unfortunately. 356 style->resetBorderRadius(); 357 style->setLineHeight(RenderStyle::initialLineHeight()); 358} 359 360bool RenderThemeGtk::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& rect) 361{ 362 return paintTextField(o, i, rect); 363} 364 365bool RenderThemeGtk::paintCapsLockIndicator(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 366{ 367 // The other paint methods don't need to check whether painting is disabled because RenderTheme already checks it 368 // before calling them, but paintCapsLockIndicator() is called by RenderTextControlSingleLine which doesn't check it. 369 if (paintInfo.context->paintingDisabled()) 370 return true; 371 372 int iconSize = std::min(rect.width(), rect.height()); 373 GRefPtr<GdkPixbuf> icon = getStockIcon(GTK_TYPE_ENTRY, GTK_STOCK_CAPS_LOCK_WARNING, 374 gtkTextDirection(renderObject->style()->direction()), 375 0, getIconSizeForPixelSize(iconSize)); 376 377 // Only re-scale the icon when it's smaller than the minimum icon size. 378 if (iconSize >= gtkIconSizeMenu) 379 iconSize = gdk_pixbuf_get_height(icon.get()); 380 381 // GTK+ locates the icon right aligned in the entry. The given rectangle is already 382 // centered vertically by RenderTextControlSingleLine. 383 IntRect iconRect(rect.x() + rect.width() - iconSize, 384 rect.y() + (rect.height() - iconSize) / 2, 385 iconSize, iconSize); 386 paintGdkPixbuf(paintInfo.context, icon.get(), iconRect); 387 return true; 388} 389 390void RenderThemeGtk::adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle* style, Element*) const 391{ 392 style->setBoxShadow(0); 393} 394 395void RenderThemeGtk::adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle* style, Element*) const 396{ 397 style->setBoxShadow(0); 398} 399 400double RenderThemeGtk::caretBlinkInterval() const 401{ 402 GtkSettings* settings = gtk_settings_get_default(); 403 404 gboolean shouldBlink; 405 gint time; 406 407 g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, NULL); 408 409 if (!shouldBlink) 410 return 0; 411 412 return time / 2000.; 413} 414 415double RenderThemeGtk::getScreenDPI() 416{ 417 // FIXME: Really this should be the widget's screen. 418 GdkScreen* screen = gdk_screen_get_default(); 419 if (!screen) 420 return 96; // Default to 96 DPI. 421 422 float dpi = gdk_screen_get_resolution(screen); 423 if (dpi <= 0) 424 return 96; 425 return dpi; 426} 427 428void RenderThemeGtk::systemFont(int, FontDescription& fontDescription) const 429{ 430 GtkSettings* settings = gtk_settings_get_default(); 431 if (!settings) 432 return; 433 434 // This will be a font selection string like "Sans 10" so we cannot use it as the family name. 435 GOwnPtr<gchar> fontName; 436 g_object_get(settings, "gtk-font-name", &fontName.outPtr(), NULL); 437 438 PangoFontDescription* pangoDescription = pango_font_description_from_string(fontName.get()); 439 if (!pangoDescription) 440 return; 441 442 fontDescription.firstFamily().setFamily(pango_font_description_get_family(pangoDescription)); 443 444 int size = pango_font_description_get_size(pangoDescription) / PANGO_SCALE; 445 // If the size of the font is in points, we need to convert it to pixels. 446 if (!pango_font_description_get_size_is_absolute(pangoDescription)) 447 size = size * (getScreenDPI() / 72.0); 448 449 fontDescription.setSpecifiedSize(size); 450 fontDescription.setIsAbsoluteSize(true); 451 fontDescription.setGenericFamily(FontDescription::NoFamily); 452 fontDescription.setWeight(FontWeightNormal); 453 fontDescription.setItalic(false); 454 pango_font_description_free(pangoDescription); 455} 456 457void RenderThemeGtk::platformColorsDidChange() 458{ 459#if ENABLE(VIDEO) 460 initMediaColors(); 461#endif 462 RenderTheme::platformColorsDidChange(); 463} 464 465#if ENABLE(VIDEO) 466String RenderThemeGtk::extraMediaControlsStyleSheet() 467{ 468 return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet)); 469} 470 471void RenderThemeGtk::adjustMediaSliderThumbSize(RenderObject* renderObject) const 472{ 473 ASSERT(renderObject->style()->appearance() == MediaSliderThumbPart); 474 renderObject->style()->setWidth(Length(m_mediaSliderThumbWidth, Fixed)); 475 renderObject->style()->setHeight(Length(m_mediaSliderThumbHeight, Fixed)); 476} 477 478bool RenderThemeGtk::paintMediaButton(RenderObject* renderObject, GraphicsContext* context, const IntRect& rect, const char* iconName) 479{ 480 GRefPtr<GdkPixbuf> icon = getStockIcon(GTK_TYPE_CONTAINER, iconName, 481 gtkTextDirection(renderObject->style()->direction()), 482 gtkIconState(this, renderObject), 483 getMediaButtonIconSize(m_mediaIconSize)); 484 IntRect iconRect(rect.x() + (rect.width() - m_mediaIconSize) / 2, 485 rect.y() + (rect.height() - m_mediaIconSize) / 2, 486 m_mediaIconSize, m_mediaIconSize); 487 context->fillRect(FloatRect(rect), m_panelColor, ColorSpaceDeviceRGB); 488 paintGdkPixbuf(context, icon.get(), iconRect); 489 return false; 490} 491 492bool RenderThemeGtk::paintMediaFullscreenButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 493{ 494 return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_FULLSCREEN); 495} 496 497bool RenderThemeGtk::paintMediaMuteButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 498{ 499 HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(renderObject); 500 if (!mediaElement) 501 return false; 502 503 return paintMediaButton(renderObject, paintInfo.context, rect, mediaElement->muted() ? "audio-volume-muted" : "audio-volume-high"); 504} 505 506bool RenderThemeGtk::paintMediaPlayButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 507{ 508 Node* node = renderObject->node(); 509 if (!node) 510 return false; 511 512 MediaControlPlayButtonElement* button = static_cast<MediaControlPlayButtonElement*>(node); 513 return paintMediaButton(renderObject, paintInfo.context, rect, button->displayType() == MediaPlayButton ? GTK_STOCK_MEDIA_PLAY : GTK_STOCK_MEDIA_PAUSE); 514} 515 516bool RenderThemeGtk::paintMediaSeekBackButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 517{ 518 return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_MEDIA_REWIND); 519} 520 521bool RenderThemeGtk::paintMediaSeekForwardButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 522{ 523 return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_MEDIA_FORWARD); 524} 525 526bool RenderThemeGtk::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 527{ 528 GraphicsContext* context = paintInfo.context; 529 530 context->fillRect(FloatRect(r), m_panelColor, ColorSpaceDeviceRGB); 531 context->fillRect(FloatRect(IntRect(r.x(), r.y() + (r.height() - m_mediaSliderHeight) / 2, 532 r.width(), m_mediaSliderHeight)), m_sliderColor, ColorSpaceDeviceRGB); 533 534 RenderStyle* style = o->style(); 535 HTMLMediaElement* mediaElement = toParentMediaElement(o); 536 537 if (!mediaElement) 538 return false; 539 540 // Draw the buffered ranges. This code is highly inspired from 541 // Chrome for the gradient code. 542 float mediaDuration = mediaElement->duration(); 543 RefPtr<TimeRanges> timeRanges = mediaElement->buffered(); 544 IntRect trackRect = r; 545 int totalWidth = trackRect.width(); 546 547 trackRect.inflate(-style->borderLeftWidth()); 548 context->save(); 549 context->setStrokeStyle(NoStroke); 550 551 for (unsigned index = 0; index < timeRanges->length(); ++index) { 552 ExceptionCode ignoredException; 553 float start = timeRanges->start(index, ignoredException); 554 float end = timeRanges->end(index, ignoredException); 555 int width = ((end - start) * totalWidth) / mediaDuration; 556 IntRect rangeRect; 557 if (!index) { 558 rangeRect = trackRect; 559 rangeRect.setWidth(width); 560 } else { 561 rangeRect.setLocation(IntPoint(trackRect.x() + start / mediaDuration* totalWidth, trackRect.y())); 562 rangeRect.setSize(IntSize(width, trackRect.height())); 563 } 564 565 // Don't bother drawing empty range. 566 if (rangeRect.isEmpty()) 567 continue; 568 569 IntPoint sliderTopLeft = rangeRect.location(); 570 IntPoint sliderTopRight = sliderTopLeft; 571 sliderTopRight.move(0, rangeRect.height()); 572 573 RefPtr<Gradient> gradient = Gradient::create(sliderTopLeft, sliderTopRight); 574 Color startColor = m_panelColor; 575 gradient->addColorStop(0.0, startColor); 576 gradient->addColorStop(1.0, Color(startColor.red() / 2, startColor.green() / 2, startColor.blue() / 2, startColor.alpha())); 577 578 context->setFillGradient(gradient); 579 context->fillRect(rangeRect); 580 } 581 582 context->restore(); 583 return false; 584} 585 586bool RenderThemeGtk::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 587{ 588 // Make the thumb nicer with rounded corners. 589 paintInfo.context->fillRoundedRect(r, IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), m_sliderThumbColor, ColorSpaceDeviceRGB); 590 return false; 591} 592 593bool RenderThemeGtk::paintMediaVolumeSliderContainer(RenderObject*, const PaintInfo& paintInfo, const IntRect& rect) 594{ 595 GraphicsContext* context = paintInfo.context; 596 context->fillRect(FloatRect(rect), m_panelColor, ColorSpaceDeviceRGB); 597 return false; 598} 599 600bool RenderThemeGtk::paintMediaVolumeSliderTrack(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 601{ 602 return paintSliderTrack(renderObject, paintInfo, rect); 603} 604 605bool RenderThemeGtk::paintMediaVolumeSliderThumb(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 606{ 607 return paintSliderThumb(renderObject, paintInfo, rect); 608} 609 610String RenderThemeGtk::formatMediaControlsCurrentTime(float currentTime, float duration) const 611{ 612 return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration); 613} 614 615bool RenderThemeGtk::paintMediaCurrentTime(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 616{ 617 GraphicsContext* context = paintInfo.context; 618 619 context->fillRect(FloatRect(rect), m_panelColor, ColorSpaceDeviceRGB); 620 return false; 621} 622#endif 623 624#if ENABLE(PROGRESS_TAG) 625void RenderThemeGtk::adjustProgressBarStyle(CSSStyleSelector*, RenderStyle* style, Element*) const 626{ 627 style->setBoxShadow(0); 628} 629 630// These values have been copied from RenderThemeChromiumSkia.cpp 631static const int progressActivityBlocks = 5; 632static const int progressAnimationFrames = 10; 633static const double progressAnimationInterval = 0.125; 634double RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress*) const 635{ 636 return progressAnimationInterval; 637} 638 639double RenderThemeGtk::animationDurationForProgressBar(RenderProgress*) const 640{ 641 return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth; 642} 643 644IntRect RenderThemeGtk::calculateProgressRect(RenderObject* renderObject, const IntRect& fullBarRect) 645{ 646 IntRect progressRect(fullBarRect); 647 RenderProgress* renderProgress = toRenderProgress(renderObject); 648 if (renderProgress->isDeterminate()) { 649 int progressWidth = progressRect.width() * renderProgress->position(); 650 if (renderObject->style()->direction() == RTL) 651 progressRect.setX(progressRect.x() + progressRect.width() - progressWidth); 652 progressRect.setWidth(progressWidth); 653 return progressRect; 654 } 655 656 double animationProgress = renderProgress->animationProgress(); 657 658 // Never let the progress rect shrink smaller than 2 pixels. 659 int newWidth = max(2, progressRect.width() / progressActivityBlocks); 660 int movableWidth = progressRect.width() - newWidth; 661 progressRect.setWidth(newWidth); 662 663 // We want the first 0.5 units of the animation progress to represent the 664 // forward motion and the second 0.5 units to represent the backward motion, 665 // thus we multiply by two here to get the full sweep of the progress bar with 666 // each direction. 667 if (animationProgress < 0.5) 668 progressRect.setX(progressRect.x() + (animationProgress * 2 * movableWidth)); 669 else 670 progressRect.setX(progressRect.x() + ((1.0 - animationProgress) * 2 * movableWidth)); 671 return progressRect; 672} 673#endif 674 675} 676