diff options
Diffstat (limited to 'Source/WebCore/platform/gtk/RenderThemeGtk2.cpp')
-rw-r--r-- | Source/WebCore/platform/gtk/RenderThemeGtk2.cpp | 477 |
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(); |