Bug 399545 - "<textbox type="number"> spin buttons look wrong with some GTK themes" (add GTK theming for spinner and spinner-textfield) [p=twanno@lycos.nl (Teune van Steeg) r+sr=roc a1.9=beltzner]
authorreed@reedloden.com
Fri, 21 Dec 2007 03:30:00 -0800
changeset 9589 af08b27a13e8356ab109c93a6667490210962036
parent 9588 66b69a8395460c6b5a5512a7c5ad5fac20907958
child 9590 d5864e81a4a48a436d09a7113ed41ce9baf4adae
push idunknown
push userunknown
push dateunknown
bugs399545
milestone1.9b3pre
Bug 399545 - "<textbox type="number"> spin buttons look wrong with some GTK themes" (add GTK theming for spinner and spinner-textfield) [p=twanno@lycos.nl (Teune van Steeg) r+sr=roc a1.9=beltzner]
gfx/public/nsThemeConstants.h
layout/style/nsCSSKeywordList.h
layout/style/nsCSSProps.cpp
toolkit/themes/gnomestripe/global/numberbox.css
toolkit/themes/winstripe/global/spinbuttons.css
widget/src/gtk2/gtk2drawing.c
widget/src/gtk2/gtkdrawing.h
widget/src/gtk2/nsNativeThemeGTK.cpp
--- a/gfx/public/nsThemeConstants.h
+++ b/gfx/public/nsThemeConstants.h
@@ -118,16 +118,19 @@
 #define NS_THEME_SPINNER                                   72
 
 // The up button of a spin control
 #define NS_THEME_SPINNER_UP_BUTTON                         73
 
 // The down button of a spin control
 #define NS_THEME_SPINNER_DOWN_BUTTON                       74
 
+// The textfield of a spin control
+#define NS_THEME_SPINNER_TEXTFIELD                         75
+
 // A scrollbar.
 #define NS_THEME_SCROLLBAR                                 80
 
 // A small scrollbar.
 #define NS_THEME_SCROLLBAR_SMALL                           81
 
 // A scrollbar button (up/down/left/right)
 #define NS_THEME_SCROLLBAR_BUTTON_UP                       82
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -502,16 +502,17 @@ CSS_KEY(tab, tab)
 CSS_KEY(tab-left-edge, tab_left_edge)
 CSS_KEY(tab-right-edge, tab_right_edge)
 CSS_KEY(tabpanels, tabpanels)
 CSS_KEY(tabpanel, tabpanel)
 CSS_KEY(tooltip, tooltip)
 CSS_KEY(spinner, spinner)
 CSS_KEY(spinner-upbutton, spinner_upbutton)
 CSS_KEY(spinner-downbutton, spinner_downbutton)
+CSS_KEY(spinner-textfield, spinner_textfield)
 CSS_KEY(scrollbarbutton-up, scrollbarbutton_up)
 CSS_KEY(scrollbarbutton-down, scrollbarbutton_down)
 CSS_KEY(scrollbarbutton-left, scrollbarbutton_left)
 CSS_KEY(scrollbarbutton-right, scrollbarbutton_right)
 CSS_KEY(scrollbartrack-horizontal, scrollbartrack_horizontal)
 CSS_KEY(scrollbartrack-vertical, scrollbartrack_vertical)
 CSS_KEY(scrollbarthumb-horizontal, scrollbarthumb_horizontal)
 CSS_KEY(scrollbarthumb-vertical, scrollbarthumb_vertical)
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -213,16 +213,17 @@ const PRInt32 nsCSSProps::kAppearanceKTa
   eCSSKeyword_tab_left_edge,          NS_THEME_TAB_LEFT_EDGE,
   eCSSKeyword_tab_right_edge,         NS_THEME_TAB_RIGHT_EDGE,
   eCSSKeyword_tabpanels,              NS_THEME_TAB_PANELS,
   eCSSKeyword_tabpanel,               NS_THEME_TAB_PANEL,
   eCSSKeyword_tooltip,                NS_THEME_TOOLTIP,
   eCSSKeyword_spinner,                NS_THEME_SPINNER,
   eCSSKeyword_spinner_upbutton,       NS_THEME_SPINNER_UP_BUTTON,
   eCSSKeyword_spinner_downbutton,     NS_THEME_SPINNER_DOWN_BUTTON,
+  eCSSKeyword_spinner_textfield,      NS_THEME_SPINNER_TEXTFIELD,
   eCSSKeyword_scrollbar,              NS_THEME_SCROLLBAR,
   eCSSKeyword_scrollbar_small,        NS_THEME_SCROLLBAR_SMALL,
   eCSSKeyword_scrollbarbutton_up,     NS_THEME_SCROLLBAR_BUTTON_UP,
   eCSSKeyword_scrollbarbutton_down,   NS_THEME_SCROLLBAR_BUTTON_DOWN,
   eCSSKeyword_scrollbarbutton_left,   NS_THEME_SCROLLBAR_BUTTON_LEFT,
   eCSSKeyword_scrollbarbutton_right,  NS_THEME_SCROLLBAR_BUTTON_RIGHT,
   eCSSKeyword_scrollbartrack_horizontal,    NS_THEME_SCROLLBAR_TRACK_HORIZONTAL,
   eCSSKeyword_scrollbartrack_vertical,      NS_THEME_SCROLLBAR_TRACK_VERTICAL,
--- a/toolkit/themes/gnomestripe/global/numberbox.css
+++ b/toolkit/themes/gnomestripe/global/numberbox.css
@@ -41,28 +41,33 @@
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 @namespace html url("http://www.w3.org/1999/xhtml");
 
 textbox[type="number"] {
   -moz-appearance: none;
   padding: 0 !important;
   border: none;
   cursor: default;
+  background-color: transparent;
 }
 
 html|*.numberbox-input {
   text-align: right;
 }
 
 .numberbox-input-box {
   -moz-box-align: center;
-  -moz-appearance: textfield;
+  -moz-appearance: spinner-textfield;
   cursor: text;
   margin-right: -1px;
   border: 2px solid;
   -moz-border-top-colors: ThreeDShadow ThreeDDarkShadow;
   -moz-border-right-colors: ThreeDHighlight ThreeDLightShadow;
   -moz-border-bottom-colors: ThreeDHighlight ThreeDLightShadow;
   -moz-border-left-colors: ThreeDShadow ThreeDDarkShadow;
   padding: 3px;
   background-color: -moz-Field;
   color: -moz-FieldText;
 }
+
+textbox[hidespinbuttons="true"] > .numberbox-input-box {
+  -moz-appearance: textfield;
+}
--- a/toolkit/themes/winstripe/global/spinbuttons.css
+++ b/toolkit/themes/winstripe/global/spinbuttons.css
@@ -33,16 +33,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 
 spinbuttons {
+  -moz-appearance: spinner;
   cursor: default;
 }
 
 .spinbuttons-button {
   min-width: 13px;
   min-height: 11px;
   margin: 0 !important;
   border: 2px solid;
--- a/widget/src/gtk2/gtk2drawing.c
+++ b/widget/src/gtk2/gtk2drawing.c
@@ -924,22 +924,41 @@ moz_gtk_scrollbar_thumb_paint(GtkThemeWi
                      rect->width,  rect->height,
                      (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ?
                      GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
 
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
-moz_gtk_spin_paint(GdkDrawable* drawable, GdkRectangle* rect, gboolean isDown,
-                   GtkWidgetState* state, GtkTextDirection direction)
+moz_gtk_spin_paint(GdkDrawable* drawable, GdkRectangle* rect,
+                   GtkTextDirection direction)
+{
+    GtkStyle* style;
+
+    ensure_spin_widget();
+    gtk_widget_set_direction(gSpinWidget, direction);
+    style = gSpinWidget->style;
+
+    TSOffsetStyleGCs(style, rect->x, rect->y);
+    gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL,
+                  gSpinWidget, "spinbutton",
+                  rect->x, rect->y, rect->width, rect->height);
+    return MOZ_GTK_SUCCESS;
+}
+
+static gint
+moz_gtk_spin_updown_paint(GdkDrawable* drawable, GdkRectangle* rect,
+                          gboolean isDown, GtkWidgetState* state,
+                          GtkTextDirection direction)
 {
     GdkRectangle arrow_rect;
     GtkStateType state_type = ConvertGtkState(state);
-    GtkShadowType shadow_type = state_type == GTK_STATE_ACTIVE ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
+    GtkShadowType shadow_type = state_type == GTK_STATE_ACTIVE ?
+                                  GTK_SHADOW_IN : GTK_SHADOW_OUT;
     GtkStyle* style;
 
     ensure_spin_widget();
     style = gSpinWidget->style;
     gtk_widget_set_direction(gSpinWidget, direction);
 
     TSOffsetStyleGCs(style, rect->x, rect->y);
     gtk_paint_box(style, drawable, state_type, shadow_type, NULL, gSpinWidget,
@@ -1090,41 +1109,40 @@ moz_gtk_vpaned_paint(GdkDrawable* drawab
                      GTK_ORIENTATION_HORIZONTAL);
 
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
 moz_gtk_entry_paint(GdkDrawable* drawable, GdkRectangle* rect,
                     GdkRectangle* cliprect, GtkWidgetState* state,
-                    GtkTextDirection direction)
+                    GtkWidget* widget, GtkTextDirection direction)
 {
     gint x, y, width = rect->width, height = rect->height;
     GtkStyle* style;
     gboolean interior_focus;
     gint focus_width;
 
-    ensure_entry_widget();
-    gtk_widget_set_direction(gEntryWidget, direction);
+    gtk_widget_set_direction(widget, direction);
 
-    style = gEntryWidget->style;
+    style = widget->style;
 
     /* paint the background first */
     x = XTHICKNESS(style);
     y = YTHICKNESS(style);
 
     /* This gets us a lovely greyish disabledish look */
-    gtk_widget_set_sensitive(gEntryWidget, !state->disabled);
+    gtk_widget_set_sensitive(widget, !state->disabled);
 
     TSOffsetStyleGCs(style, rect->x + x, rect->y + y);
     gtk_paint_flat_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_NONE,
-                       cliprect, gEntryWidget, "entry_bg",  rect->x + x,
+                       cliprect, widget, "entry_bg",  rect->x + x,
                        rect->y + y, rect->width - 2*x, rect->height - 2*y);
 
-    gtk_widget_style_get(gEntryWidget,
+    gtk_widget_style_get(widget,
                          "interior-focus", &interior_focus,
                          "focus-line-width", &focus_width,
                          NULL);
 
     /*
      * Now paint the shadow and focus border.
      *
      * gtk+ is able to draw over top of the entry when it gains focus,
@@ -1135,42 +1153,42 @@ moz_gtk_entry_paint(GdkDrawable* drawabl
      * shadow, then draw the shadow again, inset, if we're focused.
      */
 
     x = rect->x;
     y = rect->y;
 
     if (state->focused && !state->disabled) {
          /* This will get us the lit borders that focused textboxes enjoy on some themes. */
-        GTK_WIDGET_SET_FLAGS(gEntryWidget, GTK_HAS_FOCUS);
+        GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
 
         if (!interior_focus) {
             /* Indent the border a little bit if we have exterior focus 
                (this is what GTK does to draw native entries) */
             x += focus_width;
             y += focus_width;
             width -= 2 * focus_width;
             height -= 2 * focus_width;
         }
     }
 
     TSOffsetStyleGCs(style, x, y);
     gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN,
-                     cliprect, gEntryWidget, "entry", x, y, width, height);
+                     cliprect, widget, "entry", x, y, width, height);
 
     if (state->focused && !state->disabled) {
         if (!interior_focus) {
             TSOffsetStyleGCs(style, rect->x, rect->y);
             gtk_paint_focus(style, drawable,  GTK_STATE_NORMAL, cliprect,
-                            gEntryWidget, "entry",
+                            widget, "entry",
                             rect->x, rect->y, rect->width, rect->height);
         }
 
         /* Now unset the focus flag. We don't want other entries to look like they're focused too! */
-        GTK_WIDGET_UNSET_FLAGS(gEntryWidget, GTK_HAS_FOCUS);
+        GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
     }
 
     return MOZ_GTK_SUCCESS;
 }
 
 static gint 
 moz_gtk_treeview_paint(GdkDrawable* drawable, GdkRectangle* rect,
                        GdkRectangle* cliprect, GtkWidgetState* state,
@@ -2081,16 +2099,17 @@ moz_gtk_get_widget_border(GtkThemeWidget
     case MOZ_GTK_TABPANELS:
         ensure_tab_widget();
         w = gTabWidget;
         break;
     case MOZ_GTK_PROGRESSBAR:
         ensure_progress_widget();
         w = gProgressWidget;
         break;
+    case MOZ_GTK_SPINBUTTON_ENTRY:
     case MOZ_GTK_SPINBUTTON_UP:
     case MOZ_GTK_SPINBUTTON_DOWN:
         ensure_spin_widget();
         w = gSpinWidget;
         break;
     case MOZ_GTK_SCALE_HORIZONTAL:
         ensure_scale_widget();
         w = gHScaleWidget;
@@ -2193,16 +2212,17 @@ moz_gtk_get_widget_border(GtkThemeWidget
     case MOZ_GTK_SCALE_THUMB_VERTICAL:
     case MOZ_GTK_GRIPPER:
     case MOZ_GTK_PROGRESS_CHUNK:
     case MOZ_GTK_TAB:
     case MOZ_GTK_EXPANDER:
     case MOZ_GTK_TOOLBAR_SEPARATOR:
     case MOZ_GTK_MENUSEPARATOR:
     /* These widgets have no borders.*/
+    case MOZ_GTK_SPINBUTTON:
     case MOZ_GTK_TOOLTIP:
     case MOZ_GTK_WINDOW:
     case MOZ_GTK_RESIZER:
     case MOZ_GTK_MENUARROW:
         *left = *top = *right = *bottom = 0;
         return MOZ_GTK_SUCCESS;
     default:
         g_warning("Unsupported widget type: %d", widget);
@@ -2370,21 +2390,29 @@ moz_gtk_widget_paint(GtkThemeWidgetType 
         return moz_gtk_scale_paint(drawable, rect, cliprect, state,
                                    (GtkOrientation) flags, direction);
         break;
     case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
     case MOZ_GTK_SCALE_THUMB_VERTICAL:
         return moz_gtk_scale_thumb_paint(drawable, rect, cliprect, state,
                                          (GtkOrientation) flags, direction);
         break;
+    case MOZ_GTK_SPINBUTTON:
+        return moz_gtk_spin_paint(drawable, rect, direction);
+        break;
     case MOZ_GTK_SPINBUTTON_UP:
     case MOZ_GTK_SPINBUTTON_DOWN:
-        return moz_gtk_spin_paint(drawable, rect,
-                                  (widget == MOZ_GTK_SPINBUTTON_DOWN),
-                                  state, direction);
+        return moz_gtk_spin_updown_paint(drawable, rect,
+                                         (widget == MOZ_GTK_SPINBUTTON_DOWN),
+                                         state, direction);
+        break;
+    case MOZ_GTK_SPINBUTTON_ENTRY:
+        ensure_spin_widget();
+        return moz_gtk_entry_paint(drawable, rect, cliprect, state,
+                                   gSpinWidget, direction);
         break;
     case MOZ_GTK_GRIPPER:
         return moz_gtk_gripper_paint(drawable, rect, cliprect, state,
                                      direction);
         break;
     case MOZ_GTK_TREEVIEW:
         return moz_gtk_treeview_paint(drawable, rect, cliprect, state,
                                       direction);
@@ -2399,18 +2427,19 @@ moz_gtk_widget_paint(GtkThemeWidgetType 
                                                     (GtkArrowType) flags,
                                                     direction);
         break;
     case MOZ_GTK_EXPANDER:
         return moz_gtk_expander_paint(drawable, rect, cliprect, state,
                                       (GtkExpanderStyle) flags, direction);
         break;
     case MOZ_GTK_ENTRY:
+        ensure_entry_widget();
         return moz_gtk_entry_paint(drawable, rect, cliprect, state,
-                                   direction);
+                                   gEntryWidget, direction);
         break;
     case MOZ_GTK_DROPDOWN:
         return moz_gtk_option_menu_paint(drawable, rect, cliprect, state,
                                          direction);
         break;
     case MOZ_GTK_DROPDOWN_ARROW:
         return moz_gtk_dropdown_arrow_paint(drawable, rect, cliprect, state,
                                             direction);
--- a/widget/src/gtk2/gtkdrawing.h
+++ b/widget/src/gtk2/gtkdrawing.h
@@ -123,18 +123,20 @@ typedef enum {
   MOZ_GTK_SCROLLBAR_THUMB_VERTICAL,
   /* Paints a GtkScale. */
   MOZ_GTK_SCALE_HORIZONTAL,
   MOZ_GTK_SCALE_VERTICAL,
   /* Paints a GtkScale thumb. */
   MOZ_GTK_SCALE_THUMB_HORIZONTAL,
   MOZ_GTK_SCALE_THUMB_VERTICAL,
   /* Paints a GtkSpinButton */
+  MOZ_GTK_SPINBUTTON,
   MOZ_GTK_SPINBUTTON_UP,
   MOZ_GTK_SPINBUTTON_DOWN,
+  MOZ_GTK_SPINBUTTON_ENTRY,
   /* Paints the gripper of a GtkHandleBox. */
   MOZ_GTK_GRIPPER,
   /* Paints a GtkEntry. */
   MOZ_GTK_ENTRY,
   /* Paints a GtkOptionMenu. */
   MOZ_GTK_DROPDOWN,
   /* Paints a dropdown arrow (a GtkButton containing a down GtkArrow). */
   MOZ_GTK_DROPDOWN_ARROW,
--- a/widget/src/gtk2/nsNativeThemeGTK.cpp
+++ b/widget/src/gtk2/nsNativeThemeGTK.cpp
@@ -186,18 +186,20 @@ nsNativeThemeGTK::GetGtkWidgetAndState(P
                                        gint* aWidgetFlags)
 {
   if (aState) {
     if (!aFrame) {
       // reset the entire struct to zero
       memset(aState, 0, sizeof(GtkWidgetState));
     } else {
 
-      // for dropdown textfields, look at the parent frame (textbox or menulist)
-      if (aWidgetType == NS_THEME_DROPDOWN_TEXTFIELD)
+      // for dropdown and spinner textfields,
+      // look at the parent frame (textbox or menulist)
+      if (aWidgetType == NS_THEME_DROPDOWN_TEXTFIELD ||
+          aWidgetType == NS_THEME_SPINNER_TEXTFIELD)
         aFrame = aFrame->GetParent();
 
       // For XUL checkboxes and radio buttons, the state of the parent
       // determines our state.
       nsIFrame *stateFrame = aFrame;
       if (aFrame && ((aWidgetFlags && (IsCheckboxWidgetType(aWidgetType) ||
                                        IsRadioWidgetType(aWidgetType))) ||
                      aWidgetType == NS_THEME_CHECKBOX_LABEL ||
@@ -247,16 +249,17 @@ nsNativeThemeGTK::GetGtkWidgetAndState(P
 
       if (aFrame && aFrame->GetContent()->IsNodeOfType(nsINode::eXUL)) {
         // For these widget types, some element (either a child or parent)
         // actually has element focus, so we check the focused attribute
         // to see whether to draw in the focused state.
         if (aWidgetType == NS_THEME_TEXTFIELD ||
             aWidgetType == NS_THEME_TEXTFIELD_MULTILINE ||
             aWidgetType == NS_THEME_DROPDOWN_TEXTFIELD ||
+            aWidgetType == NS_THEME_SPINNER_TEXTFIELD ||
             aWidgetType == NS_THEME_RADIO_CONTAINER ||
             aWidgetType == NS_THEME_RADIO_LABEL) {
           aState->focused = IsFocused(aFrame);
         } else if (IsRadioWidgetType(aWidgetType) ||
                    IsCheckboxWidgetType(aWidgetType)) {
           // In XUL, checkboxes and radios shouldn't have focus rings, their labels do
           aState->focused = FALSE;
         }