| /* |
| * Copyright (C) 2007 Apple Inc. |
| * Copyright (C) 2007 Alp Toker <alp@atoker.com> |
| * Copyright (C) 2008 Collabora Ltd. |
| * Copyright (C) 2009 Kenneth Rohde Christiansen |
| * Copyright (C) 2010 Igalia S.L. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| * |
| */ |
| |
| #include "config.h" |
| #include "RenderThemeGtk.h" |
| |
| #ifndef GTK_API_VERSION_2 |
| |
| #include "CSSValueKeywords.h" |
| #include "GraphicsContext.h" |
| #include "GtkVersioning.h" |
| #include "HTMLNames.h" |
| #include "MediaControlElements.h" |
| #include "Page.h" |
| #include "PaintInfo.h" |
| #include "PlatformContextCairo.h" |
| #include "RenderObject.h" |
| #include "TextDirection.h" |
| #include "UserAgentStyleSheets.h" |
| #include <cmath> |
| #include <gdk/gdk.h> |
| #include <gtk/gtk.h> |
| |
| namespace WebCore { |
| |
| // This is the default value defined by GTK+, where it was defined as MIN_ARROW_SIZE in gtkarrow.c. |
| static const int minArrowSize = 15; |
| // This is the default value defined by GTK+, where it was defined as MIN_ARROW_WIDTH in gtkspinbutton.c. |
| static const int minSpinButtonArrowSize = 6; |
| |
| typedef HashMap<GType, GRefPtr<GtkStyleContext> > StyleContextMap; |
| static StyleContextMap& styleContextMap(); |
| |
| static void gtkStyleChangedCallback(GObject*, GParamSpec*) |
| { |
| StyleContextMap::const_iterator end = styleContextMap().end(); |
| for (StyleContextMap::const_iterator iter = styleContextMap().begin(); iter != end; ++iter) |
| gtk_style_context_invalidate(iter->second.get()); |
| |
| Page::scheduleForcedStyleRecalcForAllPages(); |
| } |
| |
| static StyleContextMap& styleContextMap() |
| { |
| DEFINE_STATIC_LOCAL(StyleContextMap, map, ()); |
| |
| static bool initialized = false; |
| if (!initialized) { |
| GtkSettings* settings = gtk_settings_get_default(); |
| g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(gtkStyleChangedCallback), 0); |
| g_signal_connect(settings, "notify::gtk-color-scheme", G_CALLBACK(gtkStyleChangedCallback), 0); |
| initialized = true; |
| } |
| return map; |
| } |
| |
| static GtkStyleContext* getStyleContext(GType widgetType) |
| { |
| StyleContextMap::AddResult result = styleContextMap().add(widgetType, 0); |
| if (!result.isNewEntry) |
| return result.iterator->second.get(); |
| |
| GtkWidgetPath* path = gtk_widget_path_new(); |
| gtk_widget_path_append_type(path, widgetType); |
| |
| if (widgetType == GTK_TYPE_SCROLLBAR) |
| gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SCROLLBAR); |
| else if (widgetType == GTK_TYPE_ENTRY) |
| gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_ENTRY); |
| else if (widgetType == GTK_TYPE_ARROW) |
| gtk_widget_path_iter_add_class(path, 0, "arrow"); |
| else if (widgetType == GTK_TYPE_BUTTON) |
| gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_BUTTON); |
| else if (widgetType == GTK_TYPE_SCALE) |
| gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SCALE); |
| else if (widgetType == GTK_TYPE_SEPARATOR) |
| gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SEPARATOR); |
| else if (widgetType == GTK_TYPE_PROGRESS_BAR) |
| gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_PROGRESSBAR); |
| else if (widgetType == GTK_TYPE_SPIN_BUTTON) |
| gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SPINBUTTON); |
| else if (widgetType == GTK_TYPE_TREE_VIEW) |
| gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_VIEW); |
| else if (widgetType == GTK_TYPE_CHECK_BUTTON) |
| gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_CHECK); |
| else if (widgetType == GTK_TYPE_RADIO_BUTTON) |
| gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_RADIO); |
| |
| GRefPtr<GtkStyleContext> context = adoptGRef(gtk_style_context_new()); |
| gtk_style_context_set_path(context.get(), path); |
| gtk_widget_path_free(path); |
| |
| result.iterator->second = context; |
| return context.get(); |
| } |
| |
| GtkStyleContext* RenderThemeGtk::gtkScrollbarStyle() |
| { |
| return getStyleContext(GTK_TYPE_SCROLLBAR); |
| } |
| |
| // This is not a static method, because we want to avoid having GTK+ headers in RenderThemeGtk.h. |
| extern GtkTextDirection gtkTextDirection(TextDirection); |
| |
| void RenderThemeGtk::platformInit() |
| { |
| } |
| |
| RenderThemeGtk::~RenderThemeGtk() |
| { |
| } |
| |
| #if ENABLE(VIDEO) |
| void RenderThemeGtk::initMediaColors() |
| { |
| GdkRGBA color; |
| GtkStyleContext* containerContext = getStyleContext(GTK_TYPE_CONTAINER); |
| |
| gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_NORMAL, &color); |
| m_panelColor = color; |
| gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_ACTIVE, &color); |
| m_sliderColor = color; |
| gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_SELECTED, &color); |
| m_sliderThumbColor = color; |
| } |
| #endif |
| |
| static void adjustRectForFocus(GtkStyleContext* context, IntRect& rect) |
| { |
| gint focusWidth, focusPad; |
| gtk_style_context_get_style(context, |
| "focus-line-width", &focusWidth, |
| "focus-padding", &focusPad, NULL); |
| rect.inflate(focusWidth + focusPad); |
| } |
| |
| void RenderThemeGtk::adjustRepaintRect(const RenderObject* renderObject, IntRect& rect) |
| { |
| GtkStyleContext* context = 0; |
| bool checkInteriorFocus = false; |
| ControlPart part = renderObject->style()->appearance(); |
| switch (part) { |
| case CheckboxPart: |
| case RadioPart: |
| context = getStyleContext(part == CheckboxPart ? GTK_TYPE_CHECK_BUTTON : GTK_TYPE_RADIO_BUTTON); |
| |
| gint indicatorSpacing; |
| gtk_style_context_get_style(context, "indicator-spacing", &indicatorSpacing, NULL); |
| rect.inflate(indicatorSpacing); |
| |
| return; |
| case SliderVerticalPart: |
| case SliderHorizontalPart: |
| context = getStyleContext(GTK_TYPE_SCALE); |
| break; |
| case ButtonPart: |
| case MenulistButtonPart: |
| case MenulistPart: |
| context = getStyleContext(GTK_TYPE_BUTTON); |
| checkInteriorFocus = true; |
| break; |
| case TextFieldPart: |
| case TextAreaPart: |
| context = getStyleContext(GTK_TYPE_ENTRY); |
| checkInteriorFocus = true; |
| break; |
| default: |
| return; |
| } |
| |
| ASSERT(context); |
| if (checkInteriorFocus) { |
| gboolean interiorFocus; |
| gtk_style_context_get_style(context, "interior-focus", &interiorFocus, NULL); |
| if (interiorFocus) |
| return; |
| } |
| adjustRectForFocus(context, rect); |
| } |
| |
| static void setToggleSize(GtkStyleContext* context, RenderStyle* style) |
| { |
| // The width and height are both specified, so we shouldn't change them. |
| if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) |
| return; |
| |
| // Other ports hard-code this to 13 which is also the default value defined by GTK+. |
| // GTK+ users tend to demand the native look. |
| // It could be made a configuration option values other than 13 actually break site compatibility. |
| gint indicatorSize; |
| gtk_style_context_get_style(context, "indicator-size", &indicatorSize, NULL); |
| |
| if (style->width().isIntrinsicOrAuto()) |
| style->setWidth(Length(indicatorSize, Fixed)); |
| |
| if (style->height().isAuto()) |
| style->setHeight(Length(indicatorSize, Fixed)); |
| } |
| |
| static void paintToggle(const RenderThemeGtk* theme, GType widgetType, RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& fullRect) |
| { |
| GtkStyleContext* context = getStyleContext(widgetType); |
| gtk_style_context_save(context); |
| |
| // Some themes do not render large toggle buttons properly, so we simply |
| // shrink the rectangle back down to the default size and then center it |
| // in the full toggle button region. The reason for not simply forcing toggle |
| // buttons to be a smaller size is that we don't want to break site layouts. |
| gint indicatorSize; |
| gtk_style_context_get_style(context, "indicator-size", &indicatorSize, NULL); |
| IntRect rect(fullRect); |
| if (rect.width() > indicatorSize) { |
| rect.inflateX(-(rect.width() - indicatorSize) / 2); |
| rect.setWidth(indicatorSize); // In case rect.width() was equal to indicatorSize + 1. |
| } |
| |
| if (rect.height() > indicatorSize) { |
| rect.inflateY(-(rect.height() - indicatorSize) / 2); |
| rect.setHeight(indicatorSize); // In case rect.height() was equal to indicatorSize + 1. |
| } |
| |
| gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction()))); |
| gtk_style_context_add_class(context, widgetType == GTK_TYPE_CHECK_BUTTON ? GTK_STYLE_CLASS_CHECK : GTK_STYLE_CLASS_RADIO); |
| |
| guint flags = 0; |
| if (!theme->isEnabled(renderObject) || theme->isReadOnlyControl(renderObject)) |
| flags |= GTK_STATE_FLAG_INSENSITIVE; |
| else if (theme->isHovered(renderObject)) |
| flags |= GTK_STATE_FLAG_PRELIGHT; |
| if (theme->isIndeterminate(renderObject)) |
| flags |= GTK_STATE_FLAG_INCONSISTENT; |
| else if (theme->isChecked(renderObject)) |
| flags |= GTK_STATE_FLAG_ACTIVE; |
| if (theme->isPressed(renderObject)) |
| flags |= GTK_STATE_FLAG_SELECTED; |
| gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); |
| |
| if (widgetType == GTK_TYPE_CHECK_BUTTON) |
| gtk_render_check(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); |
| else |
| gtk_render_option(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); |
| |
| if (theme->isFocused(renderObject)) { |
| IntRect indicatorRect(rect); |
| gint indicatorSpacing; |
| gtk_style_context_get_style(context, "indicator-spacing", &indicatorSpacing, NULL); |
| indicatorRect.inflate(indicatorSpacing); |
| gtk_render_focus(context, paintInfo.context->platformContext()->cr(), indicatorRect.x(), indicatorRect.y(), |
| indicatorRect.width(), indicatorRect.height()); |
| } |
| |
| gtk_style_context_restore(context); |
| } |
| |
| void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const |
| { |
| setToggleSize(getStyleContext(GTK_TYPE_CHECK_BUTTON), style); |
| } |
| |
| bool RenderThemeGtk::paintCheckbox(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
| { |
| paintToggle(this, GTK_TYPE_CHECK_BUTTON, renderObject, paintInfo, rect); |
| return false; |
| } |
| |
| void RenderThemeGtk::setRadioSize(RenderStyle* style) const |
| { |
| setToggleSize(getStyleContext(GTK_TYPE_RADIO_BUTTON), style); |
| } |
| |
| bool RenderThemeGtk::paintRadio(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
| { |
| paintToggle(this, GTK_TYPE_RADIO_BUTTON, renderObject, paintInfo, rect); |
| return false; |
| } |
| |
| static void renderButton(RenderTheme* theme, GtkStyleContext* context, RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
| { |
| IntRect buttonRect(rect); |
| |
| guint flags = 0; |
| if (!theme->isEnabled(renderObject) || theme->isReadOnlyControl(renderObject)) |
| flags |= GTK_STATE_FLAG_INSENSITIVE; |
| else if (theme->isHovered(renderObject)) |
| flags |= GTK_STATE_FLAG_PRELIGHT; |
| if (theme->isPressed(renderObject)) |
| flags |= GTK_STATE_FLAG_ACTIVE; |
| gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); |
| |
| if (theme->isDefault(renderObject)) { |
| GtkBorder* borderPtr = 0; |
| GtkBorder border = { 1, 1, 1, 1 }; |
| |
| gtk_style_context_get_style(context, "default-border", &borderPtr, NULL); |
| if (borderPtr) { |
| border = *borderPtr; |
| gtk_border_free(borderPtr); |
| } |
| |
| buttonRect.move(border.left, border.top); |
| buttonRect.setWidth(buttonRect.width() - (border.left + border.right)); |
| buttonRect.setHeight(buttonRect.height() - (border.top + border.bottom)); |
| |
| gtk_style_context_add_class(context, GTK_STYLE_CLASS_DEFAULT); |
| } |
| |
| gtk_render_background(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); |
| gtk_render_frame(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); |
| |
| if (theme->isFocused(renderObject)) { |
| gint focusWidth, focusPad; |
| gboolean displaceFocus, interiorFocus; |
| gtk_style_context_get_style(context, |
| "focus-line-width", &focusWidth, |
| "focus-padding", &focusPad, |
| "interior-focus", &interiorFocus, |
| "displace-focus", &displaceFocus, |
| NULL); |
| |
| if (interiorFocus) { |
| GtkBorder borderWidth; |
| gtk_style_context_get_border(context, static_cast<GtkStateFlags>(flags), &borderWidth); |
| |
| buttonRect = IntRect(buttonRect.x() + borderWidth.left + focusPad, buttonRect.y() + borderWidth.top + focusPad, |
| buttonRect.width() - (2 * focusPad + borderWidth.left + borderWidth.right), |
| buttonRect.height() - (2 * focusPad + borderWidth.top + borderWidth.bottom)); |
| } else |
| buttonRect.inflate(focusWidth + focusPad); |
| |
| if (displaceFocus && theme->isPressed(renderObject)) { |
| gint childDisplacementX; |
| gint childDisplacementY; |
| gtk_style_context_get_style(context, |
| "child-displacement-x", &childDisplacementX, |
| "child-displacement-y", &childDisplacementY, |
| NULL); |
| buttonRect.move(childDisplacementX, childDisplacementY); |
| } |
| |
| gtk_render_focus(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); |
| } |
| } |
| bool RenderThemeGtk::paintButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
| { |
| GtkStyleContext* context = getStyleContext(GTK_TYPE_BUTTON); |
| gtk_style_context_save(context); |
| |
| gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction()))); |
| gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON); |
| |
| renderButton(this, context, renderObject, paintInfo, rect); |
| |
| gtk_style_context_restore(context); |
| |
| return false; |
| } |
| |
| static void getComboBoxMetrics(RenderStyle* style, GtkBorder& border, int& focus, int& separator) |
| { |
| // If this menu list button isn't drawn using the native theme, we |
| // don't add any extra padding beyond what WebCore already uses. |
| if (style->appearance() == NoControlPart) |
| return; |
| |
| GtkStyleContext* context = getStyleContext(GTK_TYPE_COMBO_BOX); |
| gtk_style_context_save(context); |
| |
| gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON); |
| gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(style->direction()))); |
| |
| gtk_style_context_get_border(context, static_cast<GtkStateFlags>(0), &border); |
| |
| gboolean interiorFocus; |
| gint focusWidth, focusPad; |
| gtk_style_context_get_style(context, |
| "interior-focus", &interiorFocus, |
| "focus-line-width", &focusWidth, |
| "focus-padding", &focusPad, NULL); |
| focus = interiorFocus ? focusWidth + focusPad : 0; |
| |
| gtk_style_context_restore(context); |
| |
| context = getStyleContext(GTK_TYPE_SEPARATOR); |
| gtk_style_context_save(context); |
| |
| GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(style->direction())); |
| gtk_style_context_set_direction(context, direction); |
| gtk_style_context_add_class(context, "separator"); |
| |
| gboolean wideSeparators; |
| gint separatorWidth; |
| gtk_style_context_get_style(context, |
| "wide-separators", &wideSeparators, |
| "separator-width", &separatorWidth, |
| NULL); |
| |
| // GTK+ always uses border.left, regardless of text direction. See gtkseperator.c. |
| if (!wideSeparators) |
| separatorWidth = border.left; |
| |
| separator = separatorWidth; |
| |
| gtk_style_context_restore(context); |
| } |
| |
| int RenderThemeGtk::popupInternalPaddingLeft(RenderStyle* style) const |
| { |
| GtkBorder borderWidth = { 0, 0, 0, 0 }; |
| int focusWidth = 0, separatorWidth = 0; |
| getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth); |
| int left = borderWidth.left + focusWidth; |
| if (style->direction() == RTL) |
| left += separatorWidth + minArrowSize; |
| return left; |
| } |
| |
| int RenderThemeGtk::popupInternalPaddingRight(RenderStyle* style) const |
| { |
| GtkBorder borderWidth = { 0, 0, 0, 0 }; |
| int focusWidth = 0, separatorWidth = 0; |
| getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth); |
| int right = borderWidth.right + focusWidth; |
| if (style->direction() == LTR) |
| right += separatorWidth + minArrowSize; |
| return right; |
| } |
| |
| int RenderThemeGtk::popupInternalPaddingTop(RenderStyle* style) const |
| { |
| GtkBorder borderWidth = { 0, 0, 0, 0 }; |
| int focusWidth = 0, separatorWidth = 0; |
| getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth); |
| return borderWidth.top + focusWidth; |
| } |
| |
| int RenderThemeGtk::popupInternalPaddingBottom(RenderStyle* style) const |
| { |
| GtkBorder borderWidth = { 0, 0, 0, 0 }; |
| int focusWidth = 0, separatorWidth = 0; |
| getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth); |
| return borderWidth.bottom + focusWidth; |
| } |
| |
| bool RenderThemeGtk::paintMenuList(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
| { |
| cairo_t* cairoContext = paintInfo.context->platformContext()->cr(); |
| GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction())); |
| |
| // Paint the button. |
| GtkStyleContext* buttonStyleContext = getStyleContext(GTK_TYPE_BUTTON); |
| gtk_style_context_save(buttonStyleContext); |
| gtk_style_context_set_direction(buttonStyleContext, direction); |
| gtk_style_context_add_class(buttonStyleContext, GTK_STYLE_CLASS_BUTTON); |
| renderButton(this, buttonStyleContext, renderObject, paintInfo, rect); |
| |
| // Get the inner rectangle. |
| gint focusWidth, focusPad; |
| GtkBorder* innerBorderPtr = 0; |
| GtkBorder innerBorder = { 1, 1, 1, 1 }; |
| gtk_style_context_get_style(buttonStyleContext, |
| "inner-border", &innerBorderPtr, |
| "focus-line-width", &focusWidth, |
| "focus-padding", &focusPad, |
| NULL); |
| if (innerBorderPtr) { |
| innerBorder = *innerBorderPtr; |
| gtk_border_free(innerBorderPtr); |
| } |
| |
| GtkBorder borderWidth; |
| GtkStateFlags state = gtk_style_context_get_state(buttonStyleContext); |
| gtk_style_context_get_border(buttonStyleContext, state, &borderWidth); |
| |
| focusWidth += focusPad; |
| IntRect innerRect(rect.x() + innerBorder.left + borderWidth.left + focusWidth, |
| rect.y() + innerBorder.top + borderWidth.top + focusWidth, |
| rect.width() - borderWidth.left - borderWidth.right - innerBorder.left - innerBorder.right - (2 * focusWidth), |
| rect.height() - borderWidth.top - borderWidth.bottom - innerBorder.top - innerBorder.bottom - (2 * focusWidth)); |
| |
| if (isPressed(renderObject)) { |
| gint childDisplacementX; |
| gint childDisplacementY; |
| gtk_style_context_get_style(buttonStyleContext, |
| "child-displacement-x", &childDisplacementX, |
| "child-displacement-y", &childDisplacementY, |
| NULL); |
| innerRect.move(childDisplacementX, childDisplacementY); |
| } |
| innerRect.setWidth(max(1, innerRect.width())); |
| innerRect.setHeight(max(1, innerRect.height())); |
| |
| gtk_style_context_restore(buttonStyleContext); |
| |
| // Paint the arrow. |
| GtkStyleContext* arrowStyleContext = getStyleContext(GTK_TYPE_ARROW); |
| gtk_style_context_save(arrowStyleContext); |
| |
| gtk_style_context_set_direction(arrowStyleContext, direction); |
| gtk_style_context_add_class(arrowStyleContext, "arrow"); |
| gtk_style_context_add_class(arrowStyleContext, GTK_STYLE_CLASS_BUTTON); |
| |
| gfloat arrowScaling; |
| gtk_style_context_get_style(arrowStyleContext, "arrow-scaling", &arrowScaling, NULL); |
| |
| IntSize arrowSize(minArrowSize, innerRect.height()); |
| FloatPoint arrowPosition(innerRect.location()); |
| if (direction == GTK_TEXT_DIR_LTR) |
| arrowPosition.move(innerRect.width() - arrowSize.width(), 0); |
| |
| // GTK+ actually fetches the xalign and valign values from the widget, but since we |
| // don't have a widget here, we are just using the default xalign and valign values of 0.5. |
| gint extent = std::min(arrowSize.width(), arrowSize.height()) * arrowScaling; |
| arrowPosition.move((arrowSize.width() - extent) / 2, (arrowSize.height() - extent) / 2); |
| |
| gtk_style_context_set_state(arrowStyleContext, state); |
| gtk_render_arrow(arrowStyleContext, cairoContext, G_PI, arrowPosition.x(), arrowPosition.y(), extent); |
| |
| gtk_style_context_restore(arrowStyleContext); |
| |
| // Paint the separator if needed. |
| GtkStyleContext* separatorStyleContext = getStyleContext(GTK_TYPE_COMBO_BOX); |
| gtk_style_context_save(separatorStyleContext); |
| |
| gtk_style_context_set_direction(separatorStyleContext, direction); |
| gtk_style_context_add_class(separatorStyleContext, "separator"); |
| |
| gboolean wideSeparators; |
| gint separatorWidth; |
| gtk_style_context_get_style(separatorStyleContext, |
| "wide-separators", &wideSeparators, |
| "separator-width", &separatorWidth, |
| NULL); |
| if (wideSeparators && !separatorWidth) { |
| gtk_style_context_restore(separatorStyleContext); |
| return false; |
| } |
| |
| gtk_style_context_set_state(separatorStyleContext, state); |
| IntPoint separatorPosition(arrowPosition.x(), innerRect.y()); |
| if (wideSeparators) { |
| if (direction == GTK_TEXT_DIR_LTR) |
| separatorPosition.move(-separatorWidth, 0); |
| else |
| separatorPosition.move(arrowSize.width(), 0); |
| |
| gtk_render_frame(separatorStyleContext, cairoContext, |
| separatorPosition.x(), separatorPosition.y(), |
| separatorWidth, innerRect.height()); |
| } else { |
| GtkBorder padding; |
| gtk_style_context_get_padding(separatorStyleContext, state, &padding); |
| GtkBorder border; |
| gtk_style_context_get_border(separatorStyleContext, state, &border); |
| |
| if (direction == GTK_TEXT_DIR_LTR) |
| separatorPosition.move(-(padding.left + border.left), 0); |
| else |
| separatorPosition.move(arrowSize.width(), 0); |
| |
| cairo_save(cairoContext); |
| |
| // An extra clip prevents the separator bleeding outside of the specified rectangle because of subpixel positioning. |
| cairo_rectangle(cairoContext, separatorPosition.x(), separatorPosition.y(), border.left, innerRect.height()); |
| cairo_clip(cairoContext); |
| gtk_render_line(separatorStyleContext, cairoContext, |
| separatorPosition.x(), separatorPosition.y(), |
| separatorPosition.x(), innerRect.maxY()); |
| cairo_restore(cairoContext); |
| } |
| |
| gtk_style_context_restore(separatorStyleContext); |
| return false; |
| } |
| |
| bool RenderThemeGtk::paintTextField(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
| { |
| GtkStyleContext* context = getStyleContext(GTK_TYPE_ENTRY); |
| gtk_style_context_save(context); |
| |
| gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction()))); |
| gtk_style_context_add_class(context, GTK_STYLE_CLASS_ENTRY); |
| |
| guint flags = 0; |
| if (!isEnabled(renderObject) || isReadOnlyControl(renderObject)) |
| flags |= GTK_STATE_FLAG_INSENSITIVE; |
| else if (isFocused(renderObject)) |
| flags |= GTK_STATE_FLAG_FOCUSED; |
| gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); |
| |
| gtk_render_background(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); |
| gtk_render_frame(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); |
| |
| if (isFocused(renderObject) && isEnabled(renderObject)) { |
| gboolean interiorFocus; |
| gint focusWidth, focusPad; |
| gtk_style_context_get_style(context, |
| "interior-focus", &interiorFocus, |
| "focus-line-width", &focusWidth, |
| "focus-padding", &focusPad, |
| NULL); |
| if (!interiorFocus) { |
| IntRect focusRect(rect); |
| focusRect.inflate(focusWidth + focusPad); |
| gtk_render_focus(context, paintInfo.context->platformContext()->cr(), |
| focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height()); |
| } |
| } |
| |
| gtk_style_context_restore(context); |
| |
| return false; |
| } |
| |
| static void applySliderStyleContextClasses(GtkStyleContext* context, ControlPart part) |
| { |
| gtk_style_context_add_class(context, GTK_STYLE_CLASS_SCALE); |
| if (part == SliderHorizontalPart || part == SliderThumbHorizontalPart) |
| gtk_style_context_add_class(context, GTK_STYLE_CLASS_HORIZONTAL); |
| else if (part == SliderVerticalPart || part == SliderThumbVerticalPart) |
| gtk_style_context_add_class(context, GTK_STYLE_CLASS_VERTICAL); |
| } |
| |
| bool RenderThemeGtk::paintSliderTrack(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
| { |
| ControlPart part = renderObject->style()->appearance(); |
| ASSERT_UNUSED(part, part == SliderHorizontalPart || part == SliderVerticalPart || part == MediaVolumeSliderPart); |
| |
| GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE); |
| gtk_style_context_save(context); |
| |
| gtk_style_context_set_direction(context, gtkTextDirection(renderObject->style()->direction())); |
| applySliderStyleContextClasses(context, part); |
| gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH); |
| |
| if (!isEnabled(renderObject) || isReadOnlyControl(renderObject)) |
| gtk_style_context_set_state(context, GTK_STATE_FLAG_INSENSITIVE); |
| |
| gtk_render_background(context, paintInfo.context->platformContext()->cr(), |
| rect.x(), rect.y(), rect.width(), rect.height()); |
| gtk_render_frame(context, paintInfo.context->platformContext()->cr(), |
| rect.x(), rect.y(), rect.width(), rect.height()); |
| |
| if (isFocused(renderObject)) { |
| gint focusWidth, focusPad; |
| gtk_style_context_get_style(context, |
| "focus-line-width", &focusWidth, |
| "focus-padding", &focusPad, NULL); |
| IntRect focusRect(rect); |
| focusRect.inflate(focusWidth + focusPad); |
| gtk_render_focus(context, paintInfo.context->platformContext()->cr(), |
| focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height()); |
| } |
| |
| gtk_style_context_restore(context); |
| return false; |
| } |
| |
| bool RenderThemeGtk::paintSliderThumb(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
| { |
| ControlPart part = renderObject->style()->appearance(); |
| ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart); |
| |
| GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE); |
| gtk_style_context_save(context); |
| |
| gtk_style_context_set_direction(context, gtkTextDirection(renderObject->style()->direction())); |
| applySliderStyleContextClasses(context, part); |
| gtk_style_context_add_class(context, GTK_STYLE_CLASS_SLIDER); |
| |
| guint flags = 0; |
| if (!isEnabled(renderObject) || isReadOnlyControl(renderObject)) |
| flags |= GTK_STATE_FLAG_INSENSITIVE; |
| else if (isHovered(renderObject)) |
| flags |= GTK_STATE_FLAG_PRELIGHT; |
| if (isPressed(renderObject)) |
| flags |= GTK_STATE_FLAG_ACTIVE; |
| gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); |
| |
| gtk_render_slider(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height(), |
| part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); |
| |
| gtk_style_context_restore(context); |
| |
| return false; |
| } |
| |
| void RenderThemeGtk::adjustSliderThumbSize(RenderStyle* style) const |
| { |
| ControlPart part = style->appearance(); |
| #if ENABLE(VIDEO) |
| if (part == MediaSliderThumbPart) { |
| adjustMediaSliderThumbSize(style); |
| return; |
| } |
| #endif |
| |
| gint sliderWidth, sliderLength; |
| gtk_style_context_get_style(getStyleContext(GTK_TYPE_SCALE), |
| "slider-width", &sliderWidth, |
| "slider-length", &sliderLength, |
| NULL); |
| if (part == SliderThumbHorizontalPart) { |
| style->setWidth(Length(sliderLength, Fixed)); |
| style->setHeight(Length(sliderWidth, Fixed)); |
| return; |
| } |
| ASSERT(part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart); |
| style->setWidth(Length(sliderWidth, Fixed)); |
| style->setHeight(Length(sliderLength, Fixed)); |
| } |
| |
| #if ENABLE(PROGRESS_TAG) |
| bool RenderThemeGtk::paintProgressBar(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
| { |
| if (!renderObject->isProgress()) |
| return true; |
| |
| GtkStyleContext* context = getStyleContext(GTK_TYPE_PROGRESS_BAR); |
| gtk_style_context_save(context); |
| |
| gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH); |
| |
| gtk_render_background(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); |
| gtk_render_frame(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); |
| |
| gtk_style_context_restore(context); |
| |
| gtk_style_context_save(context); |
| gtk_style_context_add_class(context, GTK_STYLE_CLASS_PROGRESSBAR); |
| |
| |
| GtkBorder padding; |
| gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding); |
| IntRect progressRect(rect.x() + padding.left, rect.y() + padding.top, |
| rect.width() - (padding.left + padding.right), |
| rect.height() - (padding.top + padding.bottom)); |
| progressRect = RenderThemeGtk::calculateProgressRect(renderObject, progressRect); |
| |
| if (!progressRect.isEmpty()) |
| gtk_render_activity(context, paintInfo.context->platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height()); |
| |
| gtk_style_context_restore(context); |
| return false; |
| } |
| #endif |
| |
| static gint spinButtonArrowSize(GtkStyleContext* context) |
| { |
| const PangoFontDescription* fontDescription = gtk_style_context_get_font(context, static_cast<GtkStateFlags>(0)); |
| gint fontSize = pango_font_description_get_size(fontDescription); |
| gint arrowSize = max(PANGO_PIXELS(fontSize), minSpinButtonArrowSize); |
| |
| return arrowSize - arrowSize % 2; // Force even. |
| } |
| |
| void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver*, RenderStyle* style, Element*) const |
| { |
| GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON); |
| |
| GtkBorder padding; |
| gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding); |
| |
| int width = spinButtonArrowSize(context) + padding.left + padding.right; |
| style->setWidth(Length(width, Fixed)); |
| style->setMinWidth(Length(width, Fixed)); |
| } |
| |
| static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* context, RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType) |
| { |
| ASSERT(arrowType == GTK_ARROW_UP || arrowType == GTK_ARROW_DOWN); |
| |
| gtk_style_context_save(context); |
| gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON); |
| |
| GtkTextDirection direction = gtk_style_context_get_direction(context); |
| guint state = static_cast<guint>(gtk_style_context_get_state(context)); |
| if (!(state & GTK_STATE_FLAG_INSENSITIVE)) { |
| if (theme->isPressed(renderObject)) { |
| if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartPressed(renderObject)) |
| || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartPressed(renderObject))) |
| state |= GTK_STATE_FLAG_ACTIVE; |
| } else if (theme->isHovered(renderObject)) { |
| if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartHovered(renderObject)) |
| || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartHovered(renderObject))) |
| state |= GTK_STATE_FLAG_PRELIGHT; |
| } |
| } |
| gtk_style_context_set_state(context, static_cast<GtkStateFlags>(state)); |
| |
| // Paint button. |
| IntRect buttonRect(rect); |
| guint junction = gtk_style_context_get_junction_sides(context); |
| if (arrowType == GTK_ARROW_UP) |
| junction |= GTK_JUNCTION_BOTTOM; |
| else { |
| junction |= GTK_JUNCTION_TOP; |
| buttonRect.move(0, rect.height() / 2); |
| } |
| buttonRect.setHeight(rect.height() / 2); |
| gtk_style_context_set_junction_sides(context, static_cast<GtkJunctionSides>(junction)); |
| |
| gtk_render_background(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); |
| gtk_render_frame(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); |
| |
| // Paint arrow centered inside button. |
| // This code is based on gtkspinbutton.c code. |
| IntRect arrowRect; |
| gdouble angle; |
| if (arrowType == GTK_ARROW_UP) { |
| angle = 0; |
| arrowRect.setY(rect.y()); |
| arrowRect.setHeight(rect.height() / 2 - 2); |
| } else { |
| angle = G_PI; |
| arrowRect.setY(rect.y() + buttonRect.y()); |
| arrowRect.setHeight(rect.height() - arrowRect.y() - 2); |
| } |
| arrowRect.setWidth(rect.width() - 3); |
| if (direction == GTK_TEXT_DIR_LTR) |
| arrowRect.setX(rect.x() + 1); |
| else |
| arrowRect.setX(rect.x() + 2); |
| |
| gint width = arrowRect.width() / 2; |
| width -= width % 2 - 1; // Force odd. |
| gint height = (width + 1) / 2; |
| |
| arrowRect.move((arrowRect.width() - width) / 2, (arrowRect.height() - height) / 2); |
| gtk_render_arrow(context, paintInfo.context->platformContext()->cr(), angle, arrowRect.x(), arrowRect.y(), width); |
| |
| gtk_style_context_restore(context); |
| } |
| |
| bool RenderThemeGtk::paintInnerSpinButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
| { |
| GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON); |
| gtk_style_context_save(context); |
| |
| GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction())); |
| gtk_style_context_set_direction(context, direction); |
| |
| guint flags = 0; |
| if (!isEnabled(renderObject) || isReadOnlyControl(renderObject)) |
| flags |= GTK_STATE_FLAG_INSENSITIVE; |
| else if (isFocused(renderObject)) |
| flags |= GTK_STATE_FLAG_FOCUSED; |
| gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); |
| gtk_style_context_remove_class(context, GTK_STYLE_CLASS_ENTRY); |
| |
| paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_UP); |
| paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_DOWN); |
| |
| gtk_style_context_restore(context); |
| |
| return false; |
| } |
| |
| GRefPtr<GdkPixbuf> getStockIconForWidgetType(GType widgetType, const char* iconName, gint direction, gint state, gint iconSize) |
| { |
| GtkStyleContext* context = getStyleContext(widgetType); |
| GtkIconSet* iconSet = gtk_style_context_lookup_icon_set(context, iconName); |
| |
| gtk_style_context_save(context); |
| |
| guint flags = 0; |
| if (state == GTK_STATE_PRELIGHT) |
| flags |= GTK_STATE_FLAG_PRELIGHT; |
| else if (state == GTK_STATE_INSENSITIVE) |
| flags |= GTK_STATE_FLAG_INSENSITIVE; |
| |
| gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); |
| gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(direction)); |
| GdkPixbuf* icon = gtk_icon_set_render_icon_pixbuf(iconSet, context, static_cast<GtkIconSize>(iconSize)); |
| |
| gtk_style_context_restore(context); |
| |
| return adoptGRef(icon); |
| } |
| |
| Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const |
| { |
| GdkRGBA gdkRGBAColor; |
| gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor); |
| return gdkRGBAColor; |
| } |
| |
| Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const |
| { |
| GdkRGBA gdkRGBAColor; |
| gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor); |
| return gdkRGBAColor; |
| } |
| |
| Color RenderThemeGtk::platformActiveSelectionForegroundColor() const |
| { |
| GdkRGBA gdkRGBAColor; |
| gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor); |
| return gdkRGBAColor; |
| } |
| |
| Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const |
| { |
| GdkRGBA gdkRGBAColor; |
| gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor); |
| return gdkRGBAColor; |
| } |
| |
| Color RenderThemeGtk::activeListBoxSelectionBackgroundColor() const |
| { |
| GdkRGBA gdkRGBAColor; |
| gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor); |
| return gdkRGBAColor; |
| } |
| |
| Color RenderThemeGtk::inactiveListBoxSelectionBackgroundColor() const |
| { |
| GdkRGBA gdkRGBAColor; |
| gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor); |
| return gdkRGBAColor; |
| } |
| |
| Color RenderThemeGtk::activeListBoxSelectionForegroundColor() const |
| { |
| GdkRGBA gdkRGBAColor; |
| gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor); |
| return gdkRGBAColor; |
| } |
| |
| Color RenderThemeGtk::inactiveListBoxSelectionForegroundColor() const |
| { |
| GdkRGBA gdkRGBAColor; |
| gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor); |
| return gdkRGBAColor; |
| } |
| |
| Color RenderThemeGtk::systemColor(int cssValueId) const |
| { |
| GdkRGBA gdkRGBAColor; |
| |
| switch (cssValueId) { |
| case CSSValueButtontext: |
| gtk_style_context_get_color(getStyleContext(GTK_TYPE_BUTTON), static_cast<GtkStateFlags>(0), &gdkRGBAColor); |
| return gdkRGBAColor; |
| case CSSValueCaptiontext: |
| gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), static_cast<GtkStateFlags>(0), &gdkRGBAColor); |
| return gdkRGBAColor; |
| default: |
| return RenderTheme::systemColor(cssValueId); |
| } |
| } |
| |
| } // namespace WebCore |
| |
| #endif // !GTK_API_VERSION_2 |