Arrows and spinbuttons



Here is a patch that does:

        - gtkmenuitem.c

                - makes arrows that indicate that a submenu exists scale with
                  the size of the menu item

        - gtkspinbutton.c:

                - cleanups and fixes

                - all drawing moved to expose handler

                - up/down arrows are drawn disabled when they 
                  can't be used

                - up/down arrows are drawn with prelight and active
                  look (when appropriate)

        - gtkstyle.c:

                - no extra lines at the base of arrows. 

                - spinbuttons look correct with RTL languages

                - arrows scales with the enclosing rectangle, so eg.,
                  ranges look ok when they are very wide.

                - gtk_paint_arrow() now in general draws an arrow, not
                  the box below it.

                - update drawing of option menu tab to new arrows

        - gtkrange.c:

                - default width 16 instead of 14

                - update to gtk_paint_arrow() change


There are screenshots with normal fonts:

        http://www.daimi.au.dk/~sandmann/spinshot.png

with larger fonts:

        http://www.daimi.au.dk/~sandmann/spinshot2.png

and with small fonts:

        http://www.daimi.au.dk/~sandmann/spinshot3.png

Søren

Index: gtk/gtkmenuitem.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenuitem.c,v
retrieving revision 1.59
diff -u -p -r1.59 gtkmenuitem.c
--- gtk/gtkmenuitem.c	2001/11/13 00:53:36	1.59
+++ gtk/gtkmenuitem.c	2001/11/13 23:46:28
@@ -433,15 +433,15 @@ gtk_menu_item_size_request (GtkWidget   
   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
     {
       GtkRequisition child_requisition;
-      
+
       gtk_widget_size_request (bin->child, &child_requisition);
 
       requisition->width += child_requisition.width;
       requisition->height += child_requisition.height;
-    }
 
-  if (menu_item->submenu && menu_item->show_submenu_indicator)
-    requisition->width += 21;
+      if (menu_item->submenu && menu_item->show_submenu_indicator)
+	requisition->width += child_requisition.height;
+    }
 
   accel_width = 0;
   gtk_container_foreach (GTK_CONTAINER (menu_item),
@@ -466,22 +466,22 @@ gtk_menu_item_size_allocate (GtkWidget  
   
   widget->allocation = *allocation;
 
-  if (bin->child)
-    {
-      child_allocation.x = (GTK_CONTAINER (widget)->border_width +
-                            widget->style->xthickness +
-			    BORDER_SPACING);
-      child_allocation.y = (GTK_CONTAINER (widget)->border_width +
-			    widget->style->ythickness);
-      child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
-      child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2);
-      child_allocation.x += GTK_MENU_ITEM (widget)->toggle_size;
-      child_allocation.width -= GTK_MENU_ITEM (widget)->toggle_size;
-      if (menu_item->submenu && menu_item->show_submenu_indicator)
-	child_allocation.width -= 21;
+  child_allocation.x = (GTK_CONTAINER (widget)->border_width +
+			widget->style->xthickness +
+			BORDER_SPACING);
+  child_allocation.y = (GTK_CONTAINER (widget)->border_width +
+			widget->style->ythickness);
+  child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
+  child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2);
+  child_allocation.x += GTK_MENU_ITEM (widget)->toggle_size;
+  child_allocation.width -= GTK_MENU_ITEM (widget)->toggle_size;
+
+  menu_item->arrow_size = 0.9 * child_allocation.height;
+  if (menu_item->submenu && menu_item->show_submenu_indicator)
+    child_allocation.width -= menu_item->arrow_size;
       
-      gtk_widget_size_allocate (bin->child, &child_allocation);
-    }
+  if (bin->child)
+    gtk_widget_size_allocate (bin->child, &child_allocation);
 
   if (GTK_WIDGET_REALIZED (widget))
     gdk_window_move_resize (widget->window,
@@ -531,15 +531,20 @@ gtk_menu_item_paint (GtkWidget    *widge
 
       if (menu_item->submenu && menu_item->show_submenu_indicator)
 	{
+	  gint arrow_x, arrow_y;
+	  
 	  shadow_type = GTK_SHADOW_OUT;
 	  if (state_type == GTK_STATE_PRELIGHT)
 	    shadow_type = GTK_SHADOW_IN;
 
+	  arrow_x = x + width - menu_item->arrow_size - widget->style->xthickness - 1;
+	  arrow_y = y + height - widget->style->ythickness - menu_item->arrow_size;
+	  
 	  gtk_paint_arrow (widget->style, widget->window,
 			   state_type, shadow_type, 
 			   area, widget, "menuitem", 
 			   GTK_ARROW_RIGHT, TRUE,
-			   x + width - 15, y + height / 2 - 5, 10, 10);
+			   arrow_x, arrow_y, menu_item->arrow_size, menu_item->arrow_size);
 	}
       else if (!GTK_BIN (menu_item)->child)
 	{
Index: gtk/gtkmenuitem.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenuitem.h,v
retrieving revision 1.21
diff -u -p -r1.21 gtkmenuitem.h
--- gtk/gtkmenuitem.h	2001/11/13 00:53:36	1.21
+++ gtk/gtkmenuitem.h	2001/11/13 23:46:28
@@ -57,6 +57,7 @@ struct _GtkMenuItem
   guint16 toggle_size;
   guint16 accelerator_width;
   gchar  *accel_path;
+  guint16 arrow_size;
   
   guint show_submenu_indicator : 1;
   guint submenu_placement : 1;
Index: gtk/gtkrange.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkrange.c,v
retrieving revision 1.66
diff -u -p -r1.66 gtkrange.c
--- gtk/gtkrange.c	2001/11/02 01:13:31	1.66
+++ gtk/gtkrange.c	2001/11/13 23:46:28
@@ -281,7 +281,7 @@ gtk_range_class_init (GtkRangeClass *cla
 							     _("Width of scrollbar or scale thumb"),
 							     0,
 							     G_MAXINT,
-							     14,
+							     16,
 							     G_PARAM_READABLE));
   gtk_widget_class_install_style_property (widget_class,
 					   g_param_spec_int ("trough_border",
@@ -297,7 +297,7 @@ gtk_range_class_init (GtkRangeClass *cla
 							     _("Length of step buttons at ends"),
 							     0,
 							     G_MAXINT,
-							     14,
+							     16,
 							     G_PARAM_READABLE));
   gtk_widget_class_install_style_property (widget_class,
 					   g_param_spec_int ("stepper_spacing",
@@ -791,6 +791,7 @@ draw_stepper (GtkRange     *range,
   GtkStateType state_type;
   GtkShadowType shadow_type;
   GdkRectangle intersection;
+  GtkWidget *widget = GTK_WIDGET (range);
 
   /* More to get the right clip region than for efficiency */
   if (!gdk_rectangle_intersect (area, rect, &intersection))
@@ -809,14 +810,25 @@ draw_stepper (GtkRange     *range,
     shadow_type = GTK_SHADOW_IN;
   else
     shadow_type = GTK_SHADOW_OUT;
-  
-  gtk_paint_arrow (GTK_WIDGET (range)->style,
-                   GTK_WIDGET (range)->window,
+
+  gtk_paint_box (widget->style,
+		 widget->window,
+		 state_type, shadow_type,
+		 &intersection, widget,
+		 GTK_RANGE_GET_CLASS (range)->stepper_detail,
+		 rect->x, rect->y, rect->width, rect->height);
+
+  gtk_paint_arrow (widget->style,
+                   widget->window,
                    state_type, shadow_type, 
-                   &intersection, GTK_WIDGET (range),
+                   &intersection, widget,
                    GTK_RANGE_GET_CLASS (range)->stepper_detail,
                    arrow_type,
-                   TRUE, rect->x, rect->y, rect->width, rect->height);
+                   TRUE,
+		   rect->x + widget->style->xthickness,
+		   rect->y + widget->style->ythickness,
+		   rect->width - 2 * widget->style->xthickness,
+		   rect->height - 2 * widget->style->ythickness);
 }
 
 static gint
Index: gtk/gtkspinbutton.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkspinbutton.c,v
retrieving revision 1.78
diff -u -p -r1.78 gtkspinbutton.c
--- gtk/gtkspinbutton.c	2001/10/27 01:17:38	1.78
+++ gtk/gtkspinbutton.c	2001/11/13 23:46:28
@@ -45,6 +45,7 @@
 #define MAX_TIMER_CALLS                    5
 #define EPSILON                            1e-5
 #define	MAX_DIGITS			   20
+#define MIN_ARROW_WIDTH			   6
 
 enum {
   PROP_0,
@@ -126,11 +127,13 @@ static gint gtk_spin_button_default_inpu
 static gint gtk_spin_button_default_output (GtkSpinButton      *spin_button);
 static gint spin_button_get_arrow_size     (GtkSpinButton      *spin_button);
 static gint spin_button_get_shadow_type    (GtkSpinButton      *spin_button);
+static void spin_button_redraw             (GtkSpinButton      *spin_button);
 
 
 static GtkEntryClass *parent_class = NULL;
 static guint spinbutton_signals[LAST_SIGNAL] = {0};
 
+#define NO_ARROW 2
 
 GtkType
 gtk_spin_button_get_type (void)
@@ -420,8 +423,8 @@ gtk_spin_button_init (GtkSpinButton *spi
   spin_button->climb_rate = 0.0;
   spin_button->timer_step = 0.0;
   spin_button->update_policy = GTK_UPDATE_ALWAYS;
-  spin_button->in_child = 2;
-  spin_button->click_child = 2;
+  spin_button->in_child = NO_ARROW;
+  spin_button->click_child = NO_ARROW;
   spin_button->button = 0;
   spin_button->need_timer = FALSE;
   spin_button->timer_calls = 0;
@@ -429,6 +432,7 @@ gtk_spin_button_init (GtkSpinButton *spi
   spin_button->numeric = FALSE;
   spin_button->wrap = FALSE;
   spin_button->snap_to_ticks = FALSE;
+
   gtk_spin_button_set_adjustment (spin_button,
 	  (GtkAdjustment*) gtk_adjustment_new (0, 0, 0, 0, 0, 0));
 }
@@ -520,6 +524,8 @@ gtk_spin_button_realize (GtkWidget *widg
 		   &return_val);
   if (return_val == FALSE)
     gtk_spin_button_default_output (spin_button);
+
+  gtk_widget_queue_resize (GTK_WIDGET (spin_button));
 }
 
 static void
@@ -616,11 +622,10 @@ gtk_spin_button_size_request (GtkWidget 
       w = MIN (string_len, max_string_len) * digit_width;
       width = MAX (width, w);
       
-      requisition->width = (width + arrow_size +
-			    2 * widget->style->xthickness);
+      requisition->width = width;
     }
-  else
-    requisition->width += arrow_size + 2 * widget->style->xthickness;
+
+  requisition->width += arrow_size + 2 * widget->style->xthickness;
 }
 
 static void
@@ -628,45 +633,51 @@ gtk_spin_button_size_allocate (GtkWidget
 			       GtkAllocation *allocation)
 {
   GtkSpinButton *spin;
-  GtkAllocation child_allocation;
+  GtkAllocation entry_allocation;
+  GtkAllocation panel_allocation;
   gint arrow_size;
+  gint panel_width;
 
   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
   g_return_if_fail (allocation != NULL);
 
   spin = GTK_SPIN_BUTTON (widget);
   arrow_size = spin_button_get_arrow_size (spin);
-
-  child_allocation = *allocation;
-  if (child_allocation.width > arrow_size + 2 * widget->style->xthickness)
-    child_allocation.width -= arrow_size + 2 * widget->style->xthickness;
+  panel_width = arrow_size + 2 * widget->style->xthickness;
+  
+  widget->allocation = *allocation;
+  
+  entry_allocation = *allocation;
+  entry_allocation.width -= panel_width;
 
   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
-    child_allocation.x += arrow_size + 2 * widget->style->xthickness;
+    {
+      entry_allocation.x += panel_width;
+      panel_allocation.x = allocation->x;
+    }
+  else
+    {
+      panel_allocation.x = allocation->x + allocation->width - panel_width;
+    }
 
-  GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &child_allocation);
+  panel_allocation.width = panel_width;
+  panel_allocation.height = MIN (widget->requisition.height, allocation->height);
 
-  widget->allocation = *allocation;
+  panel_allocation.y = allocation->y + (allocation->height -
+				       panel_allocation.height) / 2;
+
+  GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &entry_allocation);
 
   if (GTK_WIDGET_REALIZED (widget))
     {
-      child_allocation.width = arrow_size + 2 * widget->style->xthickness;
-      child_allocation.height = widget->requisition.height;
-
-      if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
-	child_allocation.x = (allocation->x + allocation->width -
-			      arrow_size - 2 * widget->style->xthickness);
-      else
-	child_allocation.x = allocation->x;      
-
-      child_allocation.y = allocation->y + (allocation->height - widget->requisition.height) / 2;
-
       gdk_window_move_resize (GTK_SPIN_BUTTON (widget)->panel, 
-			      child_allocation.x,
-			      child_allocation.y,
-			      child_allocation.width,
-			      child_allocation.height); 
+			      panel_allocation.x,
+			      panel_allocation.y,
+			      panel_allocation.width,
+			      panel_allocation.height); 
     }
+
+  spin_button_redraw (spin);
 }
 
 static gint
@@ -674,44 +685,65 @@ gtk_spin_button_expose (GtkWidget      *
 			GdkEventExpose *event)
 {
   GtkSpinButton *spin;
-  gint arrow_size;
 
   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
   g_return_val_if_fail (event != NULL, FALSE);
 
   spin = GTK_SPIN_BUTTON (widget);
-  arrow_size = spin_button_get_arrow_size (spin);
 
   if (GTK_WIDGET_DRAWABLE (widget))
     {
       GtkShadowType shadow_type;
+      GdkRectangle rect;
+      gboolean sensitive = GTK_WIDGET_IS_SENSITIVE (widget);
+      
+      if (event->window != spin->panel)
+	GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
 
-      /* FIXME this seems like really broken code -
-       * why aren't we looking at event->window
-       * and acting accordingly?
+      /* we redraw the panel even if it wasn't exposed. This is
+       * because spin->panel is not a child window of widget->window,
+       * so it will not be invalidated by eg. gtk_widget_queue_draw().
        */
-
+      rect.x = 0;
+      rect.y = 0;
+      
+      gdk_window_get_size (spin->panel, &rect.width, &rect.height);
+      
       shadow_type = spin_button_get_shadow_type (spin);
-      if (shadow_type != GTK_SHADOW_NONE)
-	gtk_paint_box (widget->style, spin->panel,
-		       GTK_STATE_NORMAL, shadow_type,
-		       &event->area, widget, "spinbutton",
-		       0, 0, 
-		       arrow_size + 2 * widget->style->xthickness,
-		       widget->requisition.height); 
-      else
-	 {
-	    gdk_window_set_back_pixmap (spin->panel, NULL, TRUE);
-	    gdk_window_clear_area (spin->panel,
-                                   event->area.x, event->area.y,
-                                   event->area.width, event->area.height);
-	 }
-       gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
-       gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
-
-       GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
+      
+      gdk_window_begin_paint_rect (spin->panel, &rect);
+      gtk_paint_flat_box (widget->style, spin->panel,
+			  sensitive? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
+			  shadow_type,
+			  &event->area, widget, "spinbutton",
+			  rect.x, rect.y, rect.width, rect.height);
+      gtk_paint_shadow (widget->style, spin->panel,
+			GTK_WIDGET_STATE (widget), shadow_type,
+			&event->area, widget, "spinbutton",
+			rect.x, rect.y, rect.width, rect.height);
+      gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
+      gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
+      gdk_window_end_paint (spin->panel);
     }
+  
+  return FALSE;
+}
 
+static gboolean
+spin_button_at_limit (GtkSpinButton *spin_button,
+                     GtkArrowType   arrow)
+{
+  if (spin_button->wrap)
+    return FALSE;
+
+  if (arrow == GTK_ARROW_UP &&
+      (spin_button->adjustment->upper - spin_button->adjustment->value <= EPSILON))
+    return TRUE;
+
+  if (arrow == GTK_ARROW_DOWN &&
+      (spin_button->adjustment->value - spin_button->adjustment->lower <= EPSILON))
+    return TRUE;
+
   return FALSE;
 }
 
@@ -719,90 +751,77 @@ static void
 gtk_spin_button_draw_arrow (GtkSpinButton *spin_button, 
 			    guint          arrow)
 {
-  GtkShadowType spin_shadow_type;
   GtkStateType state_type;
   GtkShadowType shadow_type;
   GtkWidget *widget;
   gint x;
   gint y;
-  gint arrow_size;
+  gint height;
+  gint width;
 
   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
   
   widget = GTK_WIDGET (spin_button);
-  spin_shadow_type = spin_button_get_shadow_type (spin_button);
-  arrow_size = spin_button_get_arrow_size (spin_button);
 
-  if (GTK_WIDGET_DRAWABLE (spin_button))
+  if (GTK_WIDGET_DRAWABLE (widget))
     {
-      if (!spin_button->wrap &&
-	  (((arrow == GTK_ARROW_UP &&
-	  (spin_button->adjustment->upper - spin_button->adjustment->value
-	   <= EPSILON))) ||
-	  ((arrow == GTK_ARROW_DOWN &&
-	  (spin_button->adjustment->value - spin_button->adjustment->lower
-	   <= EPSILON)))))
+      width = spin_button_get_arrow_size (spin_button) + 2 * widget->style->xthickness;
+
+      height = widget->requisition.height / 2;
+
+      if (arrow == GTK_ARROW_UP)
 	{
-	  shadow_type = GTK_SHADOW_ETCHED_IN;
-	  state_type = GTK_STATE_NORMAL;
+	  x = 0;
+	  y = 0;
 	}
       else
 	{
-	  if (spin_button->in_child == arrow)
-	    {
-	      if (spin_button->click_child == arrow)
-		state_type = GTK_STATE_ACTIVE;
-	      else
-		state_type = GTK_STATE_PRELIGHT;
-	    }
-	  else
-	    state_type = GTK_STATE_NORMAL;
-	  
-	  if (spin_button->click_child == arrow)
-	    shadow_type = GTK_SHADOW_IN;
-	  else
-	    shadow_type = GTK_SHADOW_OUT;
+	  x = 0;
+	  y = (widget->requisition.height + 1) / 2;
 	}
-      if (arrow == GTK_ARROW_UP)
+
+      if (spin_button_at_limit (spin_button, arrow))
 	{
-	  if (spin_shadow_type != GTK_SHADOW_NONE)
-	    {
-	      x = widget->style->xthickness;
-	      y = widget->style->ythickness;
-	    }
-	  else
-	    {
-	      x = widget->style->xthickness - 1;
-	      y = widget->style->ythickness - 1;
-	    }
-	  gtk_paint_arrow (widget->style, spin_button->panel,
-			   state_type, shadow_type, 
-			   NULL, widget, "spinbutton",
-			   arrow, TRUE, 
-			   x, y, arrow_size,
-			   widget->requisition.height / 2 
-			   - widget->style->ythickness);
+	  shadow_type = GTK_SHADOW_OUT;
+	  state_type = GTK_STATE_INSENSITIVE;
 	}
       else
 	{
-	  if (spin_shadow_type != GTK_SHADOW_NONE)
+	  if (spin_button->click_child == arrow)
 	    {
-	      x = widget->style->xthickness;
-	      y = widget->requisition.height / 2;
+	      state_type = GTK_STATE_ACTIVE;
+	      shadow_type = GTK_SHADOW_IN;
 	    }
 	  else
 	    {
-	      x = widget->style->xthickness - 1;
-	      y = widget->requisition.height / 2 + 1;
+	      if (spin_button->in_child == arrow &&
+		  spin_button->click_child == NO_ARROW)
+		{
+		  state_type = GTK_STATE_PRELIGHT;
+		}
+	      else
+		{
+		  state_type = GTK_WIDGET_STATE (widget);
+		}
+	      
+	      shadow_type = GTK_SHADOW_OUT;
 	    }
-	  gtk_paint_arrow (widget->style, spin_button->panel,
-			   state_type, shadow_type, 
-			   NULL, widget, "spinbutton",
-			   arrow, TRUE, 
-			   x, y, arrow_size,
-			   widget->requisition.height / 2 
-			   - widget->style->ythickness);
 	}
+      
+      gtk_paint_box (widget->style, spin_button->panel,
+		     state_type, shadow_type,
+		     NULL, widget,
+		     (arrow == GTK_ARROW_UP)? "spinbutton_up" : "spinbutton_down",
+		     x, y, width, height);
+
+      gtk_paint_arrow (widget->style, spin_button->panel,
+		       state_type, shadow_type, 
+		       NULL, widget, "spinbutton",
+		       arrow, TRUE, 
+		       x + widget->style->xthickness,
+		       y + widget->style->ythickness,
+		       width - 2 * widget->style->xthickness,
+		       height - 2 * widget->style->ythickness);
     }
 }
 
@@ -825,18 +844,13 @@ gtk_spin_button_enter_notify (GtkWidget 
       gdk_window_get_pointer (spin->panel, &x, &y, NULL);
 
       if (y <= widget->requisition.height / 2)
-	{
-	  spin->in_child = GTK_ARROW_UP;
-	  if (spin->click_child == 2) 
-	    gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
-	}
+	spin->in_child = GTK_ARROW_UP;
       else
-	{
-	  spin->in_child = GTK_ARROW_DOWN;
-	  if (spin->click_child == 2) 
-	    gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
-	}
+	spin->in_child = GTK_ARROW_DOWN;
+
+      spin_button_redraw (spin);
     }
+  
   return FALSE;
 }
 
@@ -851,19 +865,9 @@ gtk_spin_button_leave_notify (GtkWidget 
 
   spin = GTK_SPIN_BUTTON (widget);
 
-  if (event->window == spin->panel && spin->click_child == 2)
-    {
-      if (spin->in_child == GTK_ARROW_UP) 
-	{
-	  spin->in_child = 2;
-	  gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
-	}
-      else
-	{
-	  spin->in_child = 2;
-	  gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
-	}
-    }
+  spin->in_child = NO_ARROW;
+  spin_button_redraw (spin);
+  
   return FALSE;
 }
 
@@ -926,7 +930,6 @@ gtk_spin_button_button_press (GtkWidget 
 	{
 	  if (!GTK_WIDGET_HAS_FOCUS (widget))
 	    gtk_widget_grab_focus (widget);
-	  gtk_grab_add (widget);
 	  spin->button = event->button;
 	  
 	  if (GTK_ENTRY (widget)->editable)
@@ -961,7 +964,7 @@ gtk_spin_button_button_press (GtkWidget 
 			 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
 		    }
 		}
-	      gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
+	      spin_button_redraw (spin);
 	    }
 	  else 
 	    {
@@ -992,7 +995,7 @@ gtk_spin_button_button_press (GtkWidget 
 			 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
 		    }
 		}
-	      gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
+	      spin_button_redraw (spin);
 	    }
 	  return TRUE;
 	}
@@ -1053,11 +1056,10 @@ gtk_spin_button_button_release (GtkWidge
 		}
 	    }
 	}		  
-      gtk_grab_remove (widget);
       click_child = spin->click_child;
-      spin->click_child = 2;
+      spin->click_child = NO_ARROW;
       spin->button = 0;
-      gtk_spin_button_draw_arrow (spin, click_child);
+      spin_button_redraw (spin);
       return TRUE;
     }
   else
@@ -1071,37 +1073,30 @@ gtk_spin_button_motion_notify (GtkWidget
 			       GdkEventMotion *event)
 {
   GtkSpinButton *spin;
+  gint y;
 
   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
   g_return_val_if_fail (event != NULL, FALSE);
 
   spin = GTK_SPIN_BUTTON (widget);
-  
-  if (spin->button)
-    return FALSE;
 
+  gdk_window_get_pointer (spin->panel, NULL, &y, NULL);
+  
   if (event->window == spin->panel)
     {
-      gint y;
-
-      y = event->y;
-      if (event->is_hint)
-	gdk_window_get_pointer (spin->panel, NULL, &y, NULL);
-
       if (y <= widget->requisition.height / 2 && 
 	  spin->in_child == GTK_ARROW_DOWN)
 	{
 	  spin->in_child = GTK_ARROW_UP;
-	  gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
-	  gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
+	  spin_button_redraw (spin);
 	}
       else if (y > widget->requisition.height / 2 && 
 	  spin->in_child == GTK_ARROW_UP)
 	{
 	  spin->in_child = GTK_ARROW_DOWN;
-	  gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
-	  gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
+	  spin_button_redraw (spin);
 	}
+      
       return FALSE;
     }
 	  
@@ -1168,8 +1163,7 @@ gtk_spin_button_value_changed (GtkAdjust
   gtk_signal_emit (GTK_OBJECT (spin_button), 
 		   spinbutton_signals[VALUE_CHANGED]);
 
-  gtk_spin_button_draw_arrow (spin_button, GTK_ARROW_UP);
-  gtk_spin_button_draw_arrow (spin_button, GTK_ARROW_DOWN);
+  spin_button_redraw (spin_button);
   
   g_object_notify (G_OBJECT (spin_button), "value");
 }
@@ -1468,6 +1462,8 @@ gtk_spin_button_real_spin (GtkSpinButton
 
   if (fabs (new_value - adj->value) > EPSILON)
     gtk_adjustment_set_value (adj, new_value);
+
+  spin_button_redraw (spin_button);
 }
 
 static gint
@@ -1976,8 +1972,11 @@ static gint
 spin_button_get_arrow_size (GtkSpinButton *spin_button)
 {
   gint size = pango_font_description_get_size (GTK_WIDGET (spin_button)->style->font_desc);
-  
-  return PANGO_PIXELS (size);
+  gint arrow_size;
+
+  arrow_size = MAX (PANGO_PIXELS (size), MIN_ARROW_WIDTH);
+
+  return arrow_size - arrow_size % 2; /* force even */
 }
 
 /**
@@ -2148,6 +2147,8 @@ gtk_spin_button_update (GtkSpinButton *s
   else if (return_val == GTK_INPUT_ERROR)
     error = 1;
 
+  spin_button_redraw (spin_button);
+
   if (spin_button->update_policy == GTK_UPDATE_ALWAYS)
     {
       if (val < spin_button->adjustment->lower)
@@ -2181,3 +2182,20 @@ gtk_spin_button_update (GtkSpinButton *s
     }
 }
 
+static void
+spin_button_redraw (GtkSpinButton *spin_button)
+{
+  GtkWidget *widget;
+
+  widget = GTK_WIDGET (spin_button);
+
+  if (GTK_WIDGET_DRAWABLE (widget))
+    {
+      gtk_widget_queue_draw (widget);
+
+      /* We must invalidate the panel window ourselves, because it
+       * is not a child of widget->window
+       */
+      gdk_window_invalidate_rect (spin_button->panel, NULL, TRUE);
+    }
+}        
Index: gtk/gtkstyle.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkstyle.c,v
retrieving revision 1.87
diff -u -p -r1.87 gtkstyle.c
--- gtk/gtkstyle.c	2001/10/27 00:41:11	1.87
+++ gtk/gtkstyle.c	2001/11/13 23:46:28
@@ -1955,19 +1955,19 @@ draw_spin_entry_shadow (GtkStyle      *s
 }
 
 static void
-draw_spinbutton_shadow (GtkStyle      *style,
-			GdkWindow     *window,
-			GtkStateType   state,
-			GdkRectangle  *area,
-			gint           x,
-			gint           y,
-			gint           width,
-			gint           height)
+draw_spinbutton_shadow (GtkStyle        *style,
+			GdkWindow       *window,
+			GtkStateType     state,
+			GtkShadowType    shadow_type,
+			GtkTextDirection direction,
+			GdkRectangle    *area,
+			gint             x,
+			gint             y,
+			gint             width,
+			gint             height)
 {
-  gint y_middle = y + height / 2;
-
   sanitize_size (window, &width, &height);
-  
+
   if (area)
     {
       gdk_gc_set_clip_rectangle (style->black_gc, area);
@@ -1975,33 +1975,42 @@ draw_spinbutton_shadow (GtkStyle      *s
       gdk_gc_set_clip_rectangle (style->dark_gc[state], area);
       gdk_gc_set_clip_rectangle (style->light_gc[state], area);
     }
+
+  if (direction == GTK_TEXT_DIR_LTR)
+    {
+      gdk_draw_line (window, style->dark_gc[state],
+		     x, y, x + width - 1, y);
+      gdk_draw_line (window, style->black_gc,
+		     x, y + 1, x + width - 2, y + 1);
+      gdk_draw_line (window, style->black_gc,
+		     x + width - 2, y + 2, x + width - 2, y + height - 3);
+      gdk_draw_line (window, style->light_gc[state],
+		     x + width - 1, y + 1, x + width - 1, y + height - 2);
+      gdk_draw_line (window, style->light_gc[state],
+		     x, y + height - 1, x + width - 1, y + height - 1);
+      gdk_draw_line (window, style->bg_gc[state],
+		     x, y + height - 2, x + width - 2, y + height - 2);
+      gdk_draw_line (window, style->black_gc,
+		     x, y + 2, x, y + height - 3);
+    }
+  else
+    {
+      gdk_draw_line (window, style->dark_gc[state],
+		     x, y, x + width - 1, y);
+      gdk_draw_line (window, style->dark_gc[state],
+		     x, y + 1, x, y + height - 1);
+      gdk_draw_line (window, style->black_gc,
+		     x + 1, y + 1, x + width - 1, y + 1);
+      gdk_draw_line (window, style->black_gc,
+		     x + 1, y + 2, x + 1, y + height - 2);
+      gdk_draw_line (window, style->black_gc,
+		     x + width - 1, y + 2, x + width - 1, y + height - 3);
+      gdk_draw_line (window, style->light_gc[state],
+		     x + 1, y + height - 1, x + width - 1, y + height - 1);
+      gdk_draw_line (window, style->bg_gc[state],
+		     x + 2, y + height - 2, x + width - 1, y + height - 2);
+    }
   
-  gdk_draw_line (window, style->black_gc,
-		 x, y + 2, x, y + height - 3);
-  gdk_draw_line (window, style->black_gc,
-		 x, y + 1, x + width - 2, y + 1);
-  gdk_draw_line (window, style->black_gc,
-		 x + width - 2, y + 2, x + width - 2, y + height - 3);
-  
-  gdk_draw_line (window, style->bg_gc[state],
-		 x, y + height - 2, x + width - 2, y + height - 2);
-
-  gdk_draw_line (window, style->dark_gc[state],
-		 x, y, x + width - 1, y);
-  gdk_draw_line (window, style->dark_gc[state],
-		 x + 1, y_middle - 1, x + width - 3, y_middle - 1);
-  gdk_draw_line (window, style->dark_gc[state],
-		 x + 1, y + height - 3, x + width - 3, y + height - 3);
-
-  gdk_draw_line (window, style->light_gc[state],
-		 x + 1, y + 2, x + width - 3, y + 2);
-  gdk_draw_line (window, style->light_gc[state],
-		 x + 1, y_middle, x + width - 3, y_middle);
-  gdk_draw_line (window, style->light_gc[state],
-		 x + width - 1, y + 1, x + width - 1, y + height - 1);
-  gdk_draw_line (window, style->light_gc[state],
-		 x, y + height - 1, x + width - 2, y + height - 1);
-      
   if (area)
     {
       gdk_gc_set_clip_rectangle (style->black_gc, NULL);
@@ -2050,21 +2059,16 @@ gtk_default_draw_shadow (GtkStyle      *
 			    x, y, width, height);
 	  return;
 	}
-      else if (widget && GTK_IS_SPIN_BUTTON (widget) &&
-	       detail && strcmp (detail, "entry") == 0)
-	{
-	  draw_spin_entry_shadow (style, window, state_type, area,
-				  x, y, width, height);
-	  return;
-	}
-      else if (widget && GTK_IS_SPIN_BUTTON (widget) &&
-	       detail && strcmp (detail, "spinbutton") == 0)
-	{
-	  draw_spinbutton_shadow (style, window, state_type,
-				  area, x, y, width, height);
-	  return;
-	}
     }
+
+  if (widget && GTK_IS_SPIN_BUTTON (widget) &&
+      detail && strcmp (detail, "spinbutton") == 0)
+    {
+      draw_spinbutton_shadow (style, window, state_type, shadow_type,
+			      gtk_widget_get_direction (widget), area, x, y, width, height);
+
+      return;
+    }
   
   sanitize_size (window, &width, &height);
   
@@ -2286,6 +2290,49 @@ gtk_default_draw_shadow (GtkStyle      *
       
       break;
     }
+  if (shadow_type == GTK_SHADOW_IN &&
+      widget && GTK_IS_SPIN_BUTTON (widget) &&
+      detail && strcmp (detail, "entry") == 0)
+    {
+      if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+	{
+	  gdk_draw_line (window,
+			 style->base_gc[state_type],
+			 x + width - 1, y + 2,
+			 x + width - 1, y + height - 3);
+	  gdk_draw_line (window,
+			 style->base_gc[state_type],
+			 x + width - 2, y + 2,
+			 x + width - 2, y + height - 3);
+	  gdk_draw_point (window,
+			  style->black_gc,
+			  x + width - 1, y + 1);
+	  gdk_draw_point (window,
+			  style->bg_gc[state_type],
+			  x + width - 1, y + height - 2);
+	}
+      else
+	{
+	  gdk_draw_line (window,
+			 style->base_gc[state_type],
+			 x, y + 2,
+			 x, y + height - 3);
+	  gdk_draw_line (window,
+			 style->base_gc[state_type],
+			 x + 1, y + 2,
+			 x + 1, y + height - 3);
+	  gdk_draw_point (window,
+			  style->black_gc,
+			  x, y + 1);
+	  gdk_draw_line (window,
+			 style->bg_gc[state_type],
+			 x, y + height - 2,
+			 x + 1, y + height - 2);
+	  gdk_draw_point (window,
+			  style->light_gc[state_type],
+			  x, y + height - 1);
+	}
+    }
   if (area)
     {
       gdk_gc_set_clip_rectangle (gc1, NULL);
@@ -2540,6 +2587,66 @@ draw_harrow (GdkWindow     *window,
 }
 
 static void
+calculate_arrow_size (GtkArrowType  arrow_type,
+		      gint         *x,
+		      gint         *y,
+		      gint         *width,
+		      gint         *height,
+		      gdouble       fraction)
+{
+  gint w = *width * fraction;
+  gint h = *height * fraction;
+
+  switch (arrow_type)
+    {
+    case GTK_ARROW_UP:
+    case GTK_ARROW_DOWN:
+      w += (w % 2) - 1;
+      h = (w / 2 + 1);
+
+      if (h > *height)
+	{
+	  h = *height - 2;
+	  w = 2 * h - 1;
+	}
+
+      *x += (*width - w) / 2;
+      if (arrow_type == GTK_ARROW_DOWN)
+	*y += (*height - h + 1) / 2;
+      else
+	*y += (*height - h - 1) / 2;
+
+      *height = h;
+      *width = w;
+      break;
+
+    case GTK_ARROW_RIGHT:
+    case GTK_ARROW_LEFT:
+      h += (h % 2) - 1;
+      w = (h / 2 + 1);
+
+      if (w > *width)
+	{
+	  w = *width - 2;
+	  h = 2 * w - 1;
+	}
+
+      if (arrow_type == GTK_ARROW_LEFT)
+	*x += (*width - w - 1) / 2;
+      else
+	*x += (*width - w + 1) / 2;
+      *y += (*height - h) / 2;
+
+      *height = h;
+      *width = w;
+      break;
+    default:
+      /* should not be reached */
+      break;
+    }
+}
+
+static void
 gtk_default_draw_arrow (GtkStyle      *style,
 			GdkWindow     *window,
 			GtkStateType   state,
@@ -2554,76 +2661,46 @@ gtk_default_draw_arrow (GtkStyle      *s
 			gint           width,
 			gint           height)
 {
-  sanitize_size (window, &width, &height);
+  const gdouble ARROW_FRACTION = 0.6;
   
-  if (detail && strcmp (detail, "spinbutton") == 0)
-    {
-      int hpad, vpad;
-      int my_height = height;
-      int my_width = width;
-      int vpad_add = 0;
+  sanitize_size (window, &width, &height);
 
-      if (my_height > my_width)
+  if (strcmp (detail, "spinbutton") == 0)
+    {
+      if (arrow_type == GTK_ARROW_UP)
 	{
-	  vpad_add = (my_height - my_width) / 2;
-	  my_height = my_width;
+	  x -= 1;
+	  y += 1;
+	  width += 1;
 	}
-
-      hpad = my_width / 4;
-
-      if (hpad < 4)
-	hpad = 4;
-
-      vpad = 2 * hpad - 1;
-
-      x += hpad / 2;
-      y += vpad / 2;
-
-      y += vpad_add;
+      else /* arrow_type == GTK_ARROW_DOWN */
+	{
+	  x -= 1;
+	  y -= 1;
+	  width += 1;
+	}
 
-      draw_varrow (window, style->fg_gc[state], shadow, area, arrow_type,
-		   x, y, my_width - hpad, my_height - vpad);
+      if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+	x += 1;
     }
-  else if (detail && strcmp (detail, "vscrollbar") == 0)
+
+  calculate_arrow_size (arrow_type, &x, &y, &width, &height, ARROW_FRACTION);
+  
+  if (arrow_type == GTK_ARROW_UP || arrow_type == GTK_ARROW_DOWN)
     {
-      gtk_paint_box (style, window, state, shadow, area,
-		     widget, detail, x, y, width, height);
-      
-      x += (width - 7) / 2;
-      y += (height - 5) / 2;
-      
+      if (state == GTK_STATE_INSENSITIVE)
+	draw_varrow (window, style->white_gc, shadow, area, arrow_type,
+		     x + 1, y + 1, width, height);
       draw_varrow (window, style->fg_gc[state], shadow, area, arrow_type,
-		   x, y, 7, 5);
+		   x, y, width, height);
     }
-  else if (detail && strcmp (detail, "hscrollbar") == 0)
-    {
-      gtk_paint_box (style, window, state, shadow, area,
-		     widget, detail, x, y, width, height);
-      
-      y += (height - 7) / 2;
-      x += (width - 5) / 2;
-
-      draw_harrow (window, style->fg_gc[state], shadow, area, arrow_type,
-		   x, y, 5, 7);
-    }
   else
     {
-      if (arrow_type == GTK_ARROW_UP || arrow_type == GTK_ARROW_DOWN)
-	{
-	  x += (width - 7) / 2;
-	  y += (height - 5) / 2;
-	  
-	  draw_varrow (window, style->fg_gc[state], shadow, area, arrow_type,
-		       x, y, 7, 5);
-	}
-      else
-	{
-	  x += (width - 5) / 2;
-	  y += (height - 7) / 2;
-	  
-	  draw_harrow (window, style->fg_gc[state], shadow, area, arrow_type,
-		       x, y, 5, 7);
-	}
+      if (state == GTK_STATE_INSENSITIVE)
+	draw_harrow (window, style->white_gc, shadow, area, arrow_type,
+		     x + 1, y + 1, width, height);
+      draw_harrow (window, style->fg_gc[state], shadow, area, arrow_type,
+		   x, y, width, height);
     }
 }
 
@@ -2840,10 +2917,41 @@ gtk_default_draw_box (GtkStyle      *sty
 		      gint           width,
 		      gint           height)
 {
+  gboolean is_spinbutton_box = FALSE;
+  
   g_return_if_fail (GTK_IS_STYLE (style));
   g_return_if_fail (window != NULL);
   
   sanitize_size (window, &width, &height);
+
+  if (widget && GTK_IS_SPIN_BUTTON (widget) && detail)
+    {
+      if (strcmp (detail, "spinbutton_up") == 0)
+	{
+	  y += 2;
+	  width -= 3;
+	  height -= 2;
+
+	  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+	    x += 2;
+	  else
+	    x += 1;
+
+	  is_spinbutton_box = TRUE;
+	}
+      else if (strcmp (detail, "spinbutton_down") == 0)
+	{
+	  width -= 3;
+	  height -= 2;
+
+	  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+	    x += 2;
+	  else
+	    x += 1;
+
+	  is_spinbutton_box = TRUE;
+	}
+    }
   
   if (!style->bg_pixmap[state_type] || 
       GDK_IS_PIXMAP (window))
@@ -2860,7 +2968,35 @@ gtk_default_draw_box (GtkStyle      *sty
     gtk_style_apply_default_background (style, window,
                                         widget && !GTK_WIDGET_NO_WINDOW (widget),
                                         state_type, area, x, y, width, height);
-  
+
+  if (is_spinbutton_box)
+    {
+      GdkGC *upper_gc;
+      GdkGC *lower_gc;
+      
+      lower_gc = style->dark_gc[state_type];
+      if (shadow_type == GTK_SHADOW_OUT)
+	upper_gc = style->light_gc[state_type];
+      else
+	upper_gc = style->dark_gc[state_type];
+
+      if (area)
+	{
+	  gdk_gc_set_clip_rectangle (style->dark_gc[state_type], area);
+	  gdk_gc_set_clip_rectangle (style->light_gc[state_type], area);
+	}
+      
+      gdk_draw_line (window, upper_gc, x, y, x + width - 1, y);
+      gdk_draw_line (window, lower_gc, x, y + height - 1, x + width - 1, y + height - 1);
+
+      if (area)
+	{
+	  gdk_gc_set_clip_rectangle (style->dark_gc[state_type], NULL);
+	  gdk_gc_set_clip_rectangle (style->light_gc[state_type], NULL);
+	}
+      return;
+    }
+
   gtk_paint_shadow (style, window, state_type, shadow_type, area, widget, detail,
                     x, y, width, height);
 
@@ -3149,18 +3285,39 @@ gtk_default_draw_tab (GtkStyle      *sty
 		      gint           width,
 		      gint           height)
 {
+#define ARROW_SPACE 4
+
   GtkRequisition indicator_size;
   GtkBorder indicator_spacing;
+  gint arrow_height;
   
   option_menu_get_props (widget, &indicator_size, &indicator_spacing);
 
+  indicator_size.width += (indicator_size.width % 2) - 1;
+  arrow_height = indicator_size.width / 2 + 1;
+
   x += (width - indicator_size.width) / 2;
-  y += (height - indicator_size.height) / 2 - 1;
+  y += (height - (2 * arrow_height + ARROW_SPACE)) / 2;
+
+  if (state_type == GTK_STATE_INSENSITIVE)
+    {
+      draw_varrow (window, style->white_gc, shadow_type, area,
+		   GTK_ARROW_UP, x + 1, y + 1,
+		   indicator_size.width, arrow_height);
+      
+      draw_varrow (window, style->white_gc, shadow_type, area,
+		   GTK_ARROW_DOWN, x + 1, y + arrow_height + ARROW_SPACE + 1,
+		   indicator_size.width, arrow_height);
+    }
 
-  draw_varrow (window, style->black_gc, shadow_type, area, GTK_ARROW_UP,
-	       x, y, indicator_size.width, 5);
-  draw_varrow (window, style->black_gc, shadow_type, area, GTK_ARROW_DOWN,
-	       x, y + 8, indicator_size.width, 5);
+  draw_varrow (window, style->fg_gc[state_type], shadow_type, area,
+	       GTK_ARROW_UP, x, y,
+	       indicator_size.width, arrow_height);
+ 
+  
+  draw_varrow (window, style->fg_gc[state_type], shadow_type, area,
+	       GTK_ARROW_DOWN, x, y + arrow_height + ARROW_SPACE,
+	       indicator_size.width, arrow_height);
 }
 
 static void 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]