[gtk+-2] (patch) Fine-grained user control over GtkRange value



Hi.

The patch below brings additional behavior to GtkRange objects (i.e.,
GtkHScale, GtkVScale, GtkHScrollbar, and GtkVScrollbar).  This happens
when their slider is either dragged with mouse button 1, or warped then
dragged with mouse button 2.  The original behavior is exactly the same
as long the mouse pointer is kept within the range's trough or slider.
The additional behavior is invoked by moving the mouse pointer laterally
outside these regions.  The more so, the more control over the range's
value becomes fine-grained.  This is especially useful when viewing
large documents where dragging a conventional scrollbar just one pixel
down already produces too much movement (i.e., change in range value).
The change in behavior is smooth and intuitive and respects availability
of the traditional behavior for the user's usual interaction.

The main ideas behind the patch are that

	1) This interaction with the range fundamentally aims to modify
	its value, rather than move its slider.  (Thus the change in
	the struct itself, for recording the initial state.  Also,
	notice how this cleans up the gtk_range_button_press() function.)

	2) The value's precision may be much better (smaller than 1,
	numerically) than what corresponds to a 1-pixel move of the
	slider.  The state of the range is recorded by its high-precision
	value, not by its rendering on the display (i.e., the slider's
	position).

	3) This specific interaction with the range (dragging) aims to
	_modify_ the range's value _relative to_ its former self, rather
	than to just set a completely recalculated value from its lower
	precision rendering on the display.  (Thus the new
	delta_coord_to_value() function.)

(Maybe other interactions with the range could be modified to respect the
same principles and affect the value; this remains to be investigated.)

The testgtk program's "range controls" popup has been modified to show
3 decimal digits right of the decimal point on its GtkHScale, instead
of just one, so that the effect of the patch can be seen.  Observe how
the 0-to-100 value can now be changed in the thousandths rather than
just in the tenths by using either the GtkHScale or the GtkHScrollbar
on the popup.

The patch below is for gtk+-2.  If there is interest, I also have a
back-ported patch for gtk+-1 that achieves mostly the same behavior.

I believe the code is clean and respects the surrounding coding style.

Thank you.


========================================================================
--- tests/testgtk.c.orig	Fri Mar  8 01:36:48 2002
+++ tests/testgtk.c	Tue Sep 24 18:53:18 2002
@@ -7253,7 +7253,7 @@ create_range_controls (void)
       scale = gtk_hscale_new (GTK_ADJUSTMENT (adjustment));
       gtk_widget_set_usize (GTK_WIDGET (scale), 150, -1);
       gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
-      gtk_scale_set_digits (GTK_SCALE (scale), 1);
+      gtk_scale_set_digits (GTK_SCALE (scale), 3);
       gtk_scale_set_draw_value (GTK_SCALE (scale), TRUE);
       gtk_box_pack_start (GTK_BOX (box2), scale, TRUE, TRUE, 0);
       gtk_widget_show (scale);
--- gtk/gtkrange.h.orig	Fri Mar  1 22:39:52 2002
+++ gtk/gtkrange.h	Tue Sep 24 19:41:38 2002
@@ -94,7 +94,7 @@ struct _GtkRange
   guint update_pending : 1;        /* need to emit value_changed */
   GtkRangeLayout *layout;
   GtkRangeStepTimer *timer;
-  gint slide_initial_slider_position;
+  gdouble slide_initial_value;
   gint slide_initial_coordinate;
   guint update_timeout_id;
   GdkWindow *event_window;
--- gtk/gtkrange.c.orig	Tue Sep 24 01:43:20 2002
+++ gtk/gtkrange.c	Tue Sep 24 20:51:11 2002
@@ -1159,6 +1159,38 @@ coord_to_value (GtkRange *range,
   return value;
 }
 
+static gdouble
+delta_coord_to_value (GtkRange *range,
+		      gdouble   reference_value,
+		      gdouble   delta_coord)
+{
+  gdouble value;
+  gint denominator;
+  gdouble max_value = range->adjustment->upper - range->adjustment->page_size;
+  
+  if (range->orientation == GTK_ORIENTATION_VERTICAL)
+    denominator = range->layout->trough.height - range->layout->slider.height;
+  else
+    denominator = range->layout->trough.width - range->layout->slider.width;
+
+  if (denominator <= 0)
+    value = max_value;
+  else
+    value = (reference_value +
+	     delta_coord * (max_value -
+			    range->adjustment->lower) / (gdouble) denominator);
+
+  if (should_invert (range))
+    value = range->adjustment->lower + max_value - value;
+
+  if (value < range->adjustment->lower)
+    value = range->adjustment->lower;
+  else if (value > max_value)
+    value = max_value;
+
+  return value;
+}
+
 static gint
 gtk_range_button_press (GtkWidget      *widget,
 			GdkEventButton *event)
@@ -1226,7 +1258,9 @@ gtk_range_button_press (GtkWidget      *
             event->button == 2) ||
            range->layout->mouse_location == MOUSE_SLIDER)
     {
-      gboolean need_value_update = FALSE;
+      range->slide_initial_value = range->adjustment->value;
+      range->slide_initial_coordinate =
+	range->orientation == GTK_ORIENTATION_VERTICAL ? event->y : event->x;
 
       /* Any button can be used to drag the slider, but you can start
        * dragging the slider with a trough click using button 2;
@@ -1256,26 +1290,11 @@ gtk_range_button_press (GtkWidget      *
 	  range->need_recalc = TRUE;
           gtk_range_calc_layout (range, new_value);
 
-	  /* defer adjustment updates to update_slider_position() in order
-	   * to keep pixel quantisation
-	   */
-	  need_value_update = TRUE;
+	  range->slide_initial_value = new_value;
+
+	  update_slider_position (range, event->x, event->y);
         }
       
-      if (range->orientation == GTK_ORIENTATION_VERTICAL)
-        {
-          range->slide_initial_slider_position = range->layout->slider.y;
-          range->slide_initial_coordinate = event->y;
-        }
-      else
-        {
-          range->slide_initial_slider_position = range->layout->slider.x;
-          range->slide_initial_coordinate = event->x;
-        }
-
-      if (need_value_update)
-        update_slider_position (range, event->x, event->y);
-
       range_grab_add (range, MOUSE_SLIDER, event->button);
       
       return TRUE;
@@ -1291,17 +1310,39 @@ update_slider_position (GtkRange *range,
                         gint      mouse_y)
 {
   gint delta;
-  gint c;
+  gint thickness;
+  gint t_coord;
+  gdouble delta_coord;
   gdouble new_value;
   
   if (range->orientation == GTK_ORIENTATION_VERTICAL)
-    delta = mouse_y - range->slide_initial_coordinate;
+    {
+      delta = mouse_y;
+      thickness = range->layout->trough.width;
+      t_coord = mouse_x - range->layout->trough.x;
+    }
   else
-    delta = mouse_x - range->slide_initial_coordinate;
+    {
+      delta = mouse_x;
+      thickness = range->layout->trough.height;
+      t_coord = mouse_y - range->layout->trough.y;
+    }
+
+  delta -= range->slide_initial_coordinate;
+  if (t_coord < 0)
+    t_coord = 1 + thickness - t_coord;
 
-  c = range->slide_initial_slider_position + delta;
+  delta_coord = delta;
+  if (t_coord >= thickness) /* fine-grained control if laterally outside */
+    {
+      thickness /= 2;
+      t_coord -= thickness;
+
+      delta_coord *= thickness;
+      delta_coord /= t_coord;
+    }
 
-  new_value = coord_to_value (range, c);
+  new_value = delta_coord_to_value (range, range->slide_initial_value, delta_coord);
   
   gtk_range_internal_set_value (range, new_value);
 }
========================================================================



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