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#ifndef GTK_API_VERSION_2 29 30#include "CSSValueKeywords.h" 31#include "GraphicsContext.h" 32#include "GtkVersioning.h" 33#include "HTMLNames.h" 34#include "MediaControlElements.h" 35#include "Page.h" 36#include "PaintInfo.h" 37#include "PlatformContextCairo.h" 38#include "RenderElement.h" 39#include "TextDirection.h" 40#include "UserAgentStyleSheets.h" 41#include <cmath> 42#include <gdk/gdk.h> 43#include <gtk/gtk.h> 44 45namespace WebCore { 46 47// This is the default value defined by GTK+, where it was defined as MIN_ARROW_SIZE in gtkarrow.c. 48static const int minArrowSize = 15; 49// This is the default value defined by GTK+, where it was defined as MIN_ARROW_WIDTH in gtkspinbutton.c. 50static const int minSpinButtonArrowSize = 6; 51 52typedef HashMap<GType, GRefPtr<GtkStyleContext> > StyleContextMap; 53static StyleContextMap& styleContextMap(); 54 55static void gtkStyleChangedCallback(GObject*, GParamSpec*) 56{ 57 StyleContextMap::const_iterator end = styleContextMap().end(); 58 for (StyleContextMap::const_iterator iter = styleContextMap().begin(); iter != end; ++iter) 59 gtk_style_context_invalidate(iter->value.get()); 60 61 Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment(); 62} 63 64static StyleContextMap& styleContextMap() 65{ 66 DEPRECATED_DEFINE_STATIC_LOCAL(StyleContextMap, map, ()); 67 68 static bool initialized = false; 69 if (!initialized) { 70 GtkSettings* settings = gtk_settings_get_default(); 71 g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(gtkStyleChangedCallback), 0); 72 g_signal_connect(settings, "notify::gtk-color-scheme", G_CALLBACK(gtkStyleChangedCallback), 0); 73 initialized = true; 74 } 75 return map; 76} 77 78static GtkStyleContext* getStyleContext(GType widgetType) 79{ 80 StyleContextMap::AddResult result = styleContextMap().add(widgetType, nullptr); 81 if (!result.isNewEntry) 82 return result.iterator->value.get(); 83 84 GtkWidgetPath* path = gtk_widget_path_new(); 85 gtk_widget_path_append_type(path, widgetType); 86 87 if (widgetType == GTK_TYPE_SCROLLBAR) 88 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SCROLLBAR); 89 else if (widgetType == GTK_TYPE_ENTRY) 90 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_ENTRY); 91 else if (widgetType == GTK_TYPE_ARROW) 92 gtk_widget_path_iter_add_class(path, 0, "arrow"); 93 else if (widgetType == GTK_TYPE_BUTTON) { 94 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_BUTTON); 95 gtk_widget_path_iter_add_class(path, 1, "text-button"); 96 } 97 else if (widgetType == GTK_TYPE_SCALE) 98 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SCALE); 99 else if (widgetType == GTK_TYPE_SEPARATOR) 100 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SEPARATOR); 101 else if (widgetType == GTK_TYPE_PROGRESS_BAR) 102 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_PROGRESSBAR); 103 else if (widgetType == GTK_TYPE_SPIN_BUTTON) 104 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SPINBUTTON); 105 else if (widgetType == GTK_TYPE_TREE_VIEW) 106 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_VIEW); 107 else if (widgetType == GTK_TYPE_CHECK_BUTTON) 108 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_CHECK); 109 else if (widgetType == GTK_TYPE_RADIO_BUTTON) 110 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_RADIO); 111 112 GRefPtr<GtkStyleContext> context = adoptGRef(gtk_style_context_new()); 113 gtk_style_context_set_path(context.get(), path); 114 gtk_widget_path_free(path); 115 116 result.iterator->value = context; 117 return context.get(); 118} 119 120GtkStyleContext* RenderThemeGtk::gtkScrollbarStyle() 121{ 122 return getStyleContext(GTK_TYPE_SCROLLBAR); 123} 124 125// This is not a static method, because we want to avoid having GTK+ headers in RenderThemeGtk.h. 126extern GtkTextDirection gtkTextDirection(TextDirection); 127 128void RenderThemeGtk::platformInit() 129{ 130} 131 132RenderThemeGtk::~RenderThemeGtk() 133{ 134} 135 136#if ENABLE(VIDEO) 137void RenderThemeGtk::initMediaColors() 138{ 139 GdkRGBA color; 140 GtkStyleContext* containerContext = getStyleContext(GTK_TYPE_CONTAINER); 141 142 gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_NORMAL, &color); 143 m_panelColor = color; 144 gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_ACTIVE, &color); 145 m_sliderColor = color; 146 gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_SELECTED, &color); 147 m_sliderThumbColor = color; 148} 149#endif 150 151static void adjustRectForFocus(GtkStyleContext* context, FloatRect& rect) 152{ 153 gint focusWidth, focusPad; 154 gtk_style_context_get_style(context, 155 "focus-line-width", &focusWidth, 156 "focus-padding", &focusPad, NULL); 157 rect.inflate(focusWidth + focusPad); 158} 159 160void RenderThemeGtk::adjustRepaintRect(const RenderObject& renderObject, FloatRect& rect) 161{ 162 GtkStyleContext* context = 0; 163 bool checkInteriorFocus = false; 164 ControlPart part = renderObject.style().appearance(); 165 switch (part) { 166 case CheckboxPart: 167 case RadioPart: 168 context = getStyleContext(part == CheckboxPart ? GTK_TYPE_CHECK_BUTTON : GTK_TYPE_RADIO_BUTTON); 169 170 gint indicatorSpacing; 171 gtk_style_context_get_style(context, "indicator-spacing", &indicatorSpacing, NULL); 172 rect.inflate(indicatorSpacing); 173 174 return; 175 case SliderVerticalPart: 176 case SliderHorizontalPart: 177 context = getStyleContext(GTK_TYPE_SCALE); 178 break; 179 case ButtonPart: 180 case MenulistButtonPart: 181 case MenulistPart: 182 context = getStyleContext(GTK_TYPE_BUTTON); 183 checkInteriorFocus = true; 184 break; 185 case TextFieldPart: 186 case TextAreaPart: 187 context = getStyleContext(GTK_TYPE_ENTRY); 188 checkInteriorFocus = true; 189 break; 190 default: 191 return; 192 } 193 194 ASSERT(context); 195 if (checkInteriorFocus) { 196 gboolean interiorFocus; 197 gtk_style_context_get_style(context, "interior-focus", &interiorFocus, NULL); 198 if (interiorFocus) 199 return; 200 } 201 adjustRectForFocus(context, rect); 202} 203 204static void setToggleSize(GtkStyleContext* context, RenderStyle& style) 205{ 206 // The width and height are both specified, so we shouldn't change them. 207 if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto()) 208 return; 209 210 // Other ports hard-code this to 13 which is also the default value defined by GTK+. 211 // GTK+ users tend to demand the native look. 212 // It could be made a configuration option values other than 13 actually break site compatibility. 213 gint indicatorSize; 214 gtk_style_context_get_style(context, "indicator-size", &indicatorSize, NULL); 215 216 if (style.width().isIntrinsicOrAuto()) 217 style.setWidth(Length(indicatorSize, Fixed)); 218 219 if (style.height().isAuto()) 220 style.setHeight(Length(indicatorSize, Fixed)); 221} 222 223static void paintToggle(const RenderThemeGtk* theme, GType widgetType, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& fullRect) 224{ 225 GtkStyleContext* context = getStyleContext(widgetType); 226 gtk_style_context_save(context); 227 228 // Some themes do not render large toggle buttons properly, so we simply 229 // shrink the rectangle back down to the default size and then center it 230 // in the full toggle button region. The reason for not simply forcing toggle 231 // buttons to be a smaller size is that we don't want to break site layouts. 232 gint indicatorSize; 233 gtk_style_context_get_style(context, "indicator-size", &indicatorSize, NULL); 234 IntRect rect(fullRect); 235 if (rect.width() > indicatorSize) { 236 rect.inflateX(-(rect.width() - indicatorSize) / 2); 237 rect.setWidth(indicatorSize); // In case rect.width() was equal to indicatorSize + 1. 238 } 239 240 if (rect.height() > indicatorSize) { 241 rect.inflateY(-(rect.height() - indicatorSize) / 2); 242 rect.setHeight(indicatorSize); // In case rect.height() was equal to indicatorSize + 1. 243 } 244 245 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction()))); 246 gtk_style_context_add_class(context, widgetType == GTK_TYPE_CHECK_BUTTON ? GTK_STYLE_CLASS_CHECK : GTK_STYLE_CLASS_RADIO); 247 248 guint flags = 0; 249 if (!theme->isEnabled(renderObject) || theme->isReadOnlyControl(renderObject)) 250 flags |= GTK_STATE_FLAG_INSENSITIVE; 251 else if (theme->isHovered(renderObject)) 252 flags |= GTK_STATE_FLAG_PRELIGHT; 253 if (theme->isIndeterminate(renderObject)) 254 flags |= GTK_STATE_FLAG_INCONSISTENT; 255 else if (theme->isChecked(renderObject)) 256 flags |= GTK_STATE_FLAG_ACTIVE; 257 if (theme->isPressed(renderObject)) 258 flags |= GTK_STATE_FLAG_SELECTED; 259 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); 260 261 if (widgetType == GTK_TYPE_CHECK_BUTTON) 262 gtk_render_check(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); 263 else 264 gtk_render_option(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); 265 266 if (theme->isFocused(renderObject)) { 267 IntRect indicatorRect(rect); 268 gint indicatorSpacing; 269 gtk_style_context_get_style(context, "indicator-spacing", &indicatorSpacing, NULL); 270 indicatorRect.inflate(indicatorSpacing); 271 gtk_render_focus(context, paintInfo.context->platformContext()->cr(), indicatorRect.x(), indicatorRect.y(), 272 indicatorRect.width(), indicatorRect.height()); 273 } 274 275 gtk_style_context_restore(context); 276} 277 278void RenderThemeGtk::setCheckboxSize(RenderStyle& style) const 279{ 280 setToggleSize(getStyleContext(GTK_TYPE_CHECK_BUTTON), style); 281} 282 283bool RenderThemeGtk::paintCheckbox(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect) 284{ 285 paintToggle(this, GTK_TYPE_CHECK_BUTTON, renderObject, paintInfo, rect); 286 return false; 287} 288 289void RenderThemeGtk::setRadioSize(RenderStyle& style) const 290{ 291 setToggleSize(getStyleContext(GTK_TYPE_RADIO_BUTTON), style); 292} 293 294bool RenderThemeGtk::paintRadio(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect) 295{ 296 paintToggle(this, GTK_TYPE_RADIO_BUTTON, renderObject, paintInfo, rect); 297 return false; 298} 299 300static void renderButton(RenderTheme* theme, GtkStyleContext* context, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect) 301{ 302 IntRect buttonRect(rect); 303 304 guint flags = 0; 305 if (!theme->isEnabled(renderObject) || theme->isReadOnlyControl(renderObject)) 306 flags |= GTK_STATE_FLAG_INSENSITIVE; 307 else if (theme->isHovered(renderObject)) 308 flags |= GTK_STATE_FLAG_PRELIGHT; 309 if (theme->isPressed(renderObject)) 310 flags |= GTK_STATE_FLAG_ACTIVE; 311 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); 312 313 if (theme->isDefault(renderObject)) { 314 GtkBorder* borderPtr = 0; 315 GtkBorder border = { 1, 1, 1, 1 }; 316 317 gtk_style_context_get_style(context, "default-border", &borderPtr, NULL); 318 if (borderPtr) { 319 border = *borderPtr; 320 gtk_border_free(borderPtr); 321 } 322 323 buttonRect.move(border.left, border.top); 324 buttonRect.setWidth(buttonRect.width() - (border.left + border.right)); 325 buttonRect.setHeight(buttonRect.height() - (border.top + border.bottom)); 326 327 gtk_style_context_add_class(context, GTK_STYLE_CLASS_DEFAULT); 328 } 329 330 gtk_render_background(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); 331 gtk_render_frame(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); 332 333 if (theme->isFocused(renderObject)) { 334 gint focusWidth, focusPad; 335 gboolean displaceFocus, interiorFocus; 336 gtk_style_context_get_style(context, 337 "focus-line-width", &focusWidth, 338 "focus-padding", &focusPad, 339 "interior-focus", &interiorFocus, 340 "displace-focus", &displaceFocus, 341 NULL); 342 343 if (interiorFocus) { 344 GtkBorder borderWidth; 345 gtk_style_context_get_border(context, static_cast<GtkStateFlags>(flags), &borderWidth); 346 347 buttonRect = IntRect(buttonRect.x() + borderWidth.left + focusPad, buttonRect.y() + borderWidth.top + focusPad, 348 buttonRect.width() - (2 * focusPad + borderWidth.left + borderWidth.right), 349 buttonRect.height() - (2 * focusPad + borderWidth.top + borderWidth.bottom)); 350 } else 351 buttonRect.inflate(focusWidth + focusPad); 352 353 if (displaceFocus && theme->isPressed(renderObject)) { 354 gint childDisplacementX; 355 gint childDisplacementY; 356 gtk_style_context_get_style(context, 357 "child-displacement-x", &childDisplacementX, 358 "child-displacement-y", &childDisplacementY, 359 NULL); 360 buttonRect.move(childDisplacementX, childDisplacementY); 361 } 362 363 gtk_render_focus(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); 364 } 365} 366bool RenderThemeGtk::paintButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect) 367{ 368 GtkStyleContext* context = getStyleContext(GTK_TYPE_BUTTON); 369 gtk_style_context_save(context); 370 371 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction()))); 372 gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON); 373 374 renderButton(this, context, renderObject, paintInfo, rect); 375 376 gtk_style_context_restore(context); 377 378 return false; 379} 380 381static void getComboBoxMetrics(RenderStyle& style, GtkBorder& border, int& focus, int& separator) 382{ 383 // If this menu list button isn't drawn using the native theme, we 384 // don't add any extra padding beyond what WebCore already uses. 385 if (style.appearance() == NoControlPart) 386 return; 387 388 GtkStyleContext* context = getStyleContext(GTK_TYPE_COMBO_BOX); 389 gtk_style_context_save(context); 390 391 gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON); 392 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(style.direction()))); 393 394 gtk_style_context_get_border(context, static_cast<GtkStateFlags>(0), &border); 395 396 gboolean interiorFocus; 397 gint focusWidth, focusPad; 398 gtk_style_context_get_style(context, 399 "interior-focus", &interiorFocus, 400 "focus-line-width", &focusWidth, 401 "focus-padding", &focusPad, NULL); 402 focus = interiorFocus ? focusWidth + focusPad : 0; 403 404 gtk_style_context_restore(context); 405 406 context = getStyleContext(GTK_TYPE_SEPARATOR); 407 gtk_style_context_save(context); 408 409 GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(style.direction())); 410 gtk_style_context_set_direction(context, direction); 411 gtk_style_context_add_class(context, "separator"); 412 413 gboolean wideSeparators; 414 gint separatorWidth; 415 gtk_style_context_get_style(context, 416 "wide-separators", &wideSeparators, 417 "separator-width", &separatorWidth, 418 NULL); 419 420 // GTK+ always uses border.left, regardless of text direction. See gtkseperator.c. 421 if (!wideSeparators) 422 separatorWidth = border.left; 423 424 separator = separatorWidth; 425 426 gtk_style_context_restore(context); 427} 428 429int RenderThemeGtk::popupInternalPaddingLeft(RenderStyle& style) const 430{ 431 GtkBorder borderWidth = { 0, 0, 0, 0 }; 432 int focusWidth = 0, separatorWidth = 0; 433 getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth); 434 int left = borderWidth.left + focusWidth; 435 if (style.direction() == RTL) 436 left += separatorWidth + minArrowSize; 437 return left; 438} 439 440int RenderThemeGtk::popupInternalPaddingRight(RenderStyle& style) const 441{ 442 GtkBorder borderWidth = { 0, 0, 0, 0 }; 443 int focusWidth = 0, separatorWidth = 0; 444 getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth); 445 int right = borderWidth.right + focusWidth; 446 if (style.direction() == LTR) 447 right += separatorWidth + minArrowSize; 448 return right; 449} 450 451int RenderThemeGtk::popupInternalPaddingTop(RenderStyle& style) const 452{ 453 GtkBorder borderWidth = { 0, 0, 0, 0 }; 454 int focusWidth = 0, separatorWidth = 0; 455 getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth); 456 return borderWidth.top + focusWidth; 457} 458 459int RenderThemeGtk::popupInternalPaddingBottom(RenderStyle& style) const 460{ 461 GtkBorder borderWidth = { 0, 0, 0, 0 }; 462 int focusWidth = 0, separatorWidth = 0; 463 getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth); 464 return borderWidth.bottom + focusWidth; 465} 466 467bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& r) 468{ 469 // FIXME: adopt subpixel themes. 470 IntRect rect = IntRect(r); 471 472 cairo_t* cairoContext = paintInfo.context->platformContext()->cr(); 473 GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())); 474 475 // Paint the button. 476 GtkStyleContext* buttonStyleContext = getStyleContext(GTK_TYPE_BUTTON); 477 gtk_style_context_save(buttonStyleContext); 478 gtk_style_context_set_direction(buttonStyleContext, direction); 479 gtk_style_context_add_class(buttonStyleContext, GTK_STYLE_CLASS_BUTTON); 480 renderButton(this, buttonStyleContext, renderObject, paintInfo, rect); 481 482 // Get the inner rectangle. 483 gint focusWidth, focusPad; 484 GtkBorder* innerBorderPtr = 0; 485 GtkBorder innerBorder = { 1, 1, 1, 1 }; 486 gtk_style_context_get_style(buttonStyleContext, 487 "inner-border", &innerBorderPtr, 488 "focus-line-width", &focusWidth, 489 "focus-padding", &focusPad, 490 NULL); 491 if (innerBorderPtr) { 492 innerBorder = *innerBorderPtr; 493 gtk_border_free(innerBorderPtr); 494 } 495 496 GtkBorder borderWidth; 497 GtkStateFlags state = gtk_style_context_get_state(buttonStyleContext); 498 gtk_style_context_get_border(buttonStyleContext, state, &borderWidth); 499 500 focusWidth += focusPad; 501 IntRect innerRect(rect.x() + innerBorder.left + borderWidth.left + focusWidth, 502 rect.y() + innerBorder.top + borderWidth.top + focusWidth, 503 rect.width() - borderWidth.left - borderWidth.right - innerBorder.left - innerBorder.right - (2 * focusWidth), 504 rect.height() - borderWidth.top - borderWidth.bottom - innerBorder.top - innerBorder.bottom - (2 * focusWidth)); 505 506 if (isPressed(renderObject)) { 507 gint childDisplacementX; 508 gint childDisplacementY; 509 gtk_style_context_get_style(buttonStyleContext, 510 "child-displacement-x", &childDisplacementX, 511 "child-displacement-y", &childDisplacementY, 512 NULL); 513 innerRect.move(childDisplacementX, childDisplacementY); 514 } 515 innerRect.setWidth(std::max(1, innerRect.width())); 516 innerRect.setHeight(std::max(1, innerRect.height())); 517 518 gtk_style_context_restore(buttonStyleContext); 519 520 // Paint the arrow. 521 GtkStyleContext* arrowStyleContext = getStyleContext(GTK_TYPE_ARROW); 522 gtk_style_context_save(arrowStyleContext); 523 524 gtk_style_context_set_direction(arrowStyleContext, direction); 525 gtk_style_context_add_class(arrowStyleContext, "arrow"); 526 gtk_style_context_add_class(arrowStyleContext, GTK_STYLE_CLASS_BUTTON); 527 528 gfloat arrowScaling; 529 gtk_style_context_get_style(arrowStyleContext, "arrow-scaling", &arrowScaling, NULL); 530 531 IntSize arrowSize(minArrowSize, innerRect.height()); 532 FloatPoint arrowPosition(innerRect.location()); 533 if (direction == GTK_TEXT_DIR_LTR) 534 arrowPosition.move(innerRect.width() - arrowSize.width(), 0); 535 536 // GTK+ actually fetches the xalign and valign values from the widget, but since we 537 // don't have a widget here, we are just using the default xalign and valign values of 0.5. 538 gint extent = std::min(arrowSize.width(), arrowSize.height()) * arrowScaling; 539 arrowPosition.move((arrowSize.width() - extent) / 2, (arrowSize.height() - extent) / 2); 540 541 gtk_style_context_set_state(arrowStyleContext, state); 542 gtk_render_arrow(arrowStyleContext, cairoContext, G_PI, arrowPosition.x(), arrowPosition.y(), extent); 543 544 gtk_style_context_restore(arrowStyleContext); 545 546 // Paint the separator if needed. 547 GtkStyleContext* separatorStyleContext = getStyleContext(GTK_TYPE_COMBO_BOX); 548 gtk_style_context_save(separatorStyleContext); 549 550 gtk_style_context_set_direction(separatorStyleContext, direction); 551 gtk_style_context_add_class(separatorStyleContext, "separator"); 552 553 gboolean wideSeparators; 554 gint separatorWidth; 555 gtk_style_context_get_style(separatorStyleContext, 556 "wide-separators", &wideSeparators, 557 "separator-width", &separatorWidth, 558 NULL); 559 if (wideSeparators && !separatorWidth) { 560 gtk_style_context_restore(separatorStyleContext); 561 return false; 562 } 563 564 gtk_style_context_set_state(separatorStyleContext, state); 565 IntPoint separatorPosition(arrowPosition.x(), innerRect.y()); 566 if (wideSeparators) { 567 if (direction == GTK_TEXT_DIR_LTR) 568 separatorPosition.move(-separatorWidth, 0); 569 else 570 separatorPosition.move(arrowSize.width(), 0); 571 572 gtk_render_frame(separatorStyleContext, cairoContext, 573 separatorPosition.x(), separatorPosition.y(), 574 separatorWidth, innerRect.height()); 575 } else { 576 GtkBorder padding; 577 gtk_style_context_get_padding(separatorStyleContext, state, &padding); 578 GtkBorder border; 579 gtk_style_context_get_border(separatorStyleContext, state, &border); 580 581 if (direction == GTK_TEXT_DIR_LTR) 582 separatorPosition.move(-(padding.left + border.left), 0); 583 else 584 separatorPosition.move(arrowSize.width(), 0); 585 586 cairo_save(cairoContext); 587 588 // An extra clip prevents the separator bleeding outside of the specified rectangle because of subpixel positioning. 589 cairo_rectangle(cairoContext, separatorPosition.x(), separatorPosition.y(), border.left, innerRect.height()); 590 cairo_clip(cairoContext); 591 gtk_render_line(separatorStyleContext, cairoContext, 592 separatorPosition.x(), separatorPosition.y(), 593 separatorPosition.x(), innerRect.maxY()); 594 cairo_restore(cairoContext); 595 } 596 597 gtk_style_context_restore(separatorStyleContext); 598 return false; 599} 600 601bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect) 602{ 603 GtkStyleContext* context = getStyleContext(GTK_TYPE_ENTRY); 604 gtk_style_context_save(context); 605 606 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction()))); 607 gtk_style_context_add_class(context, GTK_STYLE_CLASS_ENTRY); 608 609 guint flags = 0; 610 if (!isEnabled(renderObject) || isReadOnlyControl(renderObject)) 611 flags |= GTK_STATE_FLAG_INSENSITIVE; 612 else if (isFocused(renderObject)) 613 flags |= GTK_STATE_FLAG_FOCUSED; 614 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); 615 616 gtk_render_background(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); 617 gtk_render_frame(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); 618 619 if (isFocused(renderObject) && isEnabled(renderObject)) { 620 gboolean interiorFocus; 621 gint focusWidth, focusPad; 622 gtk_style_context_get_style(context, 623 "interior-focus", &interiorFocus, 624 "focus-line-width", &focusWidth, 625 "focus-padding", &focusPad, 626 NULL); 627 if (!interiorFocus) { 628 IntRect focusRect(rect); 629 focusRect.inflate(focusWidth + focusPad); 630 gtk_render_focus(context, paintInfo.context->platformContext()->cr(), 631 focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height()); 632 } 633 } 634 635 gtk_style_context_restore(context); 636 637 return false; 638} 639 640static void applySliderStyleContextClasses(GtkStyleContext* context, ControlPart part) 641{ 642 gtk_style_context_add_class(context, GTK_STYLE_CLASS_SCALE); 643 if (part == SliderHorizontalPart || part == SliderThumbHorizontalPart) 644 gtk_style_context_add_class(context, GTK_STYLE_CLASS_HORIZONTAL); 645 else if (part == SliderVerticalPart || part == SliderThumbVerticalPart) 646 gtk_style_context_add_class(context, GTK_STYLE_CLASS_VERTICAL); 647} 648 649bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect) 650{ 651 ControlPart part = renderObject.style().appearance(); 652 ASSERT_UNUSED(part, part == SliderHorizontalPart || part == SliderVerticalPart || part == MediaVolumeSliderPart); 653 654 GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE); 655 gtk_style_context_save(context); 656 657 gtk_style_context_set_direction(context, gtkTextDirection(renderObject.style().direction())); 658 applySliderStyleContextClasses(context, part); 659 gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH); 660 661 if (!isEnabled(renderObject) || isReadOnlyControl(renderObject)) 662 gtk_style_context_set_state(context, GTK_STATE_FLAG_INSENSITIVE); 663 664 gtk_render_background(context, paintInfo.context->platformContext()->cr(), 665 rect.x(), rect.y(), rect.width(), rect.height()); 666 gtk_render_frame(context, paintInfo.context->platformContext()->cr(), 667 rect.x(), rect.y(), rect.width(), rect.height()); 668 669 if (isFocused(renderObject)) { 670 gint focusWidth, focusPad; 671 gtk_style_context_get_style(context, 672 "focus-line-width", &focusWidth, 673 "focus-padding", &focusPad, NULL); 674 IntRect focusRect(rect); 675 focusRect.inflate(focusWidth + focusPad); 676 gtk_render_focus(context, paintInfo.context->platformContext()->cr(), 677 focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height()); 678 } 679 680 gtk_style_context_restore(context); 681 return false; 682} 683 684bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect) 685{ 686 ControlPart part = renderObject.style().appearance(); 687 ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart); 688 689 GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE); 690 gtk_style_context_save(context); 691 692 gtk_style_context_set_direction(context, gtkTextDirection(renderObject.style().direction())); 693 applySliderStyleContextClasses(context, part); 694 gtk_style_context_add_class(context, GTK_STYLE_CLASS_SLIDER); 695 696 guint flags = 0; 697 if (!isEnabled(renderObject) || isReadOnlyControl(renderObject)) 698 flags |= GTK_STATE_FLAG_INSENSITIVE; 699 else if (isHovered(renderObject)) 700 flags |= GTK_STATE_FLAG_PRELIGHT; 701 if (isPressed(renderObject)) 702 flags |= GTK_STATE_FLAG_ACTIVE; 703 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); 704 705 gtk_render_slider(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height(), 706 part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); 707 708 gtk_style_context_restore(context); 709 710 return false; 711} 712 713void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, Element&) const 714{ 715 ControlPart part = style.appearance(); 716 if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart) 717 return; 718 719 gint sliderWidth, sliderLength; 720 gtk_style_context_get_style(getStyleContext(GTK_TYPE_SCALE), 721 "slider-width", &sliderWidth, 722 "slider-length", &sliderLength, 723 NULL); 724 if (part == SliderThumbHorizontalPart) { 725 style.setWidth(Length(sliderLength, Fixed)); 726 style.setHeight(Length(sliderWidth, Fixed)); 727 return; 728 } 729 ASSERT(part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart); 730 style.setWidth(Length(sliderWidth, Fixed)); 731 style.setHeight(Length(sliderLength, Fixed)); 732} 733 734bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect) 735{ 736 if (!renderObject.isProgress()) 737 return true; 738 739 GtkStyleContext* context = getStyleContext(GTK_TYPE_PROGRESS_BAR); 740 gtk_style_context_save(context); 741 742 gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH); 743 744 gtk_render_background(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); 745 gtk_render_frame(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); 746 747 gtk_style_context_restore(context); 748 749 gtk_style_context_save(context); 750 gtk_style_context_add_class(context, GTK_STYLE_CLASS_PROGRESSBAR); 751 752 753 GtkBorder padding; 754 gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding); 755 IntRect progressRect(rect.x() + padding.left, rect.y() + padding.top, 756 rect.width() - (padding.left + padding.right), 757 rect.height() - (padding.top + padding.bottom)); 758 progressRect = RenderThemeGtk::calculateProgressRect(renderObject, progressRect); 759 760 if (!progressRect.isEmpty()) 761 gtk_render_activity(context, paintInfo.context->platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height()); 762 763 gtk_style_context_restore(context); 764 return false; 765} 766 767static gint spinButtonArrowSize(GtkStyleContext* context) 768{ 769 PangoFontDescription* fontDescription; 770 gtk_style_context_get(context, static_cast<GtkStateFlags>(0), "font", &fontDescription, NULL); 771 gint fontSize = pango_font_description_get_size(fontDescription); 772 gint arrowSize = std::max(PANGO_PIXELS(fontSize), minSpinButtonArrowSize); 773 pango_font_description_free(fontDescription); 774 775 return arrowSize - arrowSize % 2; // Force even. 776} 777 778void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle& style, Element&) const 779{ 780 GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON); 781 782 GtkBorder padding; 783 gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding); 784 785 int width = spinButtonArrowSize(context) + padding.left + padding.right; 786 style.setWidth(Length(width, Fixed)); 787 style.setMinWidth(Length(width, Fixed)); 788} 789 790static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* context, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType) 791{ 792 ASSERT(arrowType == GTK_ARROW_UP || arrowType == GTK_ARROW_DOWN); 793 794 gtk_style_context_save(context); 795 gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON); 796 797 GtkTextDirection direction = gtk_style_context_get_direction(context); 798 guint state = static_cast<guint>(gtk_style_context_get_state(context)); 799 if (!(state & GTK_STATE_FLAG_INSENSITIVE)) { 800 if (theme->isPressed(renderObject)) { 801 if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartPressed(renderObject)) 802 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartPressed(renderObject))) 803 state |= GTK_STATE_FLAG_ACTIVE; 804 } else if (theme->isHovered(renderObject)) { 805 if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartHovered(renderObject)) 806 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartHovered(renderObject))) 807 state |= GTK_STATE_FLAG_PRELIGHT; 808 } 809 } 810 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(state)); 811 812 // Paint button. 813 IntRect buttonRect(rect); 814 guint junction = gtk_style_context_get_junction_sides(context); 815 if (arrowType == GTK_ARROW_UP) 816 junction |= GTK_JUNCTION_BOTTOM; 817 else { 818 junction |= GTK_JUNCTION_TOP; 819 buttonRect.move(0, rect.height() / 2); 820 } 821 buttonRect.setHeight(rect.height() / 2); 822 gtk_style_context_set_junction_sides(context, static_cast<GtkJunctionSides>(junction)); 823 824 gtk_render_background(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); 825 gtk_render_frame(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); 826 827 // Paint arrow centered inside button. 828 // This code is based on gtkspinbutton.c code. 829 IntRect arrowRect; 830 gdouble angle; 831 if (arrowType == GTK_ARROW_UP) { 832 angle = 0; 833 arrowRect.setY(rect.y()); 834 arrowRect.setHeight(rect.height() / 2 - 2); 835 } else { 836 angle = G_PI; 837 arrowRect.setY(rect.y() + buttonRect.y()); 838 arrowRect.setHeight(rect.height() - arrowRect.y() - 2); 839 } 840 arrowRect.setWidth(rect.width() - 3); 841 if (direction == GTK_TEXT_DIR_LTR) 842 arrowRect.setX(rect.x() + 1); 843 else 844 arrowRect.setX(rect.x() + 2); 845 846 gint width = arrowRect.width() / 2; 847 width -= width % 2 - 1; // Force odd. 848 gint height = (width + 1) / 2; 849 850 arrowRect.move((arrowRect.width() - width) / 2, (arrowRect.height() - height) / 2); 851 gtk_render_arrow(context, paintInfo.context->platformContext()->cr(), angle, arrowRect.x(), arrowRect.y(), width); 852 853 gtk_style_context_restore(context); 854} 855 856bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect) 857{ 858 GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON); 859 gtk_style_context_save(context); 860 861 GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())); 862 gtk_style_context_set_direction(context, direction); 863 864 guint flags = 0; 865 if (!isEnabled(renderObject) || isReadOnlyControl(renderObject)) 866 flags |= GTK_STATE_FLAG_INSENSITIVE; 867 else if (isFocused(renderObject)) 868 flags |= GTK_STATE_FLAG_FOCUSED; 869 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); 870 gtk_style_context_remove_class(context, GTK_STYLE_CLASS_ENTRY); 871 872 paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_UP); 873 paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_DOWN); 874 875 gtk_style_context_restore(context); 876 877 return false; 878} 879 880GRefPtr<GdkPixbuf> getStockIconForWidgetType(GType widgetType, const char* iconName, gint direction, gint state, gint iconSize) 881{ 882 ASSERT(iconName); 883 884 GtkStyleContext* context = getStyleContext(widgetType); 885 GtkIconSet* iconSet = gtk_style_context_lookup_icon_set(context, iconName); 886 887 gtk_style_context_save(context); 888 889 guint flags = 0; 890 if (state == GTK_STATE_PRELIGHT) 891 flags |= GTK_STATE_FLAG_PRELIGHT; 892 else if (state == GTK_STATE_INSENSITIVE) 893 flags |= GTK_STATE_FLAG_INSENSITIVE; 894 895 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); 896 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(direction)); 897 GdkPixbuf* icon = gtk_icon_set_render_icon_pixbuf(iconSet, context, static_cast<GtkIconSize>(iconSize)); 898 899 gtk_style_context_restore(context); 900 901 return adoptGRef(icon); 902} 903 904GRefPtr<GdkPixbuf> getStockSymbolicIconForWidgetType(GType widgetType, const char* symbolicIconName, const char* fallbackStockIconName, gint direction, gint state, gint iconSize) 905{ 906 GtkStyleContext* context = getStyleContext(widgetType); 907 908 gtk_style_context_save(context); 909 910 guint flags = 0; 911 if (state == GTK_STATE_PRELIGHT) 912 flags |= GTK_STATE_FLAG_PRELIGHT; 913 else if (state == GTK_STATE_INSENSITIVE) 914 flags |= GTK_STATE_FLAG_INSENSITIVE; 915 916 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); 917 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(direction)); 918 GtkIconInfo* info = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), symbolicIconName, iconSize, 919 static_cast<GtkIconLookupFlags>(GTK_ICON_LOOKUP_FORCE_SVG | GTK_ICON_LOOKUP_FORCE_SIZE)); 920 GdkPixbuf* icon = 0; 921 if (info) { 922 icon = gtk_icon_info_load_symbolic_for_context(info, context, 0, 0); 923 gtk_icon_info_free(info); 924 } 925 926 gtk_style_context_restore(context); 927 928 if (!icon) { 929 if (!fallbackStockIconName) 930 return nullptr; 931 return getStockIconForWidgetType(widgetType, fallbackStockIconName, direction, state, iconSize); 932 } 933 934 return adoptGRef(icon); 935} 936 937Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const 938{ 939 GdkRGBA gdkRGBAColor; 940 gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor); 941 return gdkRGBAColor; 942} 943 944Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const 945{ 946 GdkRGBA gdkRGBAColor; 947 gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor); 948 return gdkRGBAColor; 949} 950 951Color RenderThemeGtk::platformActiveSelectionForegroundColor() const 952{ 953 GdkRGBA gdkRGBAColor; 954 gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor); 955 return gdkRGBAColor; 956} 957 958Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const 959{ 960 GdkRGBA gdkRGBAColor; 961 gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor); 962 return gdkRGBAColor; 963} 964 965Color RenderThemeGtk::platformActiveListBoxSelectionBackgroundColor() const 966{ 967 GdkRGBA gdkRGBAColor; 968 gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor); 969 return gdkRGBAColor; 970} 971 972Color RenderThemeGtk::platformInactiveListBoxSelectionBackgroundColor() const 973{ 974 GdkRGBA gdkRGBAColor; 975 gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor); 976 return gdkRGBAColor; 977} 978 979Color RenderThemeGtk::platformActiveListBoxSelectionForegroundColor() const 980{ 981 GdkRGBA gdkRGBAColor; 982 gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor); 983 return gdkRGBAColor; 984} 985 986Color RenderThemeGtk::platformInactiveListBoxSelectionForegroundColor() const 987{ 988 GdkRGBA gdkRGBAColor; 989 gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor); 990 return gdkRGBAColor; 991} 992 993Color RenderThemeGtk::systemColor(CSSValueID cssValueId) const 994{ 995 GdkRGBA gdkRGBAColor; 996 997 switch (cssValueId) { 998 case CSSValueButtontext: 999 gtk_style_context_get_color(getStyleContext(GTK_TYPE_BUTTON), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor); 1000 return gdkRGBAColor; 1001 case CSSValueCaptiontext: 1002 gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), static_cast<GtkStateFlags>(0), &gdkRGBAColor); 1003 return gdkRGBAColor; 1004 default: 1005 return RenderTheme::systemColor(cssValueId); 1006 } 1007} 1008 1009} // namespace WebCore 1010 1011#endif // !GTK_API_VERSION_2 1012