Re: [Banshee-List] More ListView Performance Goodness!
- From: "Scott Peterson" <lunchtimemama gmail com>
- To: banshee-list gnome org
- Subject: Re: [Banshee-List] More ListView Performance Goodness!
- Date: Tue, 12 Feb 2008 17:23:08 -0500
New patch. Adds horizontal adjustment support. I think Aaron wants to
work over the resize behavior a little, so I've left that alone. I'm
not necessarily totally happy with how the hadj stuff looks in this
patch, but I don't know enough about the way Gtk works to make it
pretty, or even to judge the aesthetics of it. I'll leave that to
abock. Attached and here:
http://homepages.nyu.edu/~stp225/listview3.patch
Index: src/Core/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs
===================================================================
--- src/Core/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs (revision 3222)
+++ src/Core/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs (working copy)
@@ -44,6 +44,8 @@
private Gdk.Rectangle footer_alloc;
private Gdk.Rectangle left_border_alloc;
private Gdk.Rectangle right_border_alloc;
+
+ private int rows_in_view;
protected override void OnRealized ()
{
@@ -184,6 +186,11 @@
list_window.Destroy ();
list_window = null;
+ list_canvas1.Dispose ();
+ list_canvas1 = null;
+ list_canvas2.Dispose ();
+ list_canvas2 = null;
+
base.OnUnrealized ();
}
@@ -233,6 +240,8 @@
list_alloc.Width = allocation.Width - 2 * left_border_alloc.Width;
list_alloc.Height = allocation.Height - header_alloc.Height - footer_alloc.Height;
list_window.MoveResize (left_border_alloc.Width, header_alloc.Height, list_alloc.Width, list_alloc.Height);
+
+ rows_in_view = (int) Math.Ceiling ((list_alloc.Height + RowHeight) / (double) RowHeight);
}
protected override void OnSizeRequested (ref Requisition requisition)
@@ -251,28 +260,32 @@
GdkWindow.MoveResize (allocation);
MoveResizeWindows (allocation);
}
-
- if (vadjustment != null) {
- vadjustment.PageSize = list_alloc.Height;
- vadjustment.PageIncrement = list_alloc.Height;
- UpdateAdjustments (null, null);
- }
if (resized_width) {
InvalidateHeaderWindow ();
InvalidateFooterWindow ();
+ RegenerateColumnCache ();
}
- if (Model is ICareAboutView) {
- ((ICareAboutView)Model).RowsInView = RowsInView;
+ if (hadjustment != null) {
+ hadjustment.PageSize = list_alloc.Width;
+ hadjustment.PageIncrement = list_alloc.Width;
+ hadjustment.StepIncrement = 1;
}
+ if (vadjustment != null) {
+ vadjustment.PageSize = list_alloc.Height;
+ vadjustment.PageIncrement = list_alloc.Height;
+ vadjustment.StepIncrement = RowHeight;
+ }
+ UpdateAdjustments (null, null);
+
+ ICareAboutView care_about_view = Model as ICareAboutView;
+ if (care_about_view != null) {
+ care_about_view.RowsInView = rows_in_view;
+ }
+
InvalidateListWindow ();
- RegenerateColumnCache ();
}
-
- private int RowsInView {
- get { return (int) Math.Ceiling (list_alloc.Height / (double) RowHeight) + 1; }
- }
}
}
Index: src/Core/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs
===================================================================
--- src/Core/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs (revision 3222)
+++ src/Core/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs (working copy)
@@ -51,6 +51,7 @@
private static Gdk.Cursor resize_x_cursor = new Gdk.Cursor (Gdk.CursorType.SbHDoubleArrow);
private static Gdk.Cursor drag_cursor = new Gdk.Cursor (Gdk.CursorType.Fleur);
+ private int header_width;
private int column_text_y;
private int column_text_height;
private int resizing_column_index = -1;
@@ -89,6 +90,7 @@
int i = 0;
column_cache = new CachedColumn[column_controller.Count];
+ header_width = 0;
foreach (Column column in column_controller) {
if (!column.Visible) {
@@ -98,7 +100,7 @@
column_cache[i] = new CachedColumn ();
column_cache[i].Column = column;
- column_cache[i].Width = (int)Math.Round (((double)list_alloc.Width * column.Width));
+ header_width += column_cache[i].Width = (int)Math.Round (((double)list_alloc.Width * column.Width));
column_cache[i].X1 = i == 0 ? 0 : column_cache[i - 1].X2;
column_cache[i].X2 = column_cache[i].X1 + column_cache[i].Width;
column_cache[i].ResizeX1 = column_cache[i].X1 + column_cache[i].Width - COLUMN_PADDING;
@@ -114,6 +116,7 @@
protected virtual void OnColumnControllerUpdated ()
{
RegenerateColumnCache ();
+ UpdateHadjustment (null);
QueueDraw ();
}
@@ -151,6 +154,8 @@
private void ResizeColumn (double x)
{
+ x += hadjustment_value;
+
CachedColumn resizing_column = column_cache[resizing_column_index];
double resize_delta = x - resizing_column.ResizeX2;
@@ -191,6 +196,8 @@
return null;
}
+ x += hadjustment_value;
+
foreach (CachedColumn column in column_cache) {
if (x >= column.ResizeX1 - 2 + left_border_alloc.Width &&
x <= column.ResizeX2 + 2 + left_border_alloc.Width ) {
@@ -207,6 +214,8 @@
return null;
}
+ x += hadjustment_value;
+
foreach (CachedColumn column in column_cache) {
if (x >= column.X1 + left_border_alloc.Width && x <= column.X2 + left_border_alloc.Width) {
return column.Column;
Index: src/Core/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
===================================================================
--- src/Core/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs (revision 3222)
+++ src/Core/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs (working copy)
@@ -3,6 +3,7 @@
//
// Author:
// Aaron Bockover <abockover novell com>
+// Scott Peterson <lunchtimemama gmail com>
//
// Copyright (C) 2007-2008 Novell, Inc.
//
@@ -52,6 +53,21 @@
private Pango.Layout header_pango_layout;
private Pango.Layout list_pango_layout;
+ // The list is rendered to an off-screen Drawable; the "canvas". The canvas is large enough to hold
+ // the maximum number of rows that can be seen in the list. When new rows comes into view b/c of
+ // scrolling, the contents of the canvas are shifted up or down (depending on whether the user is
+ // scrolling up or down) to accommodate the new rows. The new rows are then painted in situ on the
+ // canvas. The canvas is then painted onto the list_window, offset properly. This means that a row
+ // is only rendered once when it comes into view and that rendering persists on the canvas for as
+ // long as that row remains in view.
+ private Gdk.Drawable list_canvas1; // The primary off-screen Drawable canvas
+ private Gdk.Drawable list_canvas2; // The secondary off-screen Drawable canvas (used for shifting the canvas up and down)
+ private Gdk.Rectangle list_canvas_alloc; // The bounds of the canvas
+ private int list_canvas_index; // The number of the row at the top of the canvas
+ private int list_canvas_offset; // The amount by which to offset the canvas when it is copied to the list_window
+
+ private int hadjustment_value;
+
public new void QueueDraw ()
{
base.QueueDraw ();
@@ -69,41 +85,53 @@
return true;
}
+
+ private delegate void Painter (Gdk.EventExpose evnt, Gdk.Rectangle clip);
+
+ private static Pango.Layout default_pango_layout;
+
+ private static void Paint (Painter code, Gdk.EventExpose evnt, Gdk.Rectangle clip, ref Cairo.Context context)
+ {
+ Paint (code, evnt, clip, evnt.Window, clip, ref context, ref default_pango_layout, false);
+ }
+
+ private static void Paint (Painter code, Gdk.EventExpose evnt, Gdk.Rectangle clip, ref Cairo.Context context, ref Pango.Layout layout)
+ {
+ Paint (code, evnt, clip, evnt.Window, clip, ref context, ref layout, true);
+ }
+
+ private static void Paint (Painter code, Gdk.EventExpose evnt, Gdk.Rectangle clip, Gdk.Drawable canvas, Gdk.Rectangle canvas_clip, ref Cairo.Context context, ref Pango.Layout layout, bool has_layout)
+ {
+ context = CairoHelper.CreateCairoDrawable (canvas);
+ context.Rectangle (canvas_clip.X, canvas_clip.Y, canvas_clip.Width, canvas_clip.Height);
+ context.Clip ();
+
+ if (has_layout && layout == null) {
+ layout = Pango.CairoHelper.CreateLayout (context);
+ }
+
+ code (evnt, clip);
+
+ ((IDisposable)context.Target).Dispose ();
+ ((IDisposable)context).Dispose ();
+ }
private void PaintRegion (Gdk.EventExpose evnt, Gdk.Rectangle clip)
{
- Cairo.Context cr = CairoHelper.CreateCairoDrawable (evnt.Window);
- cr.Rectangle (clip.X, clip.Y, clip.Width, clip.Height);
- cr.Clip ();
-
if (evnt.Window == header_window) {
- header_cr = cr;
- if (header_pango_layout == null) {
- header_pango_layout = Pango.CairoHelper.CreateLayout (header_cr);
- }
- PaintHeader (evnt.Area);
+ Paint (PaintHeader, evnt, clip, ref header_cr, ref header_pango_layout);
} else if (evnt.Window == footer_window) {
- footer_cr = cr;
- PaintFooter (evnt, clip);
+ Paint (PaintFooter, evnt, clip, ref footer_cr);
} else if (evnt.Window == left_border_window) {
- left_border_cr = cr;
- PaintLeftBorder(evnt, clip);
+ Paint (PaintLeftBorder, evnt, clip, ref left_border_cr);
} else if (evnt.Window == right_border_window) {
- right_border_cr = cr;
- PaintRightBorder(evnt, clip);
+ Paint (PaintRightBorder, evnt, clip, ref right_border_cr);
} else if (evnt.Window == list_window) {
- list_cr = cr;
- if (list_pango_layout == null) {
- list_pango_layout = Pango.CairoHelper.CreateLayout (list_cr);
- }
PaintList (evnt, clip);
}
-
- ((IDisposable)cr.Target).Dispose ();
- ((IDisposable)cr).Dispose ();
}
- private void PaintHeader (Gdk.Rectangle clip)
+ private void PaintHeader (Gdk.EventExpose evnt, Gdk.Rectangle clip)
{
graphics.DrawHeaderBackground (header_cr, header_alloc, 2, header_visible);
@@ -120,13 +148,13 @@
continue;
}
- cell_area.X = column_cache[ci].X1 + left_border_alloc.Width;
+ cell_area.X = column_cache[ci].X1 + left_border_alloc.Width - hadjustment_value;
cell_area.Width = column_cache[ci].Width - COLUMN_PADDING;
PaintHeaderCell (cell_area, clip, ci, false);
}
if (pressed_column_is_dragging && pressed_column_index >= 0) {
- cell_area.X = pressed_column_x_drag + left_border_alloc.Width;
+ cell_area.X = pressed_column_x_drag + left_border_alloc.Width - hadjustment_value;
cell_area.Width = column_cache[pressed_column_index].Width - COLUMN_PADDING;
PaintHeaderCell (cell_area, clip, pressed_column_index, true);
}
@@ -139,7 +167,7 @@
if (dragging) {
if (ci < column_cache.Length - 1) {
graphics.DrawHeaderSeparator (header_cr, header_alloc,
- column_cache[ci].ResizeX1 - 1 + left_border_alloc.Width, 2);
+ column_cache[ci].ResizeX1 - 1 - hadjustment_value + left_border_alloc.Width, 2);
}
graphics.DrawColumnHighlight (header_cr, area, 3,
@@ -176,31 +204,104 @@
if (!dragging && ci < column_cache.Length - 1) {
graphics.DrawHeaderSeparator (header_cr, header_alloc,
- column_cache[ci].ResizeX1 - 1 + left_border_alloc.Width, 2);
+ column_cache[ci].ResizeX1 - 1 - hadjustment_value + left_border_alloc.Width, 2);
}
}
+
+ // Copy the offset contents of the canvas to the secondary canvas and make the
+ // secondary canvas the primary canvas. This is just about the fastest way of
+ // doing this (Pixbuf.GetFromDrawable is heinously slow)
+ private void ShiftCanvas (int src_y, int dest_y, int height, Gdk.Rectangle clip)
+ {
+ list_canvas2.DrawRectangle (Style.WhiteGC, true, clip);
+ list_canvas2.DrawDrawable (Style.WhiteGC, list_canvas1, 0, src_y, 0, dest_y, list_canvas_alloc.Width, height);
+
+ Gdk.Drawable tmp = list_canvas1;
+ list_canvas1 = list_canvas2;
+ list_canvas2 = tmp;
+ }
private void PaintList (Gdk.EventExpose evnt, Gdk.Rectangle clip)
{
if (model == null) {
return;
}
-
+
+ bool paint = false;
int vadjustment_value = (int)vadjustment.Value;
- int first_row = vadjustment_value / RowHeight;
- int last_row = Math.Min (model.Count, first_row + RowsInView);
+ list_canvas_offset = vadjustment_value % RowHeight;
+ clip = list_canvas_alloc;
+
+ if (vscrolled) {
+ int index = vadjustment_value / RowHeight;
+
+ // If new rows have come into view...
+ if (index != list_canvas_index) {
+ int delta;
+
+ // If everything is selected, paint over everything (it's faster this way)
+ if (Selection.Count == model.Count) {
+ }
+ // Otherwise, if some of the same rows are still visible...
+ else if ((delta = Math.Abs (index - list_canvas_index)) < rows_in_view) {
+ clip.Height = delta * RowHeight;
+ int height = list_canvas_alloc.Height - clip.Height;
+
+ // Shift the canvas up if the new rows have come in at the bottom
+ if (index > list_canvas_index) {
+ clip.Y = list_canvas_alloc.Height - clip.Height;
+ ShiftCanvas (clip.Height, 0, height, clip);
+ }
+ // Shift the canvas down if the new rows have come in at the top
+ else {
+ ShiftCanvas (0, clip.Height, height, clip);
+ }
+ }
+ // Otherwise, paint all rows
+ else {
+ list_canvas1.DrawRectangle (Style.WhiteGC, true, list_canvas_alloc);
+ }
+
+ list_canvas_index = index;
+ paint = true;
+ }
+
+ vscrolled = false;
+ }
+ else if (hscrolled) {
+ hscrolled = false;
+ } else {
+ list_canvas1.DrawRectangle (Style.WhiteGC, true, list_canvas_alloc);
+ paint = true;
+ }
+
+ // Paint the rows within the bounds of 'clip'
+ if (paint) {
+ Paint (PaintRows, evnt, clip, list_canvas1, list_canvas_alloc, ref list_cr, ref list_pango_layout, true);
+ }
+
+ // Copy the canvas to the list_window, accounting for the offset
+ list_window.DrawDrawable (Style.WhiteGC, list_canvas1, hadjustment_value, list_canvas_offset, 0, 0, list_alloc.Width, list_alloc.Height);
+ }
- Gdk.Rectangle selected_focus_alloc = Gdk.Rectangle.Zero;
+ private void PaintRows (Gdk.EventExpose evnt, Gdk.Rectangle clip)
+ {
+ int first_row = list_canvas_index + clip.Y / RowHeight;
+ int last_row = Math.Min (model.Count, first_row + clip.Height / RowHeight);
+
Gdk.Rectangle single_list_alloc = new Gdk.Rectangle ();
- single_list_alloc.Width = list_alloc.Width;
+ single_list_alloc.Width = clip.Width;
single_list_alloc.Height = RowHeight;
- single_list_alloc.X = list_alloc.X;
- single_list_alloc.Y = list_alloc.Y - vadjustment_value + (first_row * single_list_alloc.Height);
+ single_list_alloc.X = clip.X;
+ single_list_alloc.Y = clip.Y;
int selection_height = 0;
int selection_y = 0;
List<int> selected_rows = new List<int> ();
+
+ bool paint_all = last_row - first_row == rows_in_view;
+ bool focused_row_in_view = false;
for (int ri = first_row; ri < last_row; ri++) {
if (Selection.Contains (ri)) {
@@ -211,9 +312,50 @@
selection_height += single_list_alloc.Height;
selected_rows.Add (ri);
- if (focused_row_index == ri) {
- selected_focus_alloc = single_list_alloc;
+ if (ri == focused_row_index) {
+ focused_row_in_view = true;
}
+
+ // If a new row has come into view and it's part of a block selection which
+ // includes visible rows that we're not scheduled to paint, then we need to
+ // make sure the contiguous selection is drawn properly.
+ if (!paint_all) {
+
+ // If this is the last row we're rendering at the top of the list...
+ if (ri == last_row - 1 && first_row == list_canvas_index) {
+
+ // ...extend the selection block down until we get to the bottom of
+ // either the selection block or the canvas
+ while (Selection.Contains (++ri) && ri < list_canvas_index + rows_in_view) {
+ selection_height += single_list_alloc.Height;
+ selected_rows.Add (ri);
+
+ // And if the selection block covers the focused row, we'll
+ // want to re-render the focus visual
+ if (ri == focused_row_index) {
+ focused_row_in_view = true;
+ }
+ }
+ }
+ // If this is the first row we're rending at the bottom of the list...
+ else if (ri == first_row && (last_row == list_canvas_index + rows_in_view || last_row == model.Count)) {
+ int i = ri;
+
+ // ...extend the selection block up until we get to the top of
+ // either the selection block or the canvas
+ while (Selection.Contains (--i) && i >= list_canvas_index) {
+ selection_height += single_list_alloc.Height;
+ selection_y -= single_list_alloc.Height;
+ selected_rows.Add (i);
+
+ // And if the selection block covers the focused row, we'll
+ // want to re-render the focus visual
+ if (i == focused_row_index) {
+ focused_row_in_view = true;
+ }
+ }
+ }
+ }
} else {
if (rules_hint && ri % 2 != 0) {
graphics.DrawRowRule (list_cr, single_list_alloc.X, single_list_alloc.Y,
@@ -238,7 +380,7 @@
if (selection_height > 0) {
graphics.DrawRowSelection (
- list_cr, list_alloc.X, list_alloc.Y + selection_y, list_alloc.Width, selection_height);
+ list_cr, list_canvas_alloc.X, list_canvas_alloc.Y + selection_y, list_canvas_alloc.Width, selection_height);
selection_height = 0;
}
@@ -249,18 +391,19 @@
}
if (selection_height > 0) {
- graphics.DrawRowSelection (list_cr, list_alloc.X, list_alloc.Y + selection_y,
- list_alloc.Width, selection_height);
+ graphics.DrawRowSelection (list_cr, list_canvas_alloc.X, selection_y,
+ list_canvas_alloc.Width, selection_height);
}
- if (Selection.Count > 1 && !selected_focus_alloc.Equals (Gdk.Rectangle.Zero)) {
- graphics.DrawRowSelection (list_cr, selected_focus_alloc.X, selected_focus_alloc.Y,
- selected_focus_alloc.Width, selected_focus_alloc.Height, false, true,
+ if (focused_row_in_view) {
+ int focused_y = (focused_row_index - list_canvas_index) * single_list_alloc.Height;
+ graphics.DrawRowSelection (list_cr, single_list_alloc.X, focused_y,
+ single_list_alloc.Width, single_list_alloc.Height, false, true,
graphics.GetWidgetColor (GtkColorClass.Dark, StateType.Selected));
}
foreach (int ri in selected_rows) {
- single_list_alloc.Y = ri * single_list_alloc.Height - vadjustment_value;
+ single_list_alloc.Y = (ri - list_canvas_index) * single_list_alloc.Height;
PaintRow (ri, clip, single_list_alloc, StateType.Selected);
}
@@ -312,7 +455,7 @@
list_cr.Save ();
list_cr.Translate (clip.X, clip.Y);
- cell.Render (new CellContext (list_cr, list_pango_layout, this, list_window, graphics, area),
+ cell.Render (new CellContext (list_cr, list_pango_layout, this, list_canvas1, graphics, area),
dragging? StateType.Normal : state, area.Width, area.Height);
list_cr.Restore ();
}
@@ -326,6 +469,7 @@
CachedColumn column = column_cache[pressed_column_index];
int x = pressed_column_x_drag;
+ int y = list_alloc.Y + list_canvas_offset;
Cairo.Color fill_color = graphics.GetWidgetColor (GtkColorClass.Base, StateType.Normal);
fill_color.A = 0.45;
@@ -334,14 +478,14 @@
GtkColorClass.Base, StateType.Normal), 0.0);
stroke_color.A = 0.3;
- list_cr.Rectangle (x, list_alloc.Y, column.Width, list_alloc.Height);
+ list_cr.Rectangle (x, y, column.Width, list_alloc.Height);
list_cr.Color = fill_color;
list_cr.Fill ();
- list_cr.MoveTo (x, list_alloc.Y);
- list_cr.LineTo (x, list_alloc.Y + list_alloc.Height - 1.0);
- list_cr.LineTo (x + column.Width, list_alloc.Y + list_alloc.Height - 1.0);
- list_cr.LineTo (x + column.Width, list_alloc.Y);
+ list_cr.MoveTo (x, y);
+ list_cr.LineTo (x, y + list_alloc.Height - 1.0);
+ list_cr.LineTo (x + column.Width, y + list_alloc.Height - 1.0);
+ list_cr.LineTo (x + column.Width, y);
list_cr.Color = stroke_color;
list_cr.Antialias = Cairo.Antialias.None;
@@ -385,6 +529,25 @@
}
}
+ protected void RegenerateCanvas ()
+ {
+ if (!IsRealized) {
+ return;
+ }
+
+ list_canvas_alloc.Width = Math.Max (header_width, list_alloc.Width);
+ list_canvas_alloc.Height = rows_in_view * RowHeight;
+
+ if (list_canvas1 != null) {
+ list_canvas1.Dispose ();
+ list_canvas2.Dispose ();
+ }
+ list_canvas1 = new Gdk.Pixmap (GdkWindow, list_canvas_alloc.Width, list_canvas_alloc.Height);
+ list_canvas2 = new Gdk.Pixmap (GdkWindow, list_canvas_alloc.Width, list_canvas_alloc.Height);
+ list_canvas1.DrawRectangle (Style.WhiteGC, true, list_canvas_alloc);
+ list_canvas2.DrawRectangle (Style.WhiteGC, true, list_canvas_alloc);
+ }
+
private bool rules_hint = false;
public bool RulesHint {
get { return rules_hint; }
Index: src/Core/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
===================================================================
--- src/Core/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs (revision 3222)
+++ src/Core/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs (working copy)
@@ -45,6 +45,11 @@
get { return vadjustment; }
}
+ private Adjustment hadjustment;
+ public Adjustment Hadjustment {
+ get { return hadjustment; }
+ }
+
private SelectionProxy selection_proxy = new SelectionProxy ();
public SelectionProxy SelectionProxy {
get { return selection_proxy; }
@@ -443,26 +448,67 @@
#endregion
#region Adjustments & Scrolling
-
+
private void UpdateAdjustments (Adjustment hadj, Adjustment vadj)
{
+ UpdateHadjustment (hadj);
+ UpdateVadjustment (vadj);
+ }
+
+ private void UpdateHadjustment (Adjustment hadj)
+ {
+ if (hadj != null) {
+ hadjustment = hadj;
+ }
+
+ if (hadjustment != null) {
+ RegenerateCanvas ();
+ hadjustment.Upper = list_canvas_alloc.Width;
+ hadjustment.Change ();
+
+ double right_edge = hadjustment.Value + hadjustment.PageSize;
+ if (right_edge > hadjustment.Upper) {
+ ignore_hadjust_change = true;
+ hadjustment.Value -= right_edge - hadjustment.Upper;
+ ignore_hadjust_change = false;
+ }
+ }
+ }
+
+ private void UpdateVadjustment (Adjustment vadj)
+ {
if (vadj != null) {
vadjustment = vadj;
}
if (vadjustment != null && model != null) {
vadjustment.Upper = (RowHeight * (model.Count));
- vadjustment.StepIncrement = RowHeight;
+ vadjustment.Change ();
}
-
- vadjustment.Change ();
}
- private void OnAdjustmentChanged (object o, EventArgs args)
+ private bool vscrolled;
+ private bool hscrolled;
+
+ private void OnVadjustmentChanged (object o, EventArgs args)
{
+ vscrolled = true;
InvalidateListWindow ();
}
+ private bool ignore_hadjust_change;
+ private void OnHadjustmentChanged (object o, EventArgs args)
+ {
+ hadjustment_value = (int)hadjustment.Value;
+
+ if (ignore_hadjust_change) {
+ return;
+ }
+ hscrolled = true;
+ InvalidateHeaderWindow ();
+ InvalidateListWindow ();
+ }
+
public void ScrollTo (double val)
{
vadjustment.Value = Math.Min (val, vadjustment.Upper - vadjustment.PageSize);
@@ -475,12 +521,12 @@
protected override void OnSetScrollAdjustments (Adjustment hadj, Adjustment vadj)
{
- if (vadj == null || hadj == null) {
+ if (hadj == null || vadj == null) {
return;
}
- vadj.ValueChanged += OnAdjustmentChanged;
- hadj.ValueChanged += OnAdjustmentChanged;
+ hadj.ValueChanged += OnHadjustmentChanged;
+ vadj.ValueChanged += OnVadjustmentChanged;
UpdateAdjustments (hadj, vadj);
}
[
Date Prev][
Date Next] [
Thread Prev][Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]