Commit 17097258 authored by Jan Djärv's avatar Jan Djärv
Browse files

New approach to scrolling and scroll bars for better redraw and smoother

scroll bar behaviour.
parent 8cb9dfbf
2003-03-18 Jan Dj,Ad(Brv <jan.h.d@swipnet.se>
* xterm.c (xg_scroll_callback): Remove xg_ignore_next_thumb.
* gtkutil.c (xg_initialize): Remove xg_ignore_next_thumb.
2003-03-17 Jan Dj,Ad(Brv <jan.h.d@swipnet.se>
* gtkutil.c: Removed handle_fixed_child, struct xg_last_sb_pos.
(xg_resize_widgets): Don't call foreach(handle_fixed_child).
(xg_gtk_scroll_destroy): Remove free of struct xg_last_sb_pos.
(scroll_bar_button_cb): Set bar->dragging to NIL on button release.
(xg_create_scroll_bar): Pass bar to button event callback.
(xg_find_top_left_in_fixed): New function.
(xg_update_scrollbar_pos): Don't call gdk_window_clear on
whole scroll bar area. Get old position with
xg_find_top_left_in_fixed, calculate and only clear needed areas.
(xg_set_toolkit_scroll_bar_thumb): Do not adjust scroll bar if
dragging is in progress. Calculate whole as for Motif.
Remove code that saved last values. Call gtk_range functions to
set scroll bar sizes.
* gtkutil.h: Removed xg_ignore_next_thumb.
2003-03-17 Juanma Barranquero <lektu@terra.es>
* makefile.w32-in ($(BLD)/xdisp.$(O)): Add dependency on blockinput.h
......
......@@ -37,17 +37,6 @@ Boston, MA 02111-1307, USA. */
(PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
/* Key to save a struct xg_last_sb_pos in the scroll bars. */
#define XG_LAST_SB_POS "emacs_last_sb_pos"
/* Use this in scroll bars to keep track of when redraw is really needed. */
struct xg_last_sb_pos
{
int portion, position, whole;
};
/***********************************************************************
Utility functions
......@@ -298,24 +287,6 @@ xg_resize_outer_widget (f, columns, rows)
gdk_window_process_all_updates ();
}
/* When the Emacs frame is resized, we must call this function for each
child of our GtkFixed widget. The children are scroll bars and
we invalidate the last position data here so it will be properly
redrawn later when xg_update_scrollbar_pos is called.
W is the child widget.
CLIENT_DATA is not used. */
static handle_fixed_child (w, client_data)
GtkWidget *w;
gpointer client_data;
{
struct xg_last_sb_pos *last_pos
= g_object_get_data (G_OBJECT (w), XG_LAST_SB_POS);
if (last_pos)
{
last_pos->portion = last_pos->position = last_pos->whole = .1;
}
}
/* This gets called after the frame F has been cleared. Since that is
done with X calls, we need to redraw GTK widget (scroll bars). */
void
......@@ -326,9 +297,6 @@ xg_frame_cleared (f)
if (wfixed)
{
gtk_container_foreach (GTK_CONTAINER (wfixed),
(GtkCallback) handle_fixed_child,
NULL);
gtk_container_set_reallocate_redraws (GTK_CONTAINER (wfixed), TRUE);
gdk_window_process_all_updates ();
}
......@@ -2277,11 +2245,6 @@ free_frame_menubar (f)
to indicate that callback should do nothing. */
int xg_ignore_gtk_scrollbar;
/* After we send a scroll bar event, x_set_toolkit_scroll_bar_thumb will
be called. For some reason that needs to be debugged, it gets called
with bad values. Thus, we set this variable to ignore those calls. */
int xg_ignore_next_thumb;
/* SET_SCROLL_BAR_X_WINDOW assumes the second argument fits in
32 bits. But we want to store pointers, and they may be larger
than 32 bits. Keep a mapping from integer index to widget pointers
......@@ -2391,16 +2354,15 @@ xg_gtk_scroll_destroy (widget, data)
p = g_object_get_data (G_OBJECT (widget), XG_LAST_SB_DATA);
if (p) xfree (p);
p = g_object_get_data (G_OBJECT (widget), XG_LAST_SB_POS);
if (p) xfree (p);
xg_remove_widget_from_map (id);
}
/* Callback for button press/release events. Used to start timer so that
the scroll bar repetition timer in GTK gets handeled.
Also, sets bar->dragging to Qnil when dragging (button release) is done.
WIDGET is the scroll bar widget the event is for (not used).
EVENT contains the event.
USER_DATA is 0 (not used).
USER_DATA points to the struct scrollbar structure.
Returns FALSE to tell GTK that it shall continue propagate the event
to widgets. */
......@@ -2412,9 +2374,13 @@ scroll_bar_button_cb (widget, event, user_data)
{
if (event->type == GDK_BUTTON_PRESS && ! xg_timer)
xg_start_timer ();
else if (event->type == GDK_BUTTON_RELEASE && xg_timer)
xg_stop_timer ();
else if (event->type == GDK_BUTTON_RELEASE)
{
struct scroll_bar *bar = (struct scroll_bar *) user_data;
if (xg_timer) xg_stop_timer ();
bar->dragging = Qnil;
}
return FALSE;
}
......@@ -2460,28 +2426,18 @@ xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name)
g_signal_connect (G_OBJECT (wscroll),
"button-press-event",
G_CALLBACK (scroll_bar_button_cb),
0);
(gpointer)bar);
g_signal_connect (G_OBJECT (wscroll),
"button-release-event",
G_CALLBACK (scroll_bar_button_cb),
0);
(gpointer)bar);
gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget),
wscroll, 0, 0);
wscroll, -1, -1);
/* Set the cursor to an arrow. */
xg_set_cursor (wscroll, &xg_left_ptr_cursor);
/* Allocate a place to hold the last scollbar size. GTK redraw for
scroll bars is basically clear all, and then redraw. This flickers
a lot since xg_update_scrollbar_pos gets called on every cursor move
and a lot more places. So we have this to check if a redraw really
is needed. */
struct xg_last_sb_pos *last_pos
= (struct xg_last_sb_pos *) xmalloc (sizeof (struct xg_last_sb_pos));
last_pos->portion = last_pos->position = last_pos->whole = -1;
g_object_set_data (G_OBJECT (wscroll), XG_LAST_SB_POS, last_pos);
SET_SCROLL_BAR_X_WINDOW (bar, scroll_id);
}
......@@ -2509,6 +2465,29 @@ xg_remove_scroll_bar (f, scrollbar_id)
}
}
/* Find left/top for widget W in GtkFixed widget WFIXED. */
static void
xg_find_top_left_in_fixed (w, wfixed, left, top)
GtkWidget *w, *wfixed;
int *left, *top;
{
GList *iter;
for (iter = GTK_FIXED (wfixed)->children; iter; iter = g_list_next (iter))
{
GtkFixedChild *child = (GtkFixedChild *) iter->data;
if (child->widget == w)
{
*left = child->x;
*top = child->y;
return;
}
}
/* Shall never end up here. */
abort ();
}
/* Update the position of the vertical scroll bar represented by SCROLLBAR_ID
in frame F.
......@@ -2525,34 +2504,101 @@ xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height)
{
GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
if (wscroll)
{
int gheight = max (height, 1);
struct xg_last_sb_pos *last_pos
= g_object_get_data (G_OBJECT (wscroll), XG_LAST_SB_POS);
GtkWidget *wfixed = f->output_data.x->edit_widget;
int gheight = max (height, 1);
int canon_width = FRAME_SCROLL_BAR_COLS (f) * CANON_X_UNIT (f);
int winextra = canon_width > width ? (canon_width - width) / 2 : 0;
int bottom = top + gheight;
last_pos->portion = last_pos->position = last_pos->whole = -1;
xg_ignore_next_thumb = 0;
gint slider_width;
int oldtop, oldleft, oldbottom;
GtkRequisition req;
/* Get old values. */
xg_find_top_left_in_fixed (wscroll, wfixed, &oldleft, &oldtop);
gtk_widget_size_request (wscroll, &req);
oldbottom = oldtop + req.height;
/* Scroll bars in GTK has a fixed width, so if we say width 16, it
will only be its fixed width (14 is default) anyway, the rest is
blank. We are drawing the mode line across scroll bars when
the frame is split:
|bar| |fringe|
----------------
mode line
----------------
|bar| |fringe|
When we "unsplit" the frame:
|bar| |fringe|
-| |-| |
m¦ |i| |
-| |-| |
| | | |
the remains of the mode line can be seen in these blank spaces.
So we must clear them explicitly.
GTK scroll bars should do that, but they don't.
Also, the scroll bar canonical width may be wider than the width
passed in here. */
if (oldtop != -1 && oldleft != -1)
{
int gtkextra;
int xl, xr, wblank;
int bottomdiff, topdiff;
gtk_widget_style_get (wscroll, "slider_width", &slider_width, NULL);
gtkextra = width > slider_width ? (width - slider_width) / 2 : 0;
xl = left - winextra;
wblank = gtkextra + winextra;
xr = left + gtkextra + slider_width;
bottomdiff = abs (oldbottom - bottom);
topdiff = abs (oldtop - top);
if (oldtop > top)
{
gdk_window_clear_area (wfixed->window, xl, top, wblank, topdiff);
gdk_window_clear_area (wfixed->window, xr, top, wblank, topdiff);
}
else if (oldtop < top)
{
gdk_window_clear_area (wfixed->window, xl, oldtop, wblank,
topdiff);
gdk_window_clear_area (wfixed->window, xr, oldtop, wblank,
topdiff);
}
if (oldbottom > bottom)
{
gdk_window_clear_area (wfixed->window, xl, bottom, wblank,
bottomdiff);
gdk_window_clear_area (wfixed->window, xr, bottom, wblank,
bottomdiff);
}
else if (oldbottom < bottom)
{
gdk_window_clear_area (wfixed->window, xl, oldbottom, wblank,
bottomdiff);
gdk_window_clear_area (wfixed->window, xr, oldbottom, wblank,
bottomdiff);
}
}
/* Move and resize to new values. */
gtk_fixed_move (GTK_FIXED (wfixed), wscroll, left, top);
gtk_widget_set_size_request (wscroll, width, gheight);
gtk_container_set_reallocate_redraws (GTK_CONTAINER (wfixed), TRUE);
/* Must force out update so wscroll gets the resize.
Otherwise, the gdk_window_clear clears the old window size. */
gdk_window_process_all_updates ();
/* The scroll bar doesn't explicitly redraw the whole window
when a resize occurs. Since the scroll bar seems to be fixed
in width it doesn't fill the space reserved, so we must clear
the whole window. */
gdk_window_clear (wscroll->window);
/* Since we are not using a pure gtk event loop, we must force out
pending update events with this call. */
/* Make GTK draw the new sizes. We are not using a pure GTK event
loop so we need to do this. */
gdk_window_process_all_updates ();
SET_FRAME_GARBAGED (f);
......@@ -2570,16 +2616,10 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
GtkWidget *wscroll = xg_get_widget_from_map (SCROLL_BAR_X_WINDOW (bar));
FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
struct xg_last_sb_pos *last_pos
= (wscroll ? g_object_get_data (G_OBJECT (wscroll), XG_LAST_SB_POS) : 0);
BLOCK_INPUT;
if (wscroll
&& ! xg_ignore_next_thumb
&& last_pos
&& (last_pos->portion != portion
|| last_pos->position != position
|| last_pos->whole != whole))
if (wscroll && NILP (bar->dragging))
{
GtkAdjustment *adj;
gdouble shown;
......@@ -2588,6 +2628,14 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
/* We do the same as for MOTIF in xterm.c, assume 30 chars per line
rather than the real portion value. This makes the thumb less likely
to resize and that looks better. */
portion = XFASTINT (XWINDOW (bar->window)->height) * 30;
/* When the thumb is at the bottom, position == whole.
So we need to increase `whole' to make space for the thumb. */
whole += portion;
if (whole <= 0)
top = 0, shown = 1;
else
......@@ -2604,34 +2652,28 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
value = min (value, whole - size);
value = max (value, XG_SB_MIN);
adj->upper = max (whole, size);
adj->page_size = (int)size;
/* Assume a page increment is about 95% of the page size */
adj->page_increment = (int) (0.95*adj->page_size);
/* Assume all lines are equal. */
adj->step_increment = portion / max (1, FRAME_HEIGHT (f));
if (last_pos)
{
last_pos->portion = portion;
last_pos->position = position;
last_pos->whole = whole;
}
/* gtk_range_set_value invokes the callback. Set
ignore_gtk_scrollbar to make the callback do nothing */
xg_ignore_gtk_scrollbar = 1;
gtk_range_set_range (GTK_RANGE (wscroll), adj->lower, max (whole, size));
/* Assume all lines are of equal size. */
/* Assume a page increment is about 95% of the page size */
gtk_range_set_increments (GTK_RANGE (wscroll),
portion / max (1, FRAME_HEIGHT (f)),
(int) (0.95*adj->page_size));
gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value);
xg_ignore_gtk_scrollbar = 0;
/* Make sure the scroll bar is redrawn with new thumb */
gtk_widget_queue_draw (wscroll);
/* Make GTK draw the new thumb. We are not using a pure GTK event
loop so we need to do this. */
gdk_window_process_all_updates ();
}
gdk_window_process_all_updates ();
xg_ignore_next_thumb = 0;
UNBLOCK_INPUT;
}
......@@ -3079,7 +3121,6 @@ void
xg_initialize ()
{
xg_ignore_gtk_scrollbar = 0;
xg_ignore_next_thumb = 0;
xg_left_ptr_cursor = 0;
xg_did_tearoff = 0;
......
......@@ -6395,7 +6395,7 @@ xm_scroll_callback (widget, client_data, call_data)
#else /* !USE_MOTIF, i.e. Xaw or GTK */
#ifdef USE_GTK
/* Scroll bar callback for Gtk scroll bars. WIDGET is the scroll
/* Scroll bar callback for GTK scroll bars. WIDGET is the scroll
bar adjustment widget. DATA is a pointer to the scroll_bar structure. */
static void
......@@ -6453,13 +6453,12 @@ xg_scroll_callback (widget, data)
{
part = scroll_bar_handle;
whole = adj->upper - adj->page_size;
portion = min (position, whole);
bar->dragging = make_number (portion);
portion = min ((int)position, whole);
bar->dragging = make_number ((int)portion);
}
if (part >= 0)
{
xg_ignore_next_thumb = 1;
window_being_scrolled = bar->window;
last_scroll_bar_part = part;
x_send_scroll_bar_event (bar->window, part, portion, whole);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment