summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/gtk/RenderThemeGtk2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/gtk/RenderThemeGtk2.cpp')
-rw-r--r--Source/WebCore/platform/gtk/RenderThemeGtk2.cpp477
1 files changed, 393 insertions, 84 deletions
diff --git a/Source/WebCore/platform/gtk/RenderThemeGtk2.cpp b/Source/WebCore/platform/gtk/RenderThemeGtk2.cpp
index e01508e..de4195d 100644
--- a/Source/WebCore/platform/gtk/RenderThemeGtk2.cpp
+++ b/Source/WebCore/platform/gtk/RenderThemeGtk2.cpp
@@ -27,6 +27,10 @@
#ifdef GTK_API_VERSION_2
+// We need this to allow building while using GTK_WIDGET_SET_FLAGS. It's deprecated
+// but some theme engines require it to ensure proper rendering of focus indicators.
+#undef GTK_DISABLE_DEPRECATED
+
#include "CSSValueKeywords.h"
#include "GraphicsContext.h"
#include "GtkVersioning.h"
@@ -41,10 +45,6 @@
#include <gdk/gdk.h>
#include <gtk/gtk.h>
-#if ENABLE(PROGRESS_TAG)
-#include "RenderProgress.h"
-#endif
-
namespace WebCore {
// This is not a static method, because we want to avoid having GTK+ headers in RenderThemeGtk.h.
@@ -61,6 +61,13 @@ void RenderThemeGtk::platformInit()
m_gtkTreeView = 0;
m_gtkVScale = 0;
m_gtkHScale = 0;
+ m_gtkRadioButton = 0;
+ m_gtkCheckButton = 0;
+ m_gtkProgressBar = 0;
+ m_gtkComboBox = 0;
+ m_gtkComboBoxButton = 0;
+ m_gtkComboBoxArrow = 0;
+ m_gtkComboBoxSeparator = 0;
memset(&m_themeParts, 0, sizeof(GtkThemeParts));
GdkColormap* colormap = gdk_screen_get_rgba_colormap(gdk_screen_get_default());
@@ -99,8 +106,34 @@ void RenderThemeGtk::initMediaColors()
}
#endif
-void RenderThemeGtk::adjustRepaintRect(const RenderObject*, IntRect&)
+static void adjustRectForFocus(GtkWidget* widget, IntRect& rect, bool ignoreInteriorFocusProperty = false)
+{
+ gint focusWidth, focusPad;
+ gboolean interiorFocus = 0;
+ gtk_widget_style_get(widget,
+ "interior-focus", &interiorFocus,
+ "focus-line-width", &focusWidth,
+ "focus-padding", &focusPad, NULL);
+ if (!ignoreInteriorFocusProperty && interiorFocus)
+ return;
+ rect.inflate(focusWidth + focusPad);
+}
+
+void RenderThemeGtk::adjustRepaintRect(const RenderObject* renderObject, IntRect& rect)
{
+ ControlPart part = renderObject->style()->appearance();
+ switch (part) {
+ case CheckboxPart:
+ case RadioPart: {
+ // We ignore the interior focus property and always expand the focus rect. In GTK+, the
+ // focus indicator is usually on the text next to a checkbox or radio button, but that doesn't
+ // happen in WebCore. By expanding the focus rectangle unconditionally we increase its prominence.
+ adjustRectForFocus(part == CheckboxPart ? gtkCheckButton() : gtkRadioButton(), rect, true);
+ return;
+ }
+ default:
+ return;
+ }
}
static GtkStateType getGtkStateType(RenderThemeGtk* theme, RenderObject* object)
@@ -142,56 +175,89 @@ bool RenderThemeGtk::paintRenderObject(GtkThemeWidgetType type, RenderObject* re
gtkTextDirection(renderObject->style()->direction()));
}
-void RenderThemeGtk::getIndicatorMetrics(ControlPart part, int& indicatorSize, int& indicatorSpacing)
+static void setToggleSize(const RenderThemeGtk* theme, RenderStyle* style, GtkWidget* widget)
{
- ASSERT(part == CheckboxPart || part == RadioPart);
- if (part == CheckboxPart) {
- moz_gtk_checkbox_get_metrics(&indicatorSize, &indicatorSpacing);
+ // The width and height are both specified, so we shouldn't change them.
+ if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
return;
- }
- // RadioPart
- moz_gtk_radio_get_metrics(&indicatorSize, &indicatorSpacing);
+ gint indicatorSize;
+ gtk_widget_style_get(widget, "indicator-size", &indicatorSize, NULL);
+ if (style->width().isIntrinsicOrAuto())
+ style->setWidth(Length(indicatorSize, Fixed));
+ if (style->height().isAuto())
+ style->setHeight(Length(indicatorSize, Fixed));
}
-static void setToggleSize(const RenderThemeGtk* theme, RenderStyle* style, ControlPart appearance)
+static void paintToggle(RenderThemeGtk* theme, RenderObject* renderObject, const PaintInfo& info, const IntRect& rect, GtkWidget* widget)
{
- // The width and height are both specified, so we shouldn't change them.
- if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
- return;
+ // We do not call gtk_toggle_button_set_active here, because some themes begin a series of
+ // animation frames in a "toggled" signal handler. This puts some checkboxes in a half-way
+ // checked state. Every GTK+ theme I tested merely looks at the shadow type (and not the
+ // 'active' property) to determine whether or not to draw the check.
+ gtk_widget_set_sensitive(widget, theme->isEnabled(renderObject) && !theme->isReadOnlyControl(renderObject));
+ gtk_widget_set_direction(widget, gtkTextDirection(renderObject->style()->direction()));
- // FIXME: This is probably not correct use of indicatorSize and indicatorSpacing.
- gint indicatorSize, indicatorSpacing;
- RenderThemeGtk::getIndicatorMetrics(appearance, indicatorSize, indicatorSpacing);
+ bool indeterminate = theme->isIndeterminate(renderObject);
+ gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(widget), indeterminate);
- // Other ports hard-code this to 13, but GTK+ users tend to demand the native look.
- // It could be made a configuration option values other than 13 actually break site compatibility.
- int length = indicatorSize + indicatorSpacing;
- if (style->width().isIntrinsicOrAuto())
- style->setWidth(Length(length, Fixed));
+ GtkShadowType shadowType = GTK_SHADOW_OUT;
+ if (indeterminate) // This originates from the Mozilla code.
+ shadowType = GTK_SHADOW_ETCHED_IN;
+ else if (theme->isChecked(renderObject))
+ shadowType = GTK_SHADOW_IN;
- if (style->height().isAuto())
- style->setHeight(Length(length, Fixed));
+ WidgetRenderingContext widgetContext(info.context, rect);
+ IntRect buttonRect(IntPoint(), rect.size());
+ GtkStateType toggleState = getGtkStateType(theme, renderObject);
+ const char* detail = 0;
+ if (GTK_IS_RADIO_BUTTON(widget)) {
+ detail = "radiobutton";
+ widgetContext.gtkPaintOption(buttonRect, widget, toggleState, shadowType, detail);
+ } else {
+ detail = "checkbutton";
+ widgetContext.gtkPaintCheck(buttonRect, widget, toggleState, shadowType, detail);
+ }
+
+ if (theme->isFocused(renderObject)) {
+ IntRect focusRect(buttonRect);
+ adjustRectForFocus(widget, focusRect, true);
+ widgetContext.gtkPaintFocus(focusRect, widget, toggleState, detail);
+ }
}
void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const
{
- setToggleSize(this, style, RadioPart);
+ setToggleSize(this, style, gtkCheckButton());
}
-bool RenderThemeGtk::paintCheckbox(RenderObject* object, const PaintInfo& info, const IntRect& rect)
+bool RenderThemeGtk::paintCheckbox(RenderObject* renderObject, const PaintInfo& info, const IntRect& rect)
{
- return paintRenderObject(MOZ_GTK_CHECKBUTTON, object, info.context, rect, isChecked(object));
+ paintToggle(this, renderObject, info, rect, gtkCheckButton());
+ return false;
}
void RenderThemeGtk::setRadioSize(RenderStyle* style) const
{
- setToggleSize(this, style, RadioPart);
+ setToggleSize(this, style, gtkRadioButton());
+}
+
+bool RenderThemeGtk::paintRadio(RenderObject* renderObject, const PaintInfo& info, const IntRect& rect)
+{
+ paintToggle(this, renderObject, info, rect, gtkRadioButton());
+ return false;
}
-bool RenderThemeGtk::paintRadio(RenderObject* object, const PaintInfo& info, const IntRect& rect)
+static void setWidgetHasFocus(GtkWidget* widget, gboolean hasFocus)
{
- return paintRenderObject(MOZ_GTK_RADIOBUTTON, object, info.context, rect, isChecked(object));
+ g_object_set(widget, "has-focus", hasFocus, NULL);
+
+ // These functions are deprecated in GTK+ 2.22, yet theme engines still look
+ // at these flags when determining if a widget has focus, so we must use them.
+ if (hasFocus)
+ GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
+ else
+ GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
}
bool RenderThemeGtk::paintButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
@@ -208,12 +274,7 @@ bool RenderThemeGtk::paintButton(RenderObject* object, const PaintInfo& info, co
gtk_widget_set_direction(widget, gtkTextDirection(object->style()->direction()));
if (isFocused(object)) {
- if (isEnabled(object)) {
-#if !GTK_CHECK_VERSION(2, 22, 0)
- GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
-#endif
- g_object_set(widget, "has-focus", TRUE, NULL);
- }
+ setWidgetHasFocus(widget, TRUE);
gboolean interiorFocus = 0, focusWidth = 0, focusPadding = 0;
gtk_widget_style_get(widget,
@@ -239,21 +300,74 @@ bool RenderThemeGtk::paintButton(RenderObject* object, const PaintInfo& info, co
if (isFocused(object))
widgetContext.gtkPaintFocus(focusRect, widget, state, "button");
-#if !GTK_CHECK_VERSION(2, 22, 0)
- GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
-#endif
- g_object_set(widget, "has-focus", FALSE, NULL);
+ setWidgetHasFocus(widget, FALSE);
return false;
}
-static void getComboBoxPadding(RenderStyle* style, int& left, int& top, int& right, int& bottom)
+int RenderThemeGtk::getComboBoxSeparatorWidth() const
+{
+ GtkWidget* separator = gtkComboBoxSeparator();
+ if (!separator)
+ return 0;
+
+ gboolean hasWideSeparators = FALSE;
+ gint separatorWidth = 0;
+ gtk_widget_style_get(separator,
+ "wide-separators", &hasWideSeparators,
+ "separator-width", &separatorWidth,
+ NULL);
+ if (hasWideSeparators)
+ return separatorWidth;
+ return gtk_widget_get_style(separator)->xthickness;
+}
+
+int RenderThemeGtk::comboBoxArrowSize(RenderStyle* style) const
+{
+ // Taking the font size and reversing the DPI conversion seems to match
+ // GTK+ rendering as closely as possible.
+ return style->font().size() * (72.0 / RenderThemeGtk::getScreenDPI());
+}
+
+static void getButtonInnerBorder(GtkWidget* button, int& left, int& top, int& right, int& bottom)
+{
+ GtkStyle* style = gtk_widget_get_style(button);
+ int outerBorder = gtk_container_get_border_width(GTK_CONTAINER(button));
+ static GtkBorder defaultInnerBorder = {1, 1, 1, 1};
+ GtkBorder* innerBorder;
+ gtk_widget_style_get(button, "inner-border", &innerBorder, NULL);
+ if (!innerBorder)
+ innerBorder = &defaultInnerBorder;
+
+ left = outerBorder + innerBorder->left + style->xthickness;
+ right = outerBorder + innerBorder->right + style->xthickness;
+ top = outerBorder + innerBorder->top + style->ythickness;
+ bottom = outerBorder + innerBorder->bottom + style->ythickness;
+
+ if (innerBorder != &defaultInnerBorder)
+ gtk_border_free(innerBorder);
+}
+
+
+void RenderThemeGtk::getComboBoxPadding(RenderStyle* style, int& left, int& top, int& right, int& bottom) const
{
// 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;
- moz_gtk_get_widget_border(MOZ_GTK_DROPDOWN, &left, &top, &right, &bottom,
- gtkTextDirection(style->direction()), TRUE);
+
+ // A combo box button is a button with widgets packed into it.
+ GtkStyle* buttonWidgetStyle = gtk_widget_get_style(gtkComboBoxButton());
+ getButtonInnerBorder(gtkComboBoxButton(), left, top, right, bottom);
+
+ // Add xthickness amount of padding for each side of the separator. This ensures
+ // that the text does not bump up against the separator.
+ int arrowAndSeperatorLength = comboBoxArrowSize(style) +
+ getComboBoxSeparatorWidth() + (3 * buttonWidgetStyle->xthickness);
+
+ if (style->direction() == RTL)
+ left += arrowAndSeperatorLength;
+ else
+ right += arrowAndSeperatorLength;
}
int RenderThemeGtk::popupInternalPaddingLeft(RenderStyle* style) const
@@ -286,12 +400,102 @@ int RenderThemeGtk::popupInternalPaddingBottom(RenderStyle* style) const
bool RenderThemeGtk::paintMenuList(RenderObject* object, const PaintInfo& info, const IntRect& rect)
{
- return paintRenderObject(MOZ_GTK_DROPDOWN, object, info.context, rect);
+ if (paintButton(object, info, rect))
+ return true;
+
+ // Menu list button painting strategy.
+ // For buttons with appears-as-list set to false (having a separator):
+ // | left border | Button text | xthickness | vseparator | xthickness | arrow | xthickness | right border |
+ // For buttons with appears-as-list set to true (not having a separator):
+ // | left border | Button text | arrow | xthickness | right border |
+
+ int leftBorder = 0, rightBorder = 0, bottomBorder = 0, topBorder = 0;
+ getButtonInnerBorder(gtkComboBoxButton(), leftBorder, topBorder, rightBorder, bottomBorder);
+ RenderStyle* style = object->style();
+ int arrowSize = comboBoxArrowSize(style);
+ GtkStyle* buttonStyle = gtk_widget_get_style(gtkComboBoxButton());
+
+ IntRect arrowRect(0, (rect.height() - arrowSize) / 2, arrowSize, arrowSize);
+ if (style->direction() == RTL)
+ arrowRect.setX(leftBorder + buttonStyle->xthickness);
+ else
+ arrowRect.setX(rect.width() - rightBorder - buttonStyle->xthickness - arrowSize);
+ GtkShadowType shadowType = isPressed(object) ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
+
+ WidgetRenderingContext widgetContext(info.context, rect);
+ GtkStateType stateType = getGtkStateType(this, object);
+ widgetContext.gtkPaintArrow(arrowRect, gtkComboBoxArrow(), stateType, shadowType, GTK_ARROW_DOWN, "arrow");
+
+ // Some combo boxes do not have a separator.
+ GtkWidget* separator = gtkComboBoxSeparator();
+ if (!separator)
+ return false;
+
+ // We want to decrease the height of the separator based on the focus padding of the button.
+ gint focusPadding = 0, focusWidth = 0;
+ gtk_widget_style_get(gtkComboBoxButton(),
+ "focus-line-width", &focusWidth,
+ "focus-padding", &focusPadding, NULL);
+ topBorder += focusPadding + focusWidth;
+ bottomBorder += focusPadding + focusWidth;
+ int separatorWidth = getComboBoxSeparatorWidth();
+ IntRect separatorRect(0, topBorder, separatorWidth, rect.height() - topBorder - bottomBorder);
+ if (style->direction() == RTL)
+ separatorRect.setX(arrowRect.x() + arrowRect.width() + buttonStyle->xthickness + separatorWidth);
+ else
+ separatorRect.setX(arrowRect.x() - buttonStyle->xthickness - separatorWidth);
+
+ gboolean hasWideSeparators = FALSE;
+ gtk_widget_style_get(separator, "wide-separators", &hasWideSeparators, NULL);
+ if (hasWideSeparators)
+ widgetContext.gtkPaintBox(separatorRect, separator, GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT, "vseparator");
+ else
+ widgetContext.gtkPaintVLine(separatorRect, separator, GTK_STATE_NORMAL, "vseparator");
+
+ return false;
}
-bool RenderThemeGtk::paintTextField(RenderObject* object, const PaintInfo& info, const IntRect& rect)
+bool RenderThemeGtk::paintTextField(RenderObject* renderObject, const PaintInfo& info, const IntRect& rect)
{
- return paintRenderObject(MOZ_GTK_ENTRY, object, info.context, rect);
+ GtkWidget* widget = gtkEntry();
+
+ bool enabled = isEnabled(renderObject) && !isReadOnlyControl(renderObject);
+ GtkStateType backgroundState = enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE;
+ gtk_widget_set_sensitive(widget, enabled);
+ gtk_widget_set_direction(widget, gtkTextDirection(renderObject->style()->direction()));
+ setWidgetHasFocus(widget, isFocused(renderObject));
+
+ WidgetRenderingContext widgetContext(info.context, rect);
+ IntRect textFieldRect(IntPoint(), rect.size());
+
+ // The entry background is only painted over the interior part of the GTK+ entry, not
+ // the entire frame. This happens in the Mozilla theme drawing code as well.
+ IntRect interiorRect(textFieldRect);
+ GtkStyle* style = gtk_widget_get_style(widget);
+ interiorRect.inflateX(-style->xthickness);
+ interiorRect.inflateY(-style->ythickness);
+ widgetContext.gtkPaintFlatBox(interiorRect, widget, backgroundState, GTK_SHADOW_NONE, "entry_bg");
+
+ // This is responsible for drawing the actual frame.
+ widgetContext.gtkPaintShadow(textFieldRect, widget, GTK_STATE_NORMAL, GTK_SHADOW_IN, "entry");
+
+ gboolean interiorFocus;
+ gint focusWidth;
+ gtk_widget_style_get(widget,
+ "interior-focus", &interiorFocus,
+ "focus-line-width", &focusWidth, NULL);
+ if (isFocused(renderObject) && !interiorFocus) {
+ // When GTK+ paints a text entry with focus, it shrinks the size of the frame area by the
+ // focus width and paints over the previously unfocused text entry. We need to emulate that
+ // by drawing both the unfocused frame above and the focused frame here.
+ IntRect shadowRect(textFieldRect);
+ shadowRect.inflate(-focusWidth);
+ widgetContext.gtkPaintShadow(shadowRect, widget, GTK_STATE_NORMAL, GTK_SHADOW_IN, "entry");
+
+ widgetContext.gtkPaintFocus(textFieldRect, widget, GTK_STATE_NORMAL, "entry");
+ }
+
+ return false;
}
bool RenderThemeGtk::paintSliderTrack(RenderObject* object, const PaintInfo& info, const IntRect& rect)
@@ -382,45 +586,25 @@ void RenderThemeGtk::adjustSliderThumbSize(RenderObject* o) const
}
#if ENABLE(PROGRESS_TAG)
-double RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress*) const
-{
- // FIXME: It doesn't look like there is a good way yet to support animated
- // progress bars with the Mozilla theme drawing code.
- return 0;
-}
-
-double RenderThemeGtk::animationDurationForProgressBar(RenderProgress*) const
-{
- // FIXME: It doesn't look like there is a good way yet to support animated
- // progress bars with the Mozilla theme drawing code.
- return 0;
-}
-
bool RenderThemeGtk::paintProgressBar(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
{
- if (!renderObject->isProgress())
- return true;
+ GtkWidget* widget = gtkProgressBar();
+ gtk_widget_set_direction(widget, gtkTextDirection(renderObject->style()->direction()));
- GtkWidget* progressBarWidget = moz_gtk_get_progress_widget();
- if (!progressBarWidget)
- return true;
+ WidgetRenderingContext widgetContext(paintInfo.context, rect);
+ IntRect fullProgressBarRect(IntPoint(), rect.size());
+ widgetContext.gtkPaintBox(fullProgressBarRect, widget, GTK_STATE_NORMAL, GTK_SHADOW_IN, "trough");
- if (paintRenderObject(MOZ_GTK_PROGRESSBAR, renderObject, paintInfo.context, rect))
- return true;
+ GtkStyle* style = gtk_widget_get_style(widget);
+ IntRect progressRect(fullProgressBarRect);
+ progressRect.inflateX(-style->xthickness);
+ progressRect.inflateY(-style->ythickness);
+ progressRect = RenderThemeGtk::calculateProgressRect(renderObject, progressRect);
- IntRect chunkRect(rect);
- RenderProgress* renderProgress = toRenderProgress(renderObject);
+ if (!progressRect.isEmpty())
+ widgetContext.gtkPaintBox(progressRect, widget, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, "bar");
- GtkStyle* style = gtk_widget_get_style(progressBarWidget);
- chunkRect.setHeight(chunkRect.height() - (2 * style->ythickness));
- chunkRect.setY(chunkRect.y() + style->ythickness);
- chunkRect.setWidth((chunkRect.width() - (2 * style->xthickness)) * renderProgress->position());
- if (renderObject->style()->direction() == RTL)
- chunkRect.setX(rect.x() + rect.width() - chunkRect.width() - style->xthickness);
- else
- chunkRect.setX(chunkRect.x() + style->xthickness);
-
- return paintRenderObject(MOZ_GTK_PROGRESS_CHUNK, renderObject, paintInfo.context, chunkRect);
+ return false;
}
#endif
@@ -503,11 +687,16 @@ static void gtkStyleSetCallback(GtkWidget* widget, GtkStyle* previous, RenderThe
renderTheme->platformColorsDidChange();
}
-void RenderThemeGtk::setupWidgetAndAddToContainer(GtkWidget* widget, GtkWidget* window) const
+static void setupWidget(GtkWidget* widget)
{
- gtk_container_add(GTK_CONTAINER(window), widget);
gtk_widget_realize(widget);
g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
+}
+
+void RenderThemeGtk::setupWidgetAndAddToContainer(GtkWidget* widget, GtkWidget* window) const
+{
+ gtk_container_add(GTK_CONTAINER(window), widget);
+ setupWidget(widget);
// FIXME: Perhaps this should only be called for the containing window or parent container.
g_signal_connect(widget, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this));
@@ -520,7 +709,7 @@ GtkWidget* RenderThemeGtk::gtkContainer() const
m_gtkWindow = gtk_window_new(GTK_WINDOW_POPUP);
gtk_widget_set_colormap(m_gtkWindow, m_themeParts.colormap);
- gtk_widget_realize(m_gtkWindow);
+ setupWidget(m_gtkWindow);
gtk_widget_set_name(m_gtkWindow, "MozillaGtkWidget");
m_gtkContainer = gtk_fixed_new();
@@ -573,6 +762,126 @@ GtkWidget* RenderThemeGtk::gtkHScale() const
return m_gtkHScale;
}
+GtkWidget* RenderThemeGtk::gtkRadioButton() const
+{
+ if (m_gtkRadioButton)
+ return m_gtkRadioButton;
+ m_gtkRadioButton = gtk_radio_button_new(0);
+ setupWidgetAndAddToContainer(m_gtkRadioButton, gtkContainer());
+ return m_gtkRadioButton;
+}
+
+GtkWidget* RenderThemeGtk::gtkCheckButton() const
+{
+ if (m_gtkCheckButton)
+ return m_gtkCheckButton;
+ m_gtkCheckButton = gtk_check_button_new();
+ setupWidgetAndAddToContainer(m_gtkCheckButton, gtkContainer());
+ return m_gtkCheckButton;
+}
+
+GtkWidget* RenderThemeGtk::gtkProgressBar() const
+{
+ if (m_gtkProgressBar)
+ return m_gtkProgressBar;
+ m_gtkProgressBar = gtk_progress_bar_new();
+ setupWidgetAndAddToContainer(m_gtkProgressBar, gtkContainer());
+ return m_gtkProgressBar;
+}
+
+static void getGtkComboBoxButton(GtkWidget* widget, gpointer target)
+{
+ if (!GTK_IS_TOGGLE_BUTTON(widget))
+ return;
+ GtkWidget** widgetTarget = static_cast<GtkWidget**>(target);
+ *widgetTarget = widget;
+}
+
+typedef struct {
+ GtkWidget* arrow;
+ GtkWidget* separator;
+} ComboBoxWidgetPieces;
+
+static void getGtkComboBoxPieces(GtkWidget* widget, gpointer data)
+{
+ if (GTK_IS_ARROW(widget)) {
+ static_cast<ComboBoxWidgetPieces*>(data)->arrow = widget;
+ return;
+ }
+ if (GTK_IS_SEPARATOR(widget))
+ static_cast<ComboBoxWidgetPieces*>(data)->separator = widget;
+}
+
+GtkWidget* RenderThemeGtk::gtkComboBox() const
+{
+ if (m_gtkComboBox)
+ return m_gtkComboBox;
+ m_gtkComboBox = gtk_combo_box_new();
+ setupWidgetAndAddToContainer(m_gtkComboBox, gtkContainer());
+ return m_gtkComboBox;
+}
+
+void RenderThemeGtk::refreshComboBoxChildren() const
+{
+ gtkComboBox(); // Ensure that we've initialized the combo box.
+
+ // Some themes look at widget ancestry to determine how to render widgets, so
+ // get the GtkButton that is the actual child of the combo box.
+ gtk_container_forall(GTK_CONTAINER(m_gtkComboBox), getGtkComboBoxButton, &m_gtkComboBoxButton);
+ ASSERT(m_gtkComboBoxButton);
+ setupWidget(m_gtkComboBoxButton);
+ g_object_add_weak_pointer(G_OBJECT(m_gtkComboBoxButton), reinterpret_cast<gpointer*>(&m_gtkComboBoxButton));
+
+ ComboBoxWidgetPieces pieces = { 0, 0 };
+ GtkWidget* buttonChild = gtk_bin_get_child(GTK_BIN(gtkComboBoxButton()));
+ if (GTK_IS_HBOX(buttonChild))
+ gtk_container_forall(GTK_CONTAINER(buttonChild), getGtkComboBoxPieces, &pieces);
+ else if (GTK_IS_ARROW(buttonChild))
+ pieces.arrow = buttonChild;
+
+ ASSERT(pieces.arrow);
+ m_gtkComboBoxArrow = pieces.arrow;
+ setupWidget(m_gtkComboBoxArrow);
+ // When the style changes, the combo box may destroy its children.
+ g_object_add_weak_pointer(G_OBJECT(m_gtkComboBoxArrow), reinterpret_cast<gpointer*>(&m_gtkComboBoxArrow));
+
+ m_gtkComboBoxSeparator = pieces.separator;
+ if (m_gtkComboBoxSeparator) {
+ setupWidget(m_gtkComboBoxSeparator);
+ // When the style changes, the combo box may destroy its children.
+ g_object_add_weak_pointer(G_OBJECT(m_gtkComboBoxSeparator), reinterpret_cast<gpointer*>(&m_gtkComboBoxSeparator));
+ }
+}
+
+GtkWidget* RenderThemeGtk::gtkComboBoxButton() const
+{
+ if (m_gtkComboBoxButton)
+ return m_gtkComboBoxButton;
+ refreshComboBoxChildren();
+ ASSERT(m_gtkComboBoxButton);
+ return m_gtkComboBoxButton;
+}
+
+GtkWidget* RenderThemeGtk::gtkComboBoxArrow() const
+{
+ if (m_gtkComboBoxArrow)
+ return m_gtkComboBoxArrow;
+ refreshComboBoxChildren();
+ ASSERT(m_gtkComboBoxArrow);
+ return m_gtkComboBoxArrow;
+}
+
+GtkWidget* RenderThemeGtk::gtkComboBoxSeparator() const
+{
+ // m_gtkComboBoxSeparator may be null either because we haven't initialized the combo box
+ // or because the combo boxes in this theme don't have separators. If m_gtkComboBoxArrow
+ // arrow isn't null, we definitely have initialized the combo box.
+ if (m_gtkComboBoxArrow || m_gtkComboBoxButton)
+ return m_gtkComboBoxSeparator;
+ refreshComboBoxChildren();
+ return m_gtkComboBoxSeparator;
+}
+
GtkWidget* RenderThemeGtk::gtkScrollbar()
{
return moz_gtk_get_scrollbar_widget();