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 "RenderElement.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->value.get());
61 Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
64 static StyleContextMap& styleContextMap()
66 DEPRECATED_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 StyleContextMap::AddResult result = styleContextMap().add(widgetType, nullptr);
81 if (!result.isNewEntry)
82 return result.iterator->value.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 gtk_widget_path_iter_add_class(path, 1, "text-button");
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);
112 GRefPtr<GtkStyleContext> context = adoptGRef(gtk_style_context_new());
113 gtk_style_context_set_path(context.get(), path);
114 gtk_widget_path_free(path);
116 result.iterator->value = context;
117 return context.get();
120 GtkStyleContext* RenderThemeGtk::gtkScrollbarStyle()
122 return getStyleContext(GTK_TYPE_SCROLLBAR);
125 // This is not a static method, because we want to avoid having GTK+ headers in RenderThemeGtk.h.
126 extern GtkTextDirection gtkTextDirection(TextDirection);
128 void RenderThemeGtk::platformInit()
132 RenderThemeGtk::~RenderThemeGtk()
137 void RenderThemeGtk::initMediaColors()
140 GtkStyleContext* containerContext = getStyleContext(GTK_TYPE_CONTAINER);
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;
151 static void adjustRectForFocus(GtkStyleContext* context, FloatRect& rect)
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);
160 void RenderThemeGtk::adjustRepaintRect(const RenderObject& renderObject, FloatRect& rect)
162 GtkStyleContext* context = 0;
163 bool checkInteriorFocus = false;
164 ControlPart part = renderObject.style().appearance();
168 context = getStyleContext(part == CheckboxPart ? GTK_TYPE_CHECK_BUTTON : GTK_TYPE_RADIO_BUTTON);
170 gint indicatorSpacing;
171 gtk_style_context_get_style(context, "indicator-spacing", &indicatorSpacing, NULL);
172 rect.inflate(indicatorSpacing);
175 case SliderVerticalPart:
176 case SliderHorizontalPart:
177 context = getStyleContext(GTK_TYPE_SCALE);
180 case MenulistButtonPart:
182 context = getStyleContext(GTK_TYPE_BUTTON);
183 checkInteriorFocus = true;
187 context = getStyleContext(GTK_TYPE_ENTRY);
188 checkInteriorFocus = true;
195 if (checkInteriorFocus) {
196 gboolean interiorFocus;
197 gtk_style_context_get_style(context, "interior-focus", &interiorFocus, NULL);
201 adjustRectForFocus(context, rect);
204 static void setToggleSize(GtkStyleContext* context, RenderStyle* style)
206 // The width and height are both specified, so we shouldn't change them.
207 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
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.
214 gtk_style_context_get_style(context, "indicator-size", &indicatorSize, NULL);
216 if (style->width().isIntrinsicOrAuto())
217 style->setWidth(Length(indicatorSize, Fixed));
219 if (style->height().isAuto())
220 style->setHeight(Length(indicatorSize, Fixed));
223 static void paintToggle(const RenderThemeGtk* theme, GType widgetType, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& fullRect)
225 GtkStyleContext* context = getStyleContext(widgetType);
226 gtk_style_context_save(context);
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.
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.
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.
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);
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));
261 if (widgetType == GTK_TYPE_CHECK_BUTTON)
262 gtk_render_check(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
264 gtk_render_option(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
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());
275 gtk_style_context_restore(context);
278 void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const
280 setToggleSize(getStyleContext(GTK_TYPE_CHECK_BUTTON), style);
283 bool RenderThemeGtk::paintCheckbox(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
285 paintToggle(this, GTK_TYPE_CHECK_BUTTON, renderObject, paintInfo, rect);
289 void RenderThemeGtk::setRadioSize(RenderStyle* style) const
291 setToggleSize(getStyleContext(GTK_TYPE_RADIO_BUTTON), style);
294 bool RenderThemeGtk::paintRadio(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
296 paintToggle(this, GTK_TYPE_RADIO_BUTTON, renderObject, paintInfo, rect);
300 static void renderButton(RenderTheme* theme, GtkStyleContext* context, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
302 IntRect buttonRect(rect);
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));
313 if (theme->isDefault(renderObject)) {
314 GtkBorder* borderPtr = 0;
315 GtkBorder border = { 1, 1, 1, 1 };
317 gtk_style_context_get_style(context, "default-border", &borderPtr, NULL);
320 gtk_border_free(borderPtr);
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));
327 gtk_style_context_add_class(context, GTK_STYLE_CLASS_DEFAULT);
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());
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,
344 GtkBorder borderWidth;
345 gtk_style_context_get_border(context, static_cast<GtkStateFlags>(flags), &borderWidth);
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));
351 buttonRect.inflate(focusWidth + focusPad);
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,
360 buttonRect.move(childDisplacementX, childDisplacementY);
363 gtk_render_focus(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
366 bool RenderThemeGtk::paintButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
368 GtkStyleContext* context = getStyleContext(GTK_TYPE_BUTTON);
369 gtk_style_context_save(context);
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);
374 renderButton(this, context, renderObject, paintInfo, rect);
376 gtk_style_context_restore(context);
381 static void getComboBoxMetrics(RenderStyle* style, GtkBorder& border, int& focus, int& separator)
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)
388 GtkStyleContext* context = getStyleContext(GTK_TYPE_COMBO_BOX);
389 gtk_style_context_save(context);
391 gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
392 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(style->direction())));
394 gtk_style_context_get_border(context, static_cast<GtkStateFlags>(0), &border);
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;
404 gtk_style_context_restore(context);
406 context = getStyleContext(GTK_TYPE_SEPARATOR);
407 gtk_style_context_save(context);
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");
413 gboolean wideSeparators;
415 gtk_style_context_get_style(context,
416 "wide-separators", &wideSeparators,
417 "separator-width", &separatorWidth,
420 // GTK+ always uses border.left, regardless of text direction. See gtkseperator.c.
422 separatorWidth = border.left;
424 separator = separatorWidth;
426 gtk_style_context_restore(context);
429 int RenderThemeGtk::popupInternalPaddingLeft(RenderStyle* style) const
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;
440 int RenderThemeGtk::popupInternalPaddingRight(RenderStyle* style) const
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;
451 int RenderThemeGtk::popupInternalPaddingTop(RenderStyle* style) const
453 GtkBorder borderWidth = { 0, 0, 0, 0 };
454 int focusWidth = 0, separatorWidth = 0;
455 getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
456 return borderWidth.top + focusWidth;
459 int RenderThemeGtk::popupInternalPaddingBottom(RenderStyle* style) const
461 GtkBorder borderWidth = { 0, 0, 0, 0 };
462 int focusWidth = 0, separatorWidth = 0;
463 getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
464 return borderWidth.bottom + focusWidth;
467 bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& r)
469 // FIXME: adopt subpixel themes.
470 IntRect rect = IntRect(r);
472 cairo_t* cairoContext = paintInfo.context->platformContext()->cr();
473 GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction()));
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);
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,
491 if (innerBorderPtr) {
492 innerBorder = *innerBorderPtr;
493 gtk_border_free(innerBorderPtr);
496 GtkBorder borderWidth;
497 GtkStateFlags state = gtk_style_context_get_state(buttonStyleContext);
498 gtk_style_context_get_border(buttonStyleContext, state, &borderWidth);
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));
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,
513 innerRect.move(childDisplacementX, childDisplacementY);
515 innerRect.setWidth(std::max(1, innerRect.width()));
516 innerRect.setHeight(std::max(1, innerRect.height()));
518 gtk_style_context_restore(buttonStyleContext);
521 GtkStyleContext* arrowStyleContext = getStyleContext(GTK_TYPE_ARROW);
522 gtk_style_context_save(arrowStyleContext);
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);
529 gtk_style_context_get_style(arrowStyleContext, "arrow-scaling", &arrowScaling, NULL);
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);
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);
541 gtk_style_context_set_state(arrowStyleContext, state);
542 gtk_render_arrow(arrowStyleContext, cairoContext, G_PI, arrowPosition.x(), arrowPosition.y(), extent);
544 gtk_style_context_restore(arrowStyleContext);
546 // Paint the separator if needed.
547 GtkStyleContext* separatorStyleContext = getStyleContext(GTK_TYPE_COMBO_BOX);
548 gtk_style_context_save(separatorStyleContext);
550 gtk_style_context_set_direction(separatorStyleContext, direction);
551 gtk_style_context_add_class(separatorStyleContext, "separator");
553 gboolean wideSeparators;
555 gtk_style_context_get_style(separatorStyleContext,
556 "wide-separators", &wideSeparators,
557 "separator-width", &separatorWidth,
559 if (wideSeparators && !separatorWidth) {
560 gtk_style_context_restore(separatorStyleContext);
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);
570 separatorPosition.move(arrowSize.width(), 0);
572 gtk_render_frame(separatorStyleContext, cairoContext,
573 separatorPosition.x(), separatorPosition.y(),
574 separatorWidth, innerRect.height());
577 gtk_style_context_get_padding(separatorStyleContext, state, &padding);
579 gtk_style_context_get_border(separatorStyleContext, state, &border);
581 if (direction == GTK_TEXT_DIR_LTR)
582 separatorPosition.move(-(padding.left + border.left), 0);
584 separatorPosition.move(arrowSize.width(), 0);
586 cairo_save(cairoContext);
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);
597 gtk_style_context_restore(separatorStyleContext);
601 bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
603 GtkStyleContext* context = getStyleContext(GTK_TYPE_ENTRY);
604 gtk_style_context_save(context);
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);
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));
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());
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,
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());
635 gtk_style_context_restore(context);
640 static void applySliderStyleContextClasses(GtkStyleContext* context, ControlPart part)
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);
649 bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
651 ControlPart part = renderObject.style().appearance();
652 ASSERT_UNUSED(part, part == SliderHorizontalPart || part == SliderVerticalPart || part == MediaVolumeSliderPart);
654 GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE);
655 gtk_style_context_save(context);
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);
661 if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
662 gtk_style_context_set_state(context, GTK_STATE_FLAG_INSENSITIVE);
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());
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());
680 gtk_style_context_restore(context);
684 bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
686 ControlPart part = renderObject.style().appearance();
687 ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
689 GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE);
690 gtk_style_context_save(context);
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);
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));
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);
708 gtk_style_context_restore(context);
713 void RenderThemeGtk::adjustSliderThumbSize(RenderStyle* style, Element*) const
715 ControlPart part = style->appearance();
716 if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
719 gint sliderWidth, sliderLength;
720 gtk_style_context_get_style(getStyleContext(GTK_TYPE_SCALE),
721 "slider-width", &sliderWidth,
722 "slider-length", &sliderLength,
724 if (part == SliderThumbHorizontalPart) {
725 style->setWidth(Length(sliderLength, Fixed));
726 style->setHeight(Length(sliderWidth, Fixed));
729 ASSERT(part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
730 style->setWidth(Length(sliderWidth, Fixed));
731 style->setHeight(Length(sliderLength, Fixed));
734 bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
736 if (!renderObject.isProgress())
739 GtkStyleContext* context = getStyleContext(GTK_TYPE_PROGRESS_BAR);
740 gtk_style_context_save(context);
742 gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH);
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());
747 gtk_style_context_restore(context);
749 gtk_style_context_save(context);
750 gtk_style_context_add_class(context, GTK_STYLE_CLASS_PROGRESSBAR);
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);
760 if (!progressRect.isEmpty())
761 gtk_render_activity(context, paintInfo.context->platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
763 gtk_style_context_restore(context);
767 static gint spinButtonArrowSize(GtkStyleContext* context)
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);
775 return arrowSize - arrowSize % 2; // Force even.
778 void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver*, RenderStyle* style, Element*) const
780 GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON);
783 gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding);
785 int width = spinButtonArrowSize(context) + padding.left + padding.right;
786 style->setWidth(Length(width, Fixed));
787 style->setMinWidth(Length(width, Fixed));
790 static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* context, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType)
792 ASSERT(arrowType == GTK_ARROW_UP || arrowType == GTK_ARROW_DOWN);
794 gtk_style_context_save(context);
795 gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
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;
810 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(state));
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;
818 junction |= GTK_JUNCTION_TOP;
819 buttonRect.move(0, rect.height() / 2);
821 buttonRect.setHeight(rect.height() / 2);
822 gtk_style_context_set_junction_sides(context, static_cast<GtkJunctionSides>(junction));
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());
827 // Paint arrow centered inside button.
828 // This code is based on gtkspinbutton.c code.
831 if (arrowType == GTK_ARROW_UP) {
833 arrowRect.setY(rect.y());
834 arrowRect.setHeight(rect.height() / 2 - 2);
837 arrowRect.setY(rect.y() + buttonRect.y());
838 arrowRect.setHeight(rect.height() - arrowRect.y() - 2);
840 arrowRect.setWidth(rect.width() - 3);
841 if (direction == GTK_TEXT_DIR_LTR)
842 arrowRect.setX(rect.x() + 1);
844 arrowRect.setX(rect.x() + 2);
846 gint width = arrowRect.width() / 2;
847 width -= width % 2 - 1; // Force odd.
848 gint height = (width + 1) / 2;
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);
853 gtk_style_context_restore(context);
856 bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
858 GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON);
859 gtk_style_context_save(context);
861 GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction()));
862 gtk_style_context_set_direction(context, direction);
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);
872 paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_UP);
873 paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_DOWN);
875 gtk_style_context_restore(context);
880 GRefPtr<GdkPixbuf> getStockIconForWidgetType(GType widgetType, const char* iconName, gint direction, gint state, gint iconSize)
884 GtkStyleContext* context = getStyleContext(widgetType);
885 GtkIconSet* iconSet = gtk_style_context_lookup_icon_set(context, iconName);
887 gtk_style_context_save(context);
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;
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));
899 gtk_style_context_restore(context);
901 return adoptGRef(icon);
904 GRefPtr<GdkPixbuf> getStockSymbolicIconForWidgetType(GType widgetType, const char* symbolicIconName, const char* fallbackStockIconName, gint direction, gint state, gint iconSize)
906 GtkStyleContext* context = getStyleContext(widgetType);
908 gtk_style_context_save(context);
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;
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));
922 icon = gtk_icon_info_load_symbolic_for_context(info, context, 0, 0);
923 gtk_icon_info_free(info);
926 gtk_style_context_restore(context);
929 if (!fallbackStockIconName)
931 return getStockIconForWidgetType(widgetType, fallbackStockIconName, direction, state, iconSize);
934 return adoptGRef(icon);
937 Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
939 GdkRGBA gdkRGBAColor;
940 gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
944 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const
946 GdkRGBA gdkRGBAColor;
947 gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
951 Color RenderThemeGtk::platformActiveSelectionForegroundColor() const
953 GdkRGBA gdkRGBAColor;
954 gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
958 Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const
960 GdkRGBA gdkRGBAColor;
961 gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
965 Color RenderThemeGtk::platformActiveListBoxSelectionBackgroundColor() const
967 GdkRGBA gdkRGBAColor;
968 gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
972 Color RenderThemeGtk::platformInactiveListBoxSelectionBackgroundColor() const
974 GdkRGBA gdkRGBAColor;
975 gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
979 Color RenderThemeGtk::platformActiveListBoxSelectionForegroundColor() const
981 GdkRGBA gdkRGBAColor;
982 gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
986 Color RenderThemeGtk::platformInactiveListBoxSelectionForegroundColor() const
988 GdkRGBA gdkRGBAColor;
989 gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
993 Color RenderThemeGtk::systemColor(CSSValueID cssValueId) const
995 GdkRGBA gdkRGBAColor;
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;
1005 return RenderTheme::systemColor(cssValueId);
1009 } // namespace WebCore
1011 #endif // !GTK_API_VERSION_2