Re: [Banshee-List] More ListView Performance Goodness!



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]