diff --git a/widget/src/gtk/gtkdrawing.c b/widget/src/gtk/gtkdrawing.c index 9b96154d4ae..133109425e0 100644 --- a/widget/src/gtk/gtkdrawing.c +++ b/widget/src/gtk/gtkdrawing.c @@ -54,6 +54,7 @@ extern GtkWidget* gEntryWidget; extern GtkWidget* gArrowWidget; extern GtkWidget* gDropdownButtonWidget; extern GtkWidget* gHandleBoxWidget; +extern GtkTooltips* gTooltipWidget; GtkStateType ConvertGtkState(GtkWidgetState* state) @@ -158,6 +159,23 @@ moz_gtk_checkbox_paint(GdkWindow* window, GtkStyle* style, gCheckboxWidget, "checkbutton", x, y, width, height); } +void +moz_gtk_arrow_paint(GdkWindow* window, GtkStyle* style, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state) +{ + GtkMisc* misc = GTK_MISC(gArrowWidget); + gint extent = MIN(rect->width - misc->xpad * 2, rect->height - misc->ypad * 2); + gint x = ((rect->x + misc->xpad) * (1.0 - misc->xalign) + + (rect->x + rect->width - extent - misc->xpad) * misc->xalign); + gint y = ((rect->y + misc->ypad) * (1.0 - misc->yalign) + + (rect->y + rect->height - extent - misc->ypad) * misc->yalign); + + gtk_paint_arrow(style, window, ConvertGtkState(state), + state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT, cliprect, + gArrowWidget, "arrow", GTK_ARROW_DOWN, TRUE, x, + y, extent, extent); +} + void moz_gtk_scrollbar_button_paint(GdkWindow* window, GtkStyle* style, GdkRectangle* rect, GdkRectangle* cliprect, @@ -206,6 +224,11 @@ moz_gtk_get_scrollbar_metrics(gint* slider_width, gint* trough_border, gint* stepper_size, gint* stepper_spacing, gint* min_slider_size) { +#if ((GTK_MINOR_VERSION == 2) && (GTK_MICRO_VERSION > 8)) || (GTK_MINOR_VERSION > 2) + /* + * This API is only supported in GTK+ >= 1.2.9, and gives per-theme values. + */ + if (slider_width) *slider_width = gtk_style_get_prop_experimental(gScrollbarWidget->style, "GtkRange::slider_width", @@ -228,6 +251,26 @@ moz_gtk_get_scrollbar_metrics(gint* slider_width, gint* trough_border, if (min_slider_size) *min_slider_size = RANGE_CLASS(gScrollbarWidget)->min_slider_size; +#else + /* + * This is the older method, which gives per-engine values. + */ + + if (slider_width) + *slider_width = RANGE_CLASS(gScrollbarWidget)->slider_width; + + if (trough_border) + *trough_border = gScrollbarWidget->style->klass->xthickness; + + if (stepper_size) + *stepper_size = RANGE_CLASS(gScrollbarWidget)->stepper_size; + + if (stepper_spacing) + *stepper_spacing = RANGE_CLASS(gScrollbarWidget)->stepper_slider_spacing; + + if (min_slider_size) + *min_slider_size = RANGE_CLASS(gScrollbarWidget)->min_slider_size; +#endif } void @@ -275,12 +318,18 @@ moz_gtk_dropdown_arrow_paint(GdkWindow* window, GtkStyle* style, GdkRectangle* rect, GdkRectangle* cliprect, GtkWidgetState* state) { + GdkRectangle arrow_rect; + moz_gtk_button_paint(window, gDropdownButtonWidget->style, rect, cliprect, state, GTK_RELIEF_NORMAL); - gtk_paint_arrow(style, window, ConvertGtkState(state), - state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT, cliprect, - gArrowWidget, "arrow", GTK_ARROW_DOWN, TRUE, rect->x, rect->y, - rect->width, rect->height); + + /* This mirrors gtkbutton's child positioning */ + arrow_rect.x = rect->x + 1 + gDropdownButtonWidget->style->klass->xthickness; + arrow_rect.y = rect->y + 1 + gDropdownButtonWidget->style->klass->ythickness; + arrow_rect.width = MAX(1, rect->width - (arrow_rect.x - rect->x) * 2); + arrow_rect.height = MAX(1, rect->height - (arrow_rect.y - rect->y) * 2); + + moz_gtk_arrow_paint(window, style, &arrow_rect, cliprect, state); } void @@ -294,10 +343,14 @@ moz_gtk_container_paint(GdkWindow* window, GtkStyle* style, GdkRectangle* rect, state_type = GTK_STATE_NORMAL; if (state_type != GTK_STATE_NORMAL) /* this is for drawing a prelight box */ - gtk_paint_flat_box (style, window, state_type, GTK_SHADOW_ETCHED_OUT, - cliprect, gCheckboxWidget, - isradio ? "radiobutton" : "checkbutton", - rect->x, rect->y, rect->width, rect->height); + gtk_paint_flat_box(style, window, state_type, GTK_SHADOW_ETCHED_OUT, + cliprect, gCheckboxWidget, + isradio ? "radiobutton" : "checkbutton", + rect->x, rect->y, rect->width, rect->height); + + if (state->focused) + gtk_paint_focus(style, window, cliprect, gCheckboxWidget, "checkbutton", + rect->x, rect->y, rect->width - 1, rect->height - 1); } void @@ -308,3 +361,13 @@ moz_gtk_toolbar_paint(GdkWindow* window, GtkStyle* style, GdkRectangle* rect, cliprect, gHandleBoxWidget, "dockitem_bin", rect->x, rect->y, rect->width, rect->height); } + +void +moz_gtk_tooltip_paint(GdkWindow* window, GtkStyle* style, GdkRectangle* rect, + GdkRectangle* cliprect) +{ + gtk_paint_flat_box(style, window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, + cliprect, gTooltipWidget->tip_window, "tooltip", rect->x, + rect->y, rect->width, rect->height); +} + diff --git a/widget/src/gtk/gtkdrawing.h b/widget/src/gtk/gtkdrawing.h index df629a31317..ecce2bf71d5 100644 --- a/widget/src/gtk/gtkdrawing.h +++ b/widget/src/gtk/gtkdrawing.h @@ -107,6 +107,10 @@ void moz_gtk_toolbar_paint(GdkWindow* window, GtkStyle* style, GdkRectangle* rect, GdkRectangle* cliprect); +void +moz_gtk_tooltip_paint(GdkWindow* window, GtkStyle* style, GdkRectangle* rect, + GdkRectangle* cliprect); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/widget/src/gtk/nsNativeThemeGTK.cpp b/widget/src/gtk/nsNativeThemeGTK.cpp index 8986a359d03..9a245137ac9 100644 --- a/widget/src/gtk/nsNativeThemeGTK.cpp +++ b/widget/src/gtk/nsNativeThemeGTK.cpp @@ -64,6 +64,7 @@ GtkWidget* gDropdownButonWidget; GtkWidget* gArrowWidget; GtkWidget* gDropdownButtonWidget; GtkWidget* gHandleBoxWidget; +GtkTooltips* gTooltipWidget; nsNativeThemeGTK::nsNativeThemeGTK() : mProtoWindow(nsnull), @@ -82,6 +83,8 @@ nsNativeThemeGTK::~nsNativeThemeGTK() { // This will destroy all of our widgets if (mProtoWindow) gtk_widget_destroy(mProtoWindow); + if (gTooltipWidget) + gtk_object_unref(GTK_OBJECT(gTooltipWidget)); } static void GetPrimaryPresShell(nsIFrame* aFrame, nsIPresShell** aResult) @@ -166,7 +169,8 @@ GetSystemFont(PRUint8 aWidgetType, nsSystemFontID& aFont) } void -nsNativeThemeGTK::GetGtkWidgetState(nsIFrame* aFrame, GtkWidgetState* aState) +nsNativeThemeGTK::GetGtkWidgetState(PRUint8 aWidgetType, + nsIFrame* aFrame, GtkWidgetState* aState) { if (!aFrame) { aState->active = PR_FALSE; @@ -178,7 +182,11 @@ nsNativeThemeGTK::GetGtkWidgetState(nsIFrame* aFrame, GtkWidgetState* aState) } else { PRInt32 eventState = GetContentState(aFrame); aState->active = (eventState & NS_EVENT_STATE_ACTIVE); - aState->focused = (eventState & NS_EVENT_STATE_FOCUS); + if (aWidgetType == NS_THEME_TEXTFIELD || + aWidgetType == NS_THEME_RADIO_CONTAINER) + aState->focused = CheckBooleanAttr(aFrame, mFocusedAtom); + else + aState->focused = (eventState & NS_EVENT_STATE_FOCUS); aState->inHover = (eventState & NS_EVENT_STATE_HOVER); aState->disabled = IsDisabled(aFrame); aState->isDefault = PR_FALSE; // XXX fix me @@ -207,7 +215,7 @@ nsNativeThemeGTK::DrawWidgetBackground(nsIRenderingContext* aContext, GdkRectangle gdk_clip = {cr.x, cr.y, cr.width, cr.height}; GtkWidgetState state; - GetGtkWidgetState(aFrame, &state); + GetGtkWidgetState(aWidgetType, aFrame, &state); switch (aWidgetType) { @@ -292,11 +300,6 @@ nsNativeThemeGTK::DrawWidgetBackground(nsIRenderingContext* aContext, // fall through case NS_THEME_TEXTFIELD: - // The textfield element isn't actually focused, the inner - // html:input is. Use the focused attribute to get the correct - // state. - - state.focused = CheckBooleanAttr(aFrame, mFocusedAtom); EnsureEntryWidget(); moz_gtk_entry_paint(window, gEntryWidget->style, &gdk_rect, &gdk_clip, &state); @@ -320,6 +323,11 @@ nsNativeThemeGTK::DrawWidgetBackground(nsIRenderingContext* aContext, moz_gtk_toolbar_paint(window, gHandleBoxWidget->style, &gdk_rect, &gdk_clip); break; + case NS_THEME_TOOLTIP: + EnsureTooltipWidget(); + moz_gtk_tooltip_paint(window, gTooltipWidget->tip_window->style, &gdk_rect, + &gdk_clip); + break; } return NS_OK; @@ -423,10 +431,16 @@ nsNativeThemeGTK::GetMinimumWidgetSize(nsIRenderingContext* aContext, case NS_THEME_DROPDOWN_BUTTON: { EnsureArrowWidget(); - GtkRequisition req; - gtk_widget_size_request(gDropdownButtonWidget, &req); - aResult->width = req.width; - aResult->height = req.height; + + // First, get the minimum size for the button itself. + aResult->width = 2 * (1 + gDropdownButtonWidget->style->klass->xthickness); + aResult->height = 2 * (1 + gDropdownButtonWidget->style->klass->ythickness); + + // Now, add in the requested size of the arrow. + // Note: the minimum arrow size is fixed at 11 pixels. + + aResult->width += 11 + GTK_MISC(gArrowWidget)->xpad * 2; + aResult->height += 11 + GTK_MISC(gArrowWidget)->ypad * 2; } break; case NS_THEME_CHECKBOX: @@ -441,6 +455,7 @@ nsNativeThemeGTK::GetMinimumWidgetSize(nsIRenderingContext* aContext, &indicator_spacing); aResult->width = aResult->height = indicator_size; + *aIsOverridable = PR_FALSE; } break; } @@ -515,6 +530,7 @@ nsNativeThemeGTK::ThemeSupportsWidget(nsIPresContext* aPresContext, case NS_THEME_DROPDOWN_TEXTFIELD: case NS_THEME_DROPDOWN_BUTTON: case NS_THEME_TOOLBOX: + case NS_THEME_TOOLTIP: return PR_TRUE; } @@ -612,3 +628,15 @@ nsNativeThemeGTK::EnsureHandleBoxWidget() SetupWidgetPrototype(gHandleBoxWidget); } } + +void +nsNativeThemeGTK::EnsureTooltipWidget() +{ + if (!gTooltipWidget) { + gTooltipWidget = gtk_tooltips_new(); + gtk_tooltips_force_window(gTooltipWidget); + gtk_widget_set_rc_style(gTooltipWidget->tip_window); + gtk_widget_realize(gTooltipWidget->tip_window); + } +} + diff --git a/widget/src/gtk/nsNativeThemeGTK.h b/widget/src/gtk/nsNativeThemeGTK.h index e64cfdc617c..d86a0269e51 100644 --- a/widget/src/gtk/nsNativeThemeGTK.h +++ b/widget/src/gtk/nsNativeThemeGTK.h @@ -80,7 +80,9 @@ public: protected: PRBool IsDisabled(nsIFrame* aFrame); - void GetGtkWidgetState(nsIFrame* aFrame, GtkWidgetState* aState); + void GetGtkWidgetState(PRUint8 aWidgetType, + nsIFrame* aFrame, GtkWidgetState* aState); + void GetScrollbarMetrics(gint* slider_width, gint* trough_border, gint* stepper_size, @@ -94,6 +96,7 @@ protected: void EnsureEntryWidget(); void EnsureArrowWidget(); void EnsureHandleBoxWidget(); + void EnsureTooltipWidget(); private: nsCOMPtr mCheckedAtom; diff --git a/widget/src/gtk2/gtkdrawing.h b/widget/src/gtk2/gtkdrawing.h index df629a31317..ecce2bf71d5 100644 --- a/widget/src/gtk2/gtkdrawing.h +++ b/widget/src/gtk2/gtkdrawing.h @@ -107,6 +107,10 @@ void moz_gtk_toolbar_paint(GdkWindow* window, GtkStyle* style, GdkRectangle* rect, GdkRectangle* cliprect); +void +moz_gtk_tooltip_paint(GdkWindow* window, GtkStyle* style, GdkRectangle* rect, + GdkRectangle* cliprect); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/widget/src/gtk2/nsNativeThemeGTK.cpp b/widget/src/gtk2/nsNativeThemeGTK.cpp index 8986a359d03..9a245137ac9 100644 --- a/widget/src/gtk2/nsNativeThemeGTK.cpp +++ b/widget/src/gtk2/nsNativeThemeGTK.cpp @@ -64,6 +64,7 @@ GtkWidget* gDropdownButonWidget; GtkWidget* gArrowWidget; GtkWidget* gDropdownButtonWidget; GtkWidget* gHandleBoxWidget; +GtkTooltips* gTooltipWidget; nsNativeThemeGTK::nsNativeThemeGTK() : mProtoWindow(nsnull), @@ -82,6 +83,8 @@ nsNativeThemeGTK::~nsNativeThemeGTK() { // This will destroy all of our widgets if (mProtoWindow) gtk_widget_destroy(mProtoWindow); + if (gTooltipWidget) + gtk_object_unref(GTK_OBJECT(gTooltipWidget)); } static void GetPrimaryPresShell(nsIFrame* aFrame, nsIPresShell** aResult) @@ -166,7 +169,8 @@ GetSystemFont(PRUint8 aWidgetType, nsSystemFontID& aFont) } void -nsNativeThemeGTK::GetGtkWidgetState(nsIFrame* aFrame, GtkWidgetState* aState) +nsNativeThemeGTK::GetGtkWidgetState(PRUint8 aWidgetType, + nsIFrame* aFrame, GtkWidgetState* aState) { if (!aFrame) { aState->active = PR_FALSE; @@ -178,7 +182,11 @@ nsNativeThemeGTK::GetGtkWidgetState(nsIFrame* aFrame, GtkWidgetState* aState) } else { PRInt32 eventState = GetContentState(aFrame); aState->active = (eventState & NS_EVENT_STATE_ACTIVE); - aState->focused = (eventState & NS_EVENT_STATE_FOCUS); + if (aWidgetType == NS_THEME_TEXTFIELD || + aWidgetType == NS_THEME_RADIO_CONTAINER) + aState->focused = CheckBooleanAttr(aFrame, mFocusedAtom); + else + aState->focused = (eventState & NS_EVENT_STATE_FOCUS); aState->inHover = (eventState & NS_EVENT_STATE_HOVER); aState->disabled = IsDisabled(aFrame); aState->isDefault = PR_FALSE; // XXX fix me @@ -207,7 +215,7 @@ nsNativeThemeGTK::DrawWidgetBackground(nsIRenderingContext* aContext, GdkRectangle gdk_clip = {cr.x, cr.y, cr.width, cr.height}; GtkWidgetState state; - GetGtkWidgetState(aFrame, &state); + GetGtkWidgetState(aWidgetType, aFrame, &state); switch (aWidgetType) { @@ -292,11 +300,6 @@ nsNativeThemeGTK::DrawWidgetBackground(nsIRenderingContext* aContext, // fall through case NS_THEME_TEXTFIELD: - // The textfield element isn't actually focused, the inner - // html:input is. Use the focused attribute to get the correct - // state. - - state.focused = CheckBooleanAttr(aFrame, mFocusedAtom); EnsureEntryWidget(); moz_gtk_entry_paint(window, gEntryWidget->style, &gdk_rect, &gdk_clip, &state); @@ -320,6 +323,11 @@ nsNativeThemeGTK::DrawWidgetBackground(nsIRenderingContext* aContext, moz_gtk_toolbar_paint(window, gHandleBoxWidget->style, &gdk_rect, &gdk_clip); break; + case NS_THEME_TOOLTIP: + EnsureTooltipWidget(); + moz_gtk_tooltip_paint(window, gTooltipWidget->tip_window->style, &gdk_rect, + &gdk_clip); + break; } return NS_OK; @@ -423,10 +431,16 @@ nsNativeThemeGTK::GetMinimumWidgetSize(nsIRenderingContext* aContext, case NS_THEME_DROPDOWN_BUTTON: { EnsureArrowWidget(); - GtkRequisition req; - gtk_widget_size_request(gDropdownButtonWidget, &req); - aResult->width = req.width; - aResult->height = req.height; + + // First, get the minimum size for the button itself. + aResult->width = 2 * (1 + gDropdownButtonWidget->style->klass->xthickness); + aResult->height = 2 * (1 + gDropdownButtonWidget->style->klass->ythickness); + + // Now, add in the requested size of the arrow. + // Note: the minimum arrow size is fixed at 11 pixels. + + aResult->width += 11 + GTK_MISC(gArrowWidget)->xpad * 2; + aResult->height += 11 + GTK_MISC(gArrowWidget)->ypad * 2; } break; case NS_THEME_CHECKBOX: @@ -441,6 +455,7 @@ nsNativeThemeGTK::GetMinimumWidgetSize(nsIRenderingContext* aContext, &indicator_spacing); aResult->width = aResult->height = indicator_size; + *aIsOverridable = PR_FALSE; } break; } @@ -515,6 +530,7 @@ nsNativeThemeGTK::ThemeSupportsWidget(nsIPresContext* aPresContext, case NS_THEME_DROPDOWN_TEXTFIELD: case NS_THEME_DROPDOWN_BUTTON: case NS_THEME_TOOLBOX: + case NS_THEME_TOOLTIP: return PR_TRUE; } @@ -612,3 +628,15 @@ nsNativeThemeGTK::EnsureHandleBoxWidget() SetupWidgetPrototype(gHandleBoxWidget); } } + +void +nsNativeThemeGTK::EnsureTooltipWidget() +{ + if (!gTooltipWidget) { + gTooltipWidget = gtk_tooltips_new(); + gtk_tooltips_force_window(gTooltipWidget); + gtk_widget_set_rc_style(gTooltipWidget->tip_window); + gtk_widget_realize(gTooltipWidget->tip_window); + } +} + diff --git a/widget/src/gtk2/nsNativeThemeGTK.h b/widget/src/gtk2/nsNativeThemeGTK.h index e64cfdc617c..d86a0269e51 100644 --- a/widget/src/gtk2/nsNativeThemeGTK.h +++ b/widget/src/gtk2/nsNativeThemeGTK.h @@ -80,7 +80,9 @@ public: protected: PRBool IsDisabled(nsIFrame* aFrame); - void GetGtkWidgetState(nsIFrame* aFrame, GtkWidgetState* aState); + void GetGtkWidgetState(PRUint8 aWidgetType, + nsIFrame* aFrame, GtkWidgetState* aState); + void GetScrollbarMetrics(gint* slider_width, gint* trough_border, gint* stepper_size, @@ -94,6 +96,7 @@ protected: void EnsureEntryWidget(); void EnsureArrowWidget(); void EnsureHandleBoxWidget(); + void EnsureTooltipWidget(); private: nsCOMPtr mCheckedAtom;