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.
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.
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.
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.
26 #include "RenderThemeGtk.h"
28 #ifndef GTK_API_VERSION_2
30 #include "CSSValueKeywords.h"
31 #include "GraphicsContext.h"
32 #include "GtkVersioning.h"
33 #include "HTMLNames.h"
34 #include "MediaControlElements.h"
36 #include "PaintInfo.h"
37 #include "PlatformContextCairo.h"
38 #include "RenderObject.h"
39 #include "TextDirection.h"
40 #include "UserAgentStyleSheets.h"
47 // This is the default value defined by GTK+, where it was defined as MIN_ARROW_SIZE in gtkarrow.c.
48 static const int minArrowSize = 15;
49 // This is the default value defined by GTK+, where it was defined as MIN_ARROW_WIDTH in gtkspinbutton.c.
50 static const int minSpinButtonArrowSize = 6;
52 typedef HashMap<GType, GRefPtr<GtkStyleContext> > StyleContextMap;
53 static StyleContextMap& styleContextMap();
55 static void gtkStyleChangedCallback(GObject*, GParamSpec*)
57 StyleContextMap::const_iterator end = styleContextMap().end();
58 for (StyleContextMap::const_iterator iter = styleContextMap().begin(); iter != end; ++iter)
59 gtk_style_context_invalidate(iter->second.get());
61 Page::scheduleForcedStyleRecalcForAllPages();
64 static StyleContextMap& styleContextMap()
66 DEFINE_STATIC_LOCAL(StyleContextMap, map, ());
68 static bool initialized = false;
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);
78 static GtkStyleContext* getStyleContext(GType widgetType)
80 std::pair<StyleContextMap::iterator, bool> result = styleContextMap().add(widgetType, 0);
82 return result.first->second.get();
84 GtkWidgetPath* path = gtk_widget_path_new();
85 gtk_widget_path_append_type(path, widgetType);
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 else if (widgetType == GTK_TYPE_SCALE)
96 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SCALE);
97 else if (widgetType == GTK_TYPE_SEPARATOR)
98 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SEPARATOR);
99 else if (widgetType == GTK_TYPE_PROGRESS_BAR)
100 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_PROGRESSBAR);
101 else if (widgetType == GTK_TYPE_SPIN_BUTTON)
102 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SPINBUTTON);
103 else if (widgetType == GTK_TYPE_TREE_VIEW)
104 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_VIEW);
105 else if (widgetType == GTK_TYPE_CHECK_BUTTON)
106 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_CHECK);
107 else if (widgetType == GTK_TYPE_RADIO_BUTTON)
108 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_RADIO);
110 GRefPtr<GtkStyleContext> context = adoptGRef(gtk_style_context_new());
111 gtk_style_context_set_path(context.get(), path);
112 gtk_widget_path_free(path);
114 result.first->second = context;
115 return context.get();
118 GtkStyleContext* RenderThemeGtk::gtkScrollbarStyle()
120 return getStyleContext(GTK_TYPE_SCROLLBAR);
123 // This is not a static method, because we want to avoid having GTK+ headers in RenderThemeGtk.h.
124 extern GtkTextDirection gtkTextDirection(TextDirection);
126 void RenderThemeGtk::platformInit()
130 RenderThemeGtk::~RenderThemeGtk()
135 void RenderThemeGtk::initMediaColors()
138 GtkStyleContext* containerContext = getStyleContext(GTK_TYPE_CONTAINER);
140 gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_NORMAL, &color);
141 m_panelColor = color;
142 gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_ACTIVE, &color);
143 m_sliderColor = color;
144 gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_SELECTED, &color);
145 m_sliderThumbColor = color;
149 static void adjustRectForFocus(GtkStyleContext* context, IntRect& rect)
151 gint focusWidth, focusPad;
152 gtk_style_context_get_style(context,
153 "focus-line-width", &focusWidth,
154 "focus-padding", &focusPad, NULL);
155 rect.inflate(focusWidth + focusPad);
158 void RenderThemeGtk::adjustRepaintRect(const RenderObject* renderObject, IntRect& rect)
160 GtkStyleContext* context = 0;
161 bool checkInteriorFocus = false;
162 ControlPart part = renderObject->style()->appearance();
166 context = getStyleContext(part == CheckboxPart ? GTK_TYPE_CHECK_BUTTON : GTK_TYPE_RADIO_BUTTON);
168 gint indicatorSpacing;
169 gtk_style_context_get_style(context, "indicator-spacing", &indicatorSpacing, NULL);
170 rect.inflate(indicatorSpacing);
173 case SliderVerticalPart:
174 case SliderHorizontalPart:
175 context = getStyleContext(GTK_TYPE_SCALE);
178 case MenulistButtonPart:
180 context = getStyleContext(GTK_TYPE_BUTTON);
181 checkInteriorFocus = true;
185 context = getStyleContext(GTK_TYPE_ENTRY);
186 checkInteriorFocus = true;
193 if (checkInteriorFocus) {
194 gboolean interiorFocus;
195 gtk_style_context_get_style(context, "interior-focus", &interiorFocus, NULL);
199 adjustRectForFocus(context, rect);
202 static void setToggleSize(GtkStyleContext* context, RenderStyle* style)
204 // The width and height are both specified, so we shouldn't change them.
205 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
208 // Other ports hard-code this to 13 which is also the default value defined by GTK+.
209 // GTK+ users tend to demand the native look.
210 // It could be made a configuration option values other than 13 actually break site compatibility.
212 gtk_style_context_get_style(context, "indicator-size", &indicatorSize, NULL);
214 if (style->width().isIntrinsicOrAuto())
215 style->setWidth(Length(indicatorSize, Fixed));
217 if (style->height().isAuto())
218 style->setHeight(Length(indicatorSize, Fixed));
221 static void paintToggle(const RenderThemeGtk* theme, GType widgetType, RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
223 GtkStyleContext* context = getStyleContext(widgetType);
224 gtk_style_context_save(context);
226 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction())));
227 gtk_style_context_add_class(context, widgetType == GTK_TYPE_CHECK_BUTTON ? GTK_STYLE_CLASS_CHECK : GTK_STYLE_CLASS_RADIO);
230 if (!theme->isEnabled(renderObject) || theme->isReadOnlyControl(renderObject))
231 flags |= GTK_STATE_FLAG_INSENSITIVE;
232 else if (theme->isHovered(renderObject))
233 flags |= GTK_STATE_FLAG_PRELIGHT;
234 if (theme->isIndeterminate(renderObject))
235 flags |= GTK_STATE_FLAG_INCONSISTENT;
236 else if (theme->isChecked(renderObject))
237 flags |= GTK_STATE_FLAG_ACTIVE;
238 if (theme->isPressed(renderObject))
239 flags |= GTK_STATE_FLAG_SELECTED;
240 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
242 if (widgetType == GTK_TYPE_CHECK_BUTTON)
243 gtk_render_check(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
245 gtk_render_option(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
247 if (theme->isFocused(renderObject)) {
248 IntRect indicatorRect(rect);
249 gint indicatorSpacing;
250 gtk_style_context_get_style(context, "indicator-spacing", &indicatorSpacing, NULL);
251 indicatorRect.inflate(indicatorSpacing);
252 gtk_render_focus(context, paintInfo.context->platformContext()->cr(), indicatorRect.x(), indicatorRect.y(),
253 indicatorRect.width(), indicatorRect.height());
256 gtk_style_context_restore(context);
259 void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const
261 setToggleSize(getStyleContext(GTK_TYPE_CHECK_BUTTON), style);
264 bool RenderThemeGtk::paintCheckbox(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
266 paintToggle(this, GTK_TYPE_CHECK_BUTTON, renderObject, paintInfo, rect);
270 void RenderThemeGtk::setRadioSize(RenderStyle* style) const
272 setToggleSize(getStyleContext(GTK_TYPE_RADIO_BUTTON), style);
275 bool RenderThemeGtk::paintRadio(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
277 paintToggle(this, GTK_TYPE_RADIO_BUTTON, renderObject, paintInfo, rect);
281 static void renderButton(RenderTheme* theme, GtkStyleContext* context, RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
283 IntRect buttonRect(rect);
286 if (!theme->isEnabled(renderObject) || theme->isReadOnlyControl(renderObject))
287 flags |= GTK_STATE_FLAG_INSENSITIVE;
288 else if (theme->isHovered(renderObject))
289 flags |= GTK_STATE_FLAG_PRELIGHT;
290 if (theme->isPressed(renderObject))
291 flags |= GTK_STATE_FLAG_ACTIVE;
292 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
294 if (theme->isDefault(renderObject)) {
295 GtkBorder* borderPtr = 0;
296 GtkBorder border = { 1, 1, 1, 1 };
298 gtk_style_context_get_style(context, "default-border", &borderPtr, NULL);
301 gtk_border_free(borderPtr);
304 buttonRect.move(border.left, border.top);
305 buttonRect.setWidth(buttonRect.width() - (border.left + border.right));
306 buttonRect.setHeight(buttonRect.height() - (border.top + border.bottom));
308 gtk_style_context_add_class(context, GTK_STYLE_CLASS_DEFAULT);
311 gtk_render_background(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
312 gtk_render_frame(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
314 if (theme->isFocused(renderObject)) {
315 gint focusWidth, focusPad;
316 gboolean displaceFocus, interiorFocus;
317 gtk_style_context_get_style(context,
318 "focus-line-width", &focusWidth,
319 "focus-padding", &focusPad,
320 "interior-focus", &interiorFocus,
321 "displace-focus", &displaceFocus,
325 GtkBorder borderWidth;
326 gtk_style_context_get_border(context, static_cast<GtkStateFlags>(flags), &borderWidth);
328 buttonRect = IntRect(buttonRect.x() + borderWidth.left + focusPad, buttonRect.y() + borderWidth.top + focusPad,
329 buttonRect.width() - (2 * focusPad + borderWidth.left + borderWidth.right),
330 buttonRect.height() - (2 * focusPad + borderWidth.top + borderWidth.bottom));
332 buttonRect.inflate(focusWidth + focusPad);
334 if (displaceFocus && theme->isPressed(renderObject)) {
335 gint childDisplacementX;
336 gint childDisplacementY;
337 gtk_style_context_get_style(context,
338 "child-displacement-x", &childDisplacementX,
339 "child-displacement-y", &childDisplacementY,
341 buttonRect.move(childDisplacementX, childDisplacementY);
344 gtk_render_focus(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
347 bool RenderThemeGtk::paintButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
349 GtkStyleContext* context = getStyleContext(GTK_TYPE_BUTTON);
350 gtk_style_context_save(context);
352 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction())));
353 gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
355 renderButton(this, context, renderObject, paintInfo, rect);
357 gtk_style_context_restore(context);
362 static void getComboBoxMetrics(RenderStyle* style, GtkBorder& border, int& focus, int& separator)
364 // If this menu list button isn't drawn using the native theme, we
365 // don't add any extra padding beyond what WebCore already uses.
366 if (style->appearance() == NoControlPart)
369 GtkStyleContext* context = getStyleContext(GTK_TYPE_COMBO_BOX);
370 gtk_style_context_save(context);
372 gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
373 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(style->direction())));
375 gtk_style_context_get_border(context, static_cast<GtkStateFlags>(0), &border);
377 gboolean interiorFocus;
378 gint focusWidth, focusPad;
379 gtk_style_context_get_style(context,
380 "interior-focus", &interiorFocus,
381 "focus-line-width", &focusWidth,
382 "focus-padding", &focusPad, NULL);
383 focus = interiorFocus ? focusWidth + focusPad : 0;
385 gtk_style_context_restore(context);
387 context = getStyleContext(GTK_TYPE_SEPARATOR);
388 gtk_style_context_save(context);
390 GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(style->direction()));
391 gtk_style_context_set_direction(context, direction);
392 gtk_style_context_add_class(context, "separator");
394 gboolean wideSeparators;
396 gtk_style_context_get_style(context,
397 "wide-separators", &wideSeparators,
398 "separator-width", &separatorWidth,
401 // GTK+ always uses border.left, regardless of text direction. See gtkseperator.c.
403 separatorWidth = border.left;
405 separator = separatorWidth;
407 gtk_style_context_restore(context);
410 int RenderThemeGtk::popupInternalPaddingLeft(RenderStyle* style) const
412 GtkBorder borderWidth = { 0, 0, 0, 0 };
413 int focusWidth = 0, separatorWidth = 0;
414 getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
415 int left = borderWidth.left + focusWidth;
416 if (style->direction() == RTL)
417 left += separatorWidth + minArrowSize;
421 int RenderThemeGtk::popupInternalPaddingRight(RenderStyle* style) const
423 GtkBorder borderWidth = { 0, 0, 0, 0 };
424 int focusWidth = 0, separatorWidth = 0;
425 getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
426 int right = borderWidth.right + focusWidth;
427 if (style->direction() == LTR)
428 right += separatorWidth + minArrowSize;
432 int RenderThemeGtk::popupInternalPaddingTop(RenderStyle* style) const
434 GtkBorder borderWidth = { 0, 0, 0, 0 };
435 int focusWidth = 0, separatorWidth = 0;
436 getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
437 return borderWidth.top + focusWidth;
440 int RenderThemeGtk::popupInternalPaddingBottom(RenderStyle* style) const
442 GtkBorder borderWidth = { 0, 0, 0, 0 };
443 int focusWidth = 0, separatorWidth = 0;
444 getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
445 return borderWidth.bottom + focusWidth;
448 bool RenderThemeGtk::paintMenuList(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
450 cairo_t* cairoContext = paintInfo.context->platformContext()->cr();
451 GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction()));
454 GtkStyleContext* buttonStyleContext = getStyleContext(GTK_TYPE_BUTTON);
455 gtk_style_context_save(buttonStyleContext);
456 gtk_style_context_set_direction(buttonStyleContext, direction);
457 gtk_style_context_add_class(buttonStyleContext, GTK_STYLE_CLASS_BUTTON);
458 renderButton(this, buttonStyleContext, renderObject, paintInfo, rect);
460 // Get the inner rectangle.
461 gint focusWidth, focusPad;
462 GtkBorder* innerBorderPtr = 0;
463 GtkBorder innerBorder = { 1, 1, 1, 1 };
464 gtk_style_context_get_style(buttonStyleContext,
465 "inner-border", &innerBorderPtr,
466 "focus-line-width", &focusWidth,
467 "focus-padding", &focusPad,
469 if (innerBorderPtr) {
470 innerBorder = *innerBorderPtr;
471 gtk_border_free(innerBorderPtr);
474 GtkBorder borderWidth;
475 GtkStateFlags state = gtk_style_context_get_state(buttonStyleContext);
476 gtk_style_context_get_border(buttonStyleContext, state, &borderWidth);
478 focusWidth += focusPad;
479 IntRect innerRect(rect.x() + innerBorder.left + borderWidth.left + focusWidth,
480 rect.y() + innerBorder.top + borderWidth.top + focusWidth,
481 rect.width() - borderWidth.left - borderWidth.right - innerBorder.left - innerBorder.right - (2 * focusWidth),
482 rect.height() - borderWidth.top - borderWidth.bottom - innerBorder.top - innerBorder.bottom - (2 * focusWidth));
484 if (isPressed(renderObject)) {
485 gint childDisplacementX;
486 gint childDisplacementY;
487 gtk_style_context_get_style(buttonStyleContext,
488 "child-displacement-x", &childDisplacementX,
489 "child-displacement-y", &childDisplacementY,
491 innerRect.move(childDisplacementX, childDisplacementY);
493 innerRect.setWidth(max(1, innerRect.width()));
494 innerRect.setHeight(max(1, innerRect.height()));
496 gtk_style_context_restore(buttonStyleContext);
499 GtkStyleContext* arrowStyleContext = getStyleContext(GTK_TYPE_ARROW);
500 gtk_style_context_save(arrowStyleContext);
502 gtk_style_context_set_direction(arrowStyleContext, direction);
503 gtk_style_context_add_class(arrowStyleContext, "arrow");
504 gtk_style_context_add_class(arrowStyleContext, GTK_STYLE_CLASS_BUTTON);
507 gtk_style_context_get_style(arrowStyleContext, "arrow-scaling", &arrowScaling, NULL);
509 IntSize arrowSize(minArrowSize, innerRect.height());
510 FloatPoint arrowPosition(innerRect.location());
511 if (direction == GTK_TEXT_DIR_LTR)
512 arrowPosition.move(innerRect.width() - arrowSize.width(), 0);
514 // GTK+ actually fetches the xalign and valign values from the widget, but since we
515 // don't have a widget here, we are just using the default xalign and valign values of 0.5.
516 gint extent = std::min(arrowSize.width(), arrowSize.height()) * arrowScaling;
517 arrowPosition.move((arrowSize.width() - extent) / 2, (arrowSize.height() - extent) / 2);
519 gtk_style_context_set_state(arrowStyleContext, state);
520 gtk_render_arrow(arrowStyleContext, cairoContext, G_PI, arrowPosition.x(), arrowPosition.y(), extent);
522 gtk_style_context_restore(arrowStyleContext);
524 // Paint the separator if needed.
525 GtkStyleContext* separatorStyleContext = getStyleContext(GTK_TYPE_COMBO_BOX);
526 gtk_style_context_save(separatorStyleContext);
528 gtk_style_context_set_direction(separatorStyleContext, direction);
529 gtk_style_context_add_class(separatorStyleContext, "separator");
531 gboolean wideSeparators;
533 gtk_style_context_get_style(separatorStyleContext,
534 "wide-separators", &wideSeparators,
535 "separator-width", &separatorWidth,
537 if (wideSeparators && !separatorWidth) {
538 gtk_style_context_restore(separatorStyleContext);
542 gtk_style_context_set_state(separatorStyleContext, state);
543 IntPoint separatorPosition(arrowPosition.x(), innerRect.y());
544 if (wideSeparators) {
545 if (direction == GTK_TEXT_DIR_LTR)
546 separatorPosition.move(-separatorWidth, 0);
548 separatorPosition.move(arrowSize.width(), 0);
550 gtk_render_frame(separatorStyleContext, cairoContext,
551 separatorPosition.x(), separatorPosition.y(),
552 separatorWidth, innerRect.height());
555 gtk_style_context_get_padding(separatorStyleContext, state, &padding);
557 gtk_style_context_get_border(separatorStyleContext, state, &border);
559 if (direction == GTK_TEXT_DIR_LTR)
560 separatorPosition.move(-(padding.left + border.left), 0);
562 separatorPosition.move(arrowSize.width(), 0);
564 cairo_save(cairoContext);
566 // An extra clip prevents the separator bleeding outside of the specified rectangle because of subpixel positioning.
567 cairo_rectangle(cairoContext, separatorPosition.x(), separatorPosition.y(), border.left, innerRect.height());
568 cairo_clip(cairoContext);
569 gtk_render_line(separatorStyleContext, cairoContext,
570 separatorPosition.x(), separatorPosition.y(),
571 separatorPosition.x(), innerRect.maxY());
572 cairo_restore(cairoContext);
575 gtk_style_context_restore(separatorStyleContext);
579 bool RenderThemeGtk::paintTextField(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
581 GtkStyleContext* context = getStyleContext(GTK_TYPE_ENTRY);
582 gtk_style_context_save(context);
584 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction())));
585 gtk_style_context_add_class(context, GTK_STYLE_CLASS_ENTRY);
588 if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
589 flags |= GTK_STATE_FLAG_INSENSITIVE;
590 else if (isFocused(renderObject))
591 flags |= GTK_STATE_FLAG_FOCUSED;
592 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
594 gtk_render_background(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
595 gtk_render_frame(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
597 if (isFocused(renderObject) && isEnabled(renderObject)) {
598 gboolean interiorFocus;
599 gint focusWidth, focusPad;
600 gtk_style_context_get_style(context,
601 "interior-focus", &interiorFocus,
602 "focus-line-width", &focusWidth,
603 "focus-padding", &focusPad,
605 if (!interiorFocus) {
606 IntRect focusRect(rect);
607 focusRect.inflate(focusWidth + focusPad);
608 gtk_render_focus(context, paintInfo.context->platformContext()->cr(),
609 focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
613 gtk_style_context_restore(context);
618 static void applySliderStyleContextClasses(GtkStyleContext* context, ControlPart part)
620 gtk_style_context_add_class(context, GTK_STYLE_CLASS_SCALE);
621 if (part == SliderHorizontalPart || part == SliderThumbHorizontalPart)
622 gtk_style_context_add_class(context, GTK_STYLE_CLASS_HORIZONTAL);
623 else if (part == SliderVerticalPart || part == SliderThumbVerticalPart)
624 gtk_style_context_add_class(context, GTK_STYLE_CLASS_VERTICAL);
627 bool RenderThemeGtk::paintSliderTrack(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
629 ControlPart part = renderObject->style()->appearance();
630 ASSERT_UNUSED(part, part == SliderHorizontalPart || part == SliderVerticalPart || part == MediaVolumeSliderPart);
632 GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE);
633 gtk_style_context_save(context);
635 gtk_style_context_set_direction(context, gtkTextDirection(renderObject->style()->direction()));
636 applySliderStyleContextClasses(context, part);
637 gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH);
639 if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
640 gtk_style_context_set_state(context, GTK_STATE_FLAG_INSENSITIVE);
642 gtk_render_background(context, paintInfo.context->platformContext()->cr(),
643 rect.x(), rect.y(), rect.width(), rect.height());
644 gtk_render_frame(context, paintInfo.context->platformContext()->cr(),
645 rect.x(), rect.y(), rect.width(), rect.height());
647 if (isFocused(renderObject)) {
648 gint focusWidth, focusPad;
649 gtk_style_context_get_style(context,
650 "focus-line-width", &focusWidth,
651 "focus-padding", &focusPad, NULL);
652 IntRect focusRect(rect);
653 focusRect.inflate(focusWidth + focusPad);
654 gtk_render_focus(context, paintInfo.context->platformContext()->cr(),
655 focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
658 gtk_style_context_restore(context);
662 bool RenderThemeGtk::paintSliderThumb(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
664 ControlPart part = renderObject->style()->appearance();
665 ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
667 GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE);
668 gtk_style_context_save(context);
670 gtk_style_context_set_direction(context, gtkTextDirection(renderObject->style()->direction()));
671 applySliderStyleContextClasses(context, part);
672 gtk_style_context_add_class(context, GTK_STYLE_CLASS_SLIDER);
675 if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
676 flags |= GTK_STATE_FLAG_INSENSITIVE;
677 else if (isHovered(renderObject))
678 flags |= GTK_STATE_FLAG_PRELIGHT;
679 if (isPressed(renderObject))
680 flags |= GTK_STATE_FLAG_ACTIVE;
681 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
683 gtk_render_slider(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height(),
684 part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
686 gtk_style_context_restore(context);
691 void RenderThemeGtk::adjustSliderThumbSize(RenderStyle* style) const
693 ControlPart part = style->appearance();
695 if (part == MediaSliderThumbPart) {
696 adjustMediaSliderThumbSize(style);
701 gint sliderWidth, sliderLength;
702 gtk_style_context_get_style(getStyleContext(GTK_TYPE_SCALE),
703 "slider-width", &sliderWidth,
704 "slider-length", &sliderLength,
706 if (part == SliderThumbHorizontalPart) {
707 style->setWidth(Length(sliderLength, Fixed));
708 style->setHeight(Length(sliderWidth, Fixed));
711 ASSERT(part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
712 style->setWidth(Length(sliderWidth, Fixed));
713 style->setHeight(Length(sliderLength, Fixed));
716 #if ENABLE(PROGRESS_TAG)
717 bool RenderThemeGtk::paintProgressBar(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
719 if (!renderObject->isProgress())
722 GtkStyleContext* context = getStyleContext(GTK_TYPE_PROGRESS_BAR);
723 gtk_style_context_save(context);
725 gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH);
727 gtk_render_background(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
728 gtk_render_frame(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
730 gtk_style_context_restore(context);
732 gtk_style_context_save(context);
733 gtk_style_context_add_class(context, GTK_STYLE_CLASS_PROGRESSBAR);
737 gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding);
738 IntRect progressRect(rect.x() + padding.left, rect.y() + padding.top,
739 rect.width() - (padding.left + padding.right),
740 rect.height() - (padding.top + padding.bottom));
741 progressRect = RenderThemeGtk::calculateProgressRect(renderObject, progressRect);
743 if (!progressRect.isEmpty())
744 gtk_render_activity(context, paintInfo.context->platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
746 gtk_style_context_restore(context);
751 static gint spinButtonArrowSize(GtkStyleContext* context)
753 const PangoFontDescription* fontDescription = gtk_style_context_get_font(context, static_cast<GtkStateFlags>(0));
754 gint fontSize = pango_font_description_get_size(fontDescription);
755 gint arrowSize = max(PANGO_PIXELS(fontSize), minSpinButtonArrowSize);
757 return arrowSize - arrowSize % 2; // Force even.
760 void RenderThemeGtk::adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
762 GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON);
765 gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding);
767 int width = spinButtonArrowSize(context) + padding.left + padding.right;
768 style->setWidth(Length(width, Fixed));
769 style->setMinWidth(Length(width, Fixed));
772 static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* context, RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType)
774 ASSERT(arrowType == GTK_ARROW_UP || arrowType == GTK_ARROW_DOWN);
776 gtk_style_context_save(context);
777 gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
779 GtkTextDirection direction = gtk_style_context_get_direction(context);
780 guint state = static_cast<guint>(gtk_style_context_get_state(context));
781 if (!(state & GTK_STATE_FLAG_INSENSITIVE)) {
782 if (theme->isPressed(renderObject)) {
783 if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartPressed(renderObject))
784 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartPressed(renderObject)))
785 state |= GTK_STATE_FLAG_ACTIVE;
786 } else if (theme->isHovered(renderObject)) {
787 if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartHovered(renderObject))
788 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartHovered(renderObject)))
789 state |= GTK_STATE_FLAG_PRELIGHT;
792 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(state));
795 IntRect buttonRect(rect);
796 guint junction = gtk_style_context_get_junction_sides(context);
797 if (arrowType == GTK_ARROW_UP)
798 junction |= GTK_JUNCTION_BOTTOM;
800 junction |= GTK_JUNCTION_TOP;
801 buttonRect.move(0, rect.height() / 2);
803 buttonRect.setHeight(rect.height() / 2);
804 gtk_style_context_set_junction_sides(context, static_cast<GtkJunctionSides>(junction));
806 gtk_render_background(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
807 gtk_render_frame(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
809 // Paint arrow centered inside button.
810 // This code is based on gtkspinbutton.c code.
813 if (arrowType == GTK_ARROW_UP) {
815 arrowRect.setY(rect.y());
816 arrowRect.setHeight(rect.height() / 2 - 2);
819 arrowRect.setY(rect.y() + buttonRect.y());
820 arrowRect.setHeight(rect.height() - arrowRect.y() - 2);
822 arrowRect.setWidth(rect.width() - 3);
823 if (direction == GTK_TEXT_DIR_LTR)
824 arrowRect.setX(rect.x() + 1);
826 arrowRect.setX(rect.x() + 2);
828 gint width = arrowRect.width() / 2;
829 width -= width % 2 - 1; // Force odd.
830 gint height = (width + 1) / 2;
832 arrowRect.move((arrowRect.width() - width) / 2, (arrowRect.height() - height) / 2);
833 gtk_render_arrow(context, paintInfo.context->platformContext()->cr(), angle, arrowRect.x(), arrowRect.y(), width);
835 gtk_style_context_restore(context);
838 bool RenderThemeGtk::paintInnerSpinButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
840 GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON);
841 gtk_style_context_save(context);
843 GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction()));
844 gtk_style_context_set_direction(context, direction);
847 if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
848 flags |= GTK_STATE_FLAG_INSENSITIVE;
849 else if (isFocused(renderObject))
850 flags |= GTK_STATE_FLAG_FOCUSED;
851 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
852 gtk_style_context_remove_class(context, GTK_STYLE_CLASS_ENTRY);
854 paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_UP);
855 paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_DOWN);
857 gtk_style_context_restore(context);
862 GRefPtr<GdkPixbuf> getStockIconForWidgetType(GType widgetType, const char* iconName, gint direction, gint state, gint iconSize)
864 GtkStyleContext* context = getStyleContext(widgetType);
865 GtkIconSet* iconSet = gtk_style_context_lookup_icon_set(context, iconName);
867 gtk_style_context_save(context);
870 if (state == GTK_STATE_PRELIGHT)
871 flags |= GTK_STATE_FLAG_PRELIGHT;
872 else if (state == GTK_STATE_INSENSITIVE)
873 flags |= GTK_STATE_FLAG_INSENSITIVE;
875 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
876 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(direction));
877 GdkPixbuf* icon = gtk_icon_set_render_icon_pixbuf(iconSet, context, static_cast<GtkIconSize>(iconSize));
879 gtk_style_context_restore(context);
881 return adoptGRef(icon);
884 Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
886 GdkRGBA gdkRGBAColor;
887 gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
891 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const
893 GdkRGBA gdkRGBAColor;
894 gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
898 Color RenderThemeGtk::platformActiveSelectionForegroundColor() const
900 GdkRGBA gdkRGBAColor;
901 gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
905 Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const
907 GdkRGBA gdkRGBAColor;
908 gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
912 Color RenderThemeGtk::activeListBoxSelectionBackgroundColor() const
914 GdkRGBA gdkRGBAColor;
915 gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
919 Color RenderThemeGtk::inactiveListBoxSelectionBackgroundColor() const
921 GdkRGBA gdkRGBAColor;
922 gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
926 Color RenderThemeGtk::activeListBoxSelectionForegroundColor() const
928 GdkRGBA gdkRGBAColor;
929 gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
933 Color RenderThemeGtk::inactiveListBoxSelectionForegroundColor() const
935 GdkRGBA gdkRGBAColor;
936 gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
940 Color RenderThemeGtk::systemColor(int cssValueId) const
942 GdkRGBA gdkRGBAColor;
944 switch (cssValueId) {
945 case CSSValueButtontext:
946 gtk_style_context_get_color(getStyleContext(GTK_TYPE_BUTTON), static_cast<GtkStateFlags>(0), &gdkRGBAColor);
948 case CSSValueCaptiontext:
949 gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), static_cast<GtkStateFlags>(0), &gdkRGBAColor);
952 return RenderTheme::systemColor(cssValueId);
956 } // namespace WebCore
958 #endif // !GTK_API_VERSION_2