--- gtk+-2.6.4/gtk/gtkscrolledwindow.c 2004-08-09 19:59:52.000000000 +0300 +++ gtk+-2.6.4/gtk/gtkscrolledwindow.c 2005-04-06 16:19:37.898776872 +0300 @@ -289,6 +289,13 @@ DEFAULT_SCROLLBAR_SPACING, G_PARAM_READABLE)); + gtk_widget_class_install_style_property (widget_class, + g_param_spec_boolean ("scrollbar_dislocation", + P_("Scrollbar dislocation"), + P_("Flag for having scrollbar at the outer border or container padding instead of at the inner border"), + FALSE, + G_PARAM_READABLE)); + signals[SCROLL_CHILD] = g_signal_new ("scroll_child", G_TYPE_FROM_CLASS (object_class), @@ -1062,6 +1069,73 @@ } } +static gdouble +gtk_scrolled_window_get_focus_movement (GtkScrolledWindow *scrolled_window) +{ + GtkWidget *focus_child; + GtkRange *range; + GtkAdjustment *adj; + gdouble value, new_value; + gint x, y; + + focus_child = GTK_CONTAINER(scrolled_window)->focus_child; + if (focus_child == NULL) + return 0; + + while (GTK_IS_CONTAINER (focus_child) && + GTK_CONTAINER (focus_child)->focus_child) + { + focus_child = GTK_CONTAINER (focus_child)->focus_child; + } + + range = GTK_RANGE (scrolled_window->vscrollbar); + adj = range->adjustment; + value = gtk_adjustment_get_value (adj); + + gtk_widget_translate_coordinates (focus_child->parent, + GTK_WIDGET(scrolled_window), + focus_child->allocation.x, + focus_child->allocation.y, &x, &y); + + if (y < 0) + { + /* scroll up */ + new_value = value + y; + if (new_value < adj->lower) + new_value = adj->lower; + } + else if (y + focus_child->allocation.height > adj->page_size) + { + /* scroll down */ + new_value = value + y + focus_child->allocation.height - adj->page_size; + if (new_value > adj->upper - adj->page_size) + new_value = adj->upper - adj->page_size; + } + else + { + new_value = value; + } + + return new_value - value; +} + +static void +gtk_scrolled_window_scroll_to_focus (GtkScrolledWindow *scrolled_window) +{ + GtkAdjustment *adj; + gdouble diff; + + diff = gtk_scrolled_window_get_focus_movement (scrolled_window); + if (diff != 0) + { + adj = GTK_RANGE (scrolled_window->vscrollbar)->adjustment; + + gtk_adjustment_set_value (adj, gtk_adjustment_get_value (adj) + diff); + gtk_scrolled_window_set_vadjustment (scrolled_window, + GTK_ADJUSTMENT (adj)); + } +} + static void gtk_scrolled_window_size_allocate (GtkWidget *widget, GtkAllocation *allocation) @@ -1071,17 +1145,32 @@ GtkAllocation relative_allocation; GtkAllocation child_allocation; gint scrollbar_spacing; - + gboolean is_focus_visible, dislocate; + gint dislocation; + g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget)); g_return_if_fail (allocation != NULL); scrolled_window = GTK_SCROLLED_WINDOW (widget); bin = GTK_BIN (scrolled_window); + is_focus_visible = + gtk_scrolled_window_get_focus_movement (scrolled_window) == 0; + scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window); widget->allocation = *allocation; + /* See how much scrollbar needs be "dislocated" (to get it to the other + * edge of the border). Does not apply to all occasions. */ + gtk_widget_style_get (GTK_WIDGET (scrolled_window), + "scrollbar_dislocation", &dislocate, + NULL); + if (dislocate) + dislocation = GTK_CONTAINER (scrolled_window)->border_width; + else + dislocation = 0; + if (scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS) scrolled_window->hscrollbar_visible = TRUE; else if (scrolled_window->hscrollbar_policy == GTK_POLICY_NEVER) @@ -1150,10 +1239,12 @@ child_allocation.y = (relative_allocation.y + relative_allocation.height + scrollbar_spacing + + dislocation + (scrolled_window->shadow_type == GTK_SHADOW_NONE ? 0 : widget->style->ythickness)); else - child_allocation.y = GTK_CONTAINER (scrolled_window)->border_width; + child_allocation.y = GTK_CONTAINER (scrolled_window)->border_width - + dislocation; child_allocation.width = relative_allocation.width; child_allocation.height = hscrollbar_requisition.height; @@ -1189,10 +1280,12 @@ child_allocation.x = (relative_allocation.x + relative_allocation.width + scrollbar_spacing + + dislocation + (scrolled_window->shadow_type == GTK_SHADOW_NONE ? 0 : widget->style->xthickness)); else - child_allocation.x = GTK_CONTAINER (scrolled_window)->border_width; + child_allocation.x = GTK_CONTAINER (scrolled_window)->border_width - + dislocation; child_allocation.y = relative_allocation.y; child_allocation.width = vscrollbar_requisition.width; @@ -1207,6 +1300,9 @@ } gtk_widget_size_allocate (scrolled_window->vscrollbar, &child_allocation); + + if (is_focus_visible) + gtk_scrolled_window_scroll_to_focus (scrolled_window); } else if (GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar)) gtk_widget_hide (scrolled_window->vscrollbar);