[banshee/grid: 49/50] [grid] Started refactoring grid into proper layout



commit ff2601c4cee5608068f3204973651b5f9c72974e
Author: Aaron Bockover <abockover novell com>
Date:   Wed Jan 20 17:56:30 2010 -0500

    [grid] Started refactoring grid into proper layout
    
    It became clear when implementing more complicated interaction that
    the layout refactoring I had planned needed to be done sooner than
    later.
    
    This is the first chunk of work to abstract layout into adapter
    classes, and grid is the first such adapter. I am leaving list
    support in-place confined in the view for now until layout is
    finished so there are no list regressions. Therefore I have
    sprinkled many FIXME's about layout and what should be obsolete
    in the future.
    
    Eventually list support will become a layout adapter and column,
    header, and renderers will move into the layout. It's possible that
    the column controller will be made obsolete entirely.
    
    So what is all of this layout stuff? The list view was always fairly
    simple, and layout (how things are positioned on screen) was computed
    on the go during the rendering phase. This presents a major problem
    though with more complicated layouts since layout essentially has
    to be computed on render and any sort of interaction, though it was
    never a big deal with list.
    
    So now the layout is computed and cached when things change:
    
      - the view allocation changes
      - the view is scrolled
      - the model size changes
      - hamburgers
    
    This allows the rendering and interaction phases to simply iterate
    the layout's children to render children on screen or interact with
    them. Life is easier and cleaner.
    
    So rendering is now mostly complete for grid, there are a few
    glitches. Interaction doesn't work at all anymore, but it'll come
    next.
    
    I was thinking about the epic burger I had last night:
    
      - 1/2 lb burger patty
      - 1 grilled chicken filet
      - layer of fried mac and cheese
      - layer of bacon
      - layer of swiss cheese
      - buffalo sauce
      - mayo
      - grilled onions
    
    It was life changing. Or maybe validating. Or something. But the big
    problem with this burger was the mess, and on top of that, I really
    wanted two of these beasts. So what I am going to do in the future
    is make a casserole of these burgers. Top the dish off with cheese
    and maybe some cracker crumbles or something and bake shortly. Yes.

 .../Banshee.Collection.Gui/AlbumListView.cs        |   30 ++++--
 .../Banshee.Collection.Gui/ColumnCellAlbum.cs      |   16 ++-
 .../Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs         |    2 +-
 .../Hyena.Gui/Hyena.Data.Gui/DataViewLayout.cs     |  103 ++++++++++++++++++
 .../Hyena.Gui/Hyena.Data.Gui/DataViewLayoutGrid.cs |  115 ++++++++++++++++++++
 .../Hyena.Data.Gui/DataViewLayoutStyle.cs          |   37 -------
 .../Hyena.Data.Gui/ListView/ListView_Header.cs     |    3 +-
 .../ListView/ListView_Interaction.cs               |   68 ++++++++----
 .../Hyena.Data.Gui/ListView/ListView_Model.cs      |    5 +-
 .../Hyena.Data.Gui/ListView/ListView_Rendering.cs  |   93 ++++++----------
 .../Hyena.Data.Gui/ListView/ListView_Windowing.cs  |   19 ++--
 src/Libraries/Hyena.Gui/Hyena.Gui.csproj           |    3 +-
 src/Libraries/Hyena.Gui/Makefile.am                |    9 +-
 src/Libraries/Hyena/Makefile.am                    |    2 +-
 14 files changed, 359 insertions(+), 146 deletions(-)
---
diff --git a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/AlbumListView.cs b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/AlbumListView.cs
index d4abdd9..a22aab3 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/AlbumListView.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/AlbumListView.cs
@@ -38,16 +38,33 @@ using Banshee.Gui;
 
 namespace Banshee.Collection.Gui
 {
+    // FIXME: Renderers and the column controller will move into
+    // DataViewLayout, so this subclass will be unnecessary
+    public class AlbumViewGridLayout : DataViewLayoutGrid
+    {
+        public ColumnCellAlbum Renderer { get; private set; }
+
+        public AlbumViewGridLayout (AlbumListView view)
+        {
+            View = view;
+            Renderer = new ColumnCellAlbum () { ViewLayout = this };
+        }
+
+        protected override void InvalidateChildSize ()
+        {
+            ChildSize = Renderer.Measure (View);
+        }
+    }
+
     public class AlbumListView : TrackFilterListView<AlbumInfo>
     {
-        private ColumnCellAlbum renderer;
+        private AlbumViewGridLayout view_layout;
 
         public AlbumListView () : base ()
         {
-            LayoutStyle = DataViewLayoutStyle.Grid;
-            renderer = new ColumnCellAlbum () { LayoutStyle = LayoutStyle };
+            ViewLayout = view_layout = new AlbumViewGridLayout (this);
 
-            column_controller.Add (new Column ("Album", renderer, 1.0));
+            column_controller.Add (new Column ("Album", view_layout.Renderer, 1.0));
             ColumnController = column_controller;
 
             ServiceManager.PlayerEngine.ConnectEvent (OnPlayerEvent, PlayerEvent.TrackInfoUpdated);
@@ -59,10 +76,5 @@ namespace Banshee.Collection.Gui
             //       b) xfade the artwork if it is, that'd be slick
             QueueDraw ();
         }
-
-        protected override Gdk.Size OnMeasureChild ()
-        {
-            return renderer.Measure (this);
-        }
     }
 }
diff --git a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellAlbum.cs b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellAlbum.cs
index ce45c7b..58f1812 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellAlbum.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellAlbum.cs
@@ -51,6 +51,12 @@ namespace Banshee.Collection.Gui
         public double ImageSpacing { get; set; }
         public double TextSpacing { get; set; }
 
+        private bool IsGridLayout {
+            // FIXME: Cache this after implementing virtual notification
+            // on ColumnCell that ViewLayout has changed ...
+            get { return ViewLayout is AlbumViewGridLayout; }
+        }
+
         public ColumnCellAlbum () : base (null, true)
         {
             artwork_manager = ServiceManager.Get<ArtworkManager> ();
@@ -90,7 +96,7 @@ namespace Banshee.Collection.Gui
                 height = image_surface.Height;
             }
 
-            if (LayoutStyle == DataViewLayoutStyle.Grid) {
+            if (IsGridLayout) {
                 x = Math.Round ((cellWidth - 2 * PaddingX - width) / 2.0);
             } else {
                 y = Math.Round ((cellHeight - 2 * PaddingY - height) / 2.0);
@@ -99,7 +105,7 @@ namespace Banshee.Collection.Gui
             RenderImageSurface (context, new Rectangle (x, y, width, height), image_surface);
 
             // Render the overlay
-            if (LayoutStyle == DataViewLayoutStyle.Grid && hover_object != null) {
+            if (IsGridLayout && hover_object != null) {
                 var cr = context.Context;
                 cr.Color = new Color (0, 0, 0, 0.5);
                 cr.Rectangle (x, y, width, height);
@@ -119,7 +125,7 @@ namespace Banshee.Collection.Gui
             layout.Ellipsize = Pango.EllipsizeMode.End;
             layout.FontDescription.Weight = Pango.Weight.Bold;
 
-            layout.Width = (int)((LayoutStyle == DataViewLayoutStyle.Grid
+            layout.Width = (int)((IsGridLayout
                 ? cellWidth - 2 * PaddingX
                 : cellWidth - ImageSize - ImageSpacing - 2 * PaddingX) * Pango.Scale.PangoScale);
 
@@ -137,7 +143,7 @@ namespace Banshee.Collection.Gui
                 layout.GetPixelSize (out sl_width, out sl_height);
             }
 
-            if (LayoutStyle == DataViewLayoutStyle.Grid) {
+            if (IsGridLayout) {
                 x = 0;
                 y = ImageSize + ImageSpacing;
             } else {
@@ -184,7 +190,7 @@ namespace Banshee.Collection.Gui
 
             double width, height;
 
-            if (LayoutStyle == DataViewLayoutStyle.Grid) {
+            if (ViewLayout is AlbumViewGridLayout) {
                 width = ImageSize + 2 * PaddingX;
                 height = ImageSize + ImageSpacing + TextSpacing + text_height + 2 * PaddingY;
             } else {
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs
index 8c4a923..20e8c57 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs
@@ -134,7 +134,7 @@ namespace Hyena.Data.Gui
             set { expand = value; }
         }
 
-        public DataViewLayoutStyle LayoutStyle { get; set; }
+        public DataViewLayout ViewLayout { get; set; }
 
         public string Property {
             get { return property; }
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/DataViewLayout.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/DataViewLayout.cs
new file mode 100644
index 0000000..1cc3ae2
--- /dev/null
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/DataViewLayout.cs
@@ -0,0 +1,103 @@
+//
+// DataViewLayout.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright 2010 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace Hyena.Data.Gui
+{
+    public abstract class DataViewLayout
+    {
+        public class Child
+        {
+            public Gdk.Rectangle Allocation { get; set; }
+            public int ModelRowIndex { get; set; }
+        }
+
+        private List<Child> children = new List<Child> ();
+        protected List<Child> Children {
+            get { return children; }
+        }
+
+        protected ListViewBase View { get; set; }
+
+        public Gdk.Rectangle ActualAllocation { get; protected set; }
+        public Gdk.Size VirtualSize { get; protected set; }
+        public Gdk.Size ChildSize { get; protected set; }
+        public int XPosition { get; protected set; }
+        public int YPosition { get; protected set; }
+        public int ModelRowCount { get; protected set; }
+
+        public int ChildCount {
+            get { return Children.Count; }
+        }
+
+        public Child this[int index] {
+            get { return Children[index]; }
+        }
+
+        public void UpdatePosition (int x, int y)
+        {
+            XPosition = x;
+            YPosition = y;
+            InvalidateChildLayout ();
+        }
+
+        public void UpdateModelRowCount (int modelRowCount)
+        {
+            ModelRowCount = modelRowCount;
+            InvalidateVirtualSize ();
+        }
+
+        public virtual void Allocate (Gdk.Rectangle actualAllocation)
+        {
+            ActualAllocation = actualAllocation;
+
+            InvalidateChildSize ();
+            InvalidateChildCollection ();
+            InvalidateChildLayout ();
+        }
+
+        protected abstract void InvalidateChildSize ();
+        protected abstract void InvalidateVirtualSize ();
+        protected abstract void InvalidateChildCollection ();
+        protected abstract void InvalidateChildLayout ();
+
+        protected void ResizeChildCollection (int newChildCount)
+        {
+            int difference = Children.Count - newChildCount;
+            while (Children.Count != newChildCount) {
+                if (difference > 0) {
+                    Children.RemoveAt (0);
+                } else {
+                    Children.Add (new Child ());
+                }
+            }
+        }
+    }
+}
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/DataViewLayoutGrid.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/DataViewLayoutGrid.cs
new file mode 100644
index 0000000..bdc19ce
--- /dev/null
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/DataViewLayoutGrid.cs
@@ -0,0 +1,115 @@
+//
+// DataViewLayoutGrid.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright 2010 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace Hyena.Data.Gui
+{
+    public class DataViewLayoutGrid : DataViewLayout
+    {
+        public int Rows { get; private set; }
+        public int Columns { get; private set; }
+
+        protected override void InvalidateChildSize ()
+        {
+            ChildSize = new Gdk.Size (48, 48);
+        }
+
+        protected override void InvalidateVirtualSize ()
+        {
+            // FIXME: this is too simplistic and can result in
+            // an extra row of allocation, check the model size
+            VirtualSize = new Gdk.Size (
+                ChildSize.Width * Columns,
+                (ChildSize.Height * ModelRowCount) / Rows);
+        }
+
+        protected override void InvalidateChildCollection ()
+        {
+            Rows = ChildSize.Height > 0
+                ? (int)Math.Ceiling ((ActualAllocation.Height +
+                    ChildSize.Height) / (double)ChildSize.Height)
+                : 0;
+            Columns = ChildSize.Width > 0
+                ? (int)Math.Max (ActualAllocation.Width / ChildSize.Width, 1)
+                : 0;
+
+            ResizeChildCollection (Rows * Columns);
+        }
+
+        protected override void InvalidateChildLayout ()
+        {
+            if (ChildSize.Width <= 0 || ChildSize.Height <= 0) {
+                // FIXME: empty/reset all child slots here?
+                return;
+            }
+
+            // Compute where we should start and end in the model
+            int offset = ActualAllocation.Y - YPosition % ChildSize.Height;
+            int first_model_row = (int)Math.Floor (YPosition / (double)ChildSize.Height) * Columns;
+            int last_model_row = first_model_row + Rows * Columns;
+
+            // Setup for the layout iteration
+            int model_row_index = first_model_row;
+            int layout_child_index = 0;
+            int view_row_index = 0;
+            int view_column_index = 0;
+
+            // Allocation of the first child in the layout, this
+            // will change as we iterate the layout children
+            var child_allocation = new Gdk.Rectangle () {
+                X = ActualAllocation.X,
+                Y = offset,
+                Width = ChildSize.Width,
+                Height = ChildSize.Height
+            };
+
+            // Iterate the layout children and configure them for the current
+            // view state to be consumed by interaction and rendering phases
+            for (; model_row_index < last_model_row; model_row_index++, layout_child_index++) {
+                var child = Children[layout_child_index];
+                child.Allocation = child_allocation;
+                child.ModelRowIndex = model_row_index;
+
+                // Update the allocation for the next child
+                if (++view_column_index % Columns == 0) {
+                    view_row_index++;
+                    view_column_index = 0;
+
+                    child_allocation.Y += ChildSize.Height;
+                    child_allocation.X = ActualAllocation.X;
+                } else {
+                    child_allocation.X += ChildSize.Width;
+                }
+
+                // FIXME: clear any layout children that go beyond the model
+            }
+        }
+    }
+}
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs
index fade711..a41e922 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs
@@ -523,7 +523,8 @@ namespace Hyena.Data.Gui
         private int header_height = 0;
         private int HeaderHeight {
             get {
-                if (!header_visible || LayoutStyle != DataViewLayoutStyle.List) {
+                // FIXME: ViewLayout should have the header info and never be null
+                if (!header_visible || ViewLayout != null) {
                     return 0;
                 }
 
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
index d0b174e..7dd941f 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
@@ -160,8 +160,9 @@ namespace Hyena.Data.Gui
         protected override bool OnKeyPressEvent (Gdk.EventKey press)
         {
             bool handled = false;
-            bool grid = LayoutStyle == DataViewLayoutStyle.Grid;
-            int items_per_row = grid ? GridColumnsInView : 1;
+            // FIXME: hard-coded grid logic here...
+            bool grid = ViewLayout != null;
+            int items_per_row = grid ? 1 : 1;
 
             switch (press.Key) {
                 case Gdk.Key.a:
@@ -368,7 +369,9 @@ namespace Hyena.Data.Gui
 
             var view_point = GetViewPointForModelRow (row_index);
             icell_area.Y = (int)view_point.Y + list_interaction_alloc.Y + Allocation.Y;
-            if (LayoutStyle == DataViewLayoutStyle.Grid) {
+
+            // FIXME: hard-coded grid logic
+            if (ViewLayout != null) {
                 icell_area.X = (int)view_point.X + Allocation.X;
                 icell_area.Width = ChildSize.Width;
                 icell_area.Height = ChildSize.Height;
@@ -770,35 +773,41 @@ namespace Hyena.Data.Gui
 
         protected int GetModelRowAt (int x, int y)
         {
-            if (y < 0 || ChildSize.Height <= 0) {
+            var child_width = ChildSize.Width;
+            var child_height = ChildSize.Height;
+
+            if (y < 0 || child_height <= 0) {
                 return -1;
             }
 
-            if (LayoutStyle == DataViewLayoutStyle.Grid) {
-                if (ChildSize.Width <= 0) {
+            // FIXME: hard-coded grid logic
+            if (ViewLayout != null) {
+                if (child_width <= 0) {
                     return -1;
                 }
 
-                int v_page_offset = VadjustmentValue % ChildSize.Height;
-                int h_page_offset = HadjustmentValue % ChildSize.Width;
-                int first_row = VadjustmentValue / ChildSize.Height;
-                int first_col = HadjustmentValue / ChildSize.Width;
-                int row_offset = (y + v_page_offset) / ChildSize.Height;
-                int col_offset = Math.Min ((x + h_page_offset) / ChildSize.Width, GridColumnsInView);
-                int model_row = ((first_row + row_offset) * GridColumnsInView) + first_col + col_offset;
+                int v_page_offset = VadjustmentValue % child_height;
+                int h_page_offset = HadjustmentValue % child_width;
+                int first_row = VadjustmentValue / child_height;
+                int first_col = HadjustmentValue / child_width;
+                int row_offset = (y + v_page_offset) / child_height;
+                int col_offset = Math.Min ((x + h_page_offset) / child_width, 1/*GridColumnsInView*/);
+                int model_row = ((first_row + row_offset) * 1/*GridColumnsInView*/) + first_col + col_offset;
+
                 return model_row;
             } else {
-                int v_page_offset = VadjustmentValue % ChildSize.Height;
-                int first_row = VadjustmentValue / ChildSize.Height;
-                int row_offset = (y + v_page_offset) / ChildSize.Height;
+                int v_page_offset = VadjustmentValue % child_height;
+                int first_row = VadjustmentValue / child_height;
+                int row_offset = (y + v_page_offset) / child_height;
                 return first_row + row_offset;
             }
         }
 
         protected Cairo.PointD GetViewPointForModelRow (int row)
         {
-            if (LayoutStyle == DataViewLayoutStyle.Grid) {
-                int cols = GridColumnsInView;
+            // FIXME: hard-coded grid logic
+            if (ViewLayout != null) {
+                int cols = 1/*GridColumnsInView*/;
                 return new Cairo.PointD (
                     row  == 0 ? 0 : ChildSize.Width * (cols % row),
                     cols == 0 ? 0 : ChildSize.Height * (row / cols)
@@ -832,6 +841,8 @@ namespace Hyena.Data.Gui
                 vadjustment = vadj;
             }
 
+            // FIXME: with ViewLayout, hadj and vadj should be unified
+            // since the layout will take the header into account...
             if (hadjustment != null) {
                 hadjustment.Upper = header_width;
                 hadjustment.StepIncrement = 10.0;
@@ -841,10 +852,15 @@ namespace Hyena.Data.Gui
             }
 
             if (vadjustment != null && model != null) {
-                vadjustment.Upper = LayoutStyle == DataViewLayoutStyle.List
-                    ? ChildSize.Height * model.Count
-                    : (int)Math.Ceiling (model.Count / (double)GridColumnsInView) * ChildSize.Height;
-                vadjustment.StepIncrement = ChildSize.Height;
+                // FIXME: hard-coded grid logic
+                if (ViewLayout != null) {
+                    vadjustment.Upper = ViewLayout.VirtualSize.Height;
+                    vadjustment.StepIncrement = ViewLayout.ChildSize.Height;
+                } else {
+                    vadjustment.Upper = ChildSize.Height * model.Count;
+                    vadjustment.StepIncrement = ChildSize.Height;
+                }
+
                 if (vadjustment.Value + vadjustment.PageSize > vadjustment.Upper) {
                     vadjustment.Value = vadjustment.Upper - vadjustment.PageSize;
                 }
@@ -863,11 +879,19 @@ namespace Hyena.Data.Gui
         {
             InvalidateHeader ();
             InvalidateList ();
+
+            if (ViewLayout != null) {
+                ViewLayout.UpdatePosition (HadjustmentValue, VadjustmentValue);
+            }
         }
 
         private void OnVadjustmentChanged (object o, EventArgs args)
         {
             InvalidateList ();
+
+            if (ViewLayout != null) {
+                ViewLayout.UpdatePosition (HadjustmentValue, VadjustmentValue);
+            }
         }
 
         public void ScrollTo (double val)
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
index 8663161..1aa8b58 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
@@ -84,13 +84,16 @@ namespace Hyena.Data.Gui
             }
         }
 
-
         private void RefreshViewForModel (double? vpos)
         {
             if (Model == null) {
                 return;
             }
 
+            if (ViewLayout != null) {
+                ViewLayout.UpdateModelRowCount (Model.Count);
+            }
+
             UpdateAdjustments ();
 
             if (vpos != null) {
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
index 849614b..944e256 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
@@ -66,7 +66,12 @@ namespace Hyena.Data.Gui
             changing_style = false;
 
             base.OnStyleSet (old_style);
-            OnInvalidateMeasure ();
+
+            // FIXME: legacy list foo
+            if (ViewLayout == null) {
+                OnInvalidateMeasure ();
+            }
+
             theme = Hyena.Gui.Theming.ThemeEngine.CreateTheme (this);
 
             // Save the drawable so we can reuse it
@@ -98,10 +103,16 @@ namespace Hyena.Data.Gui
             cell_context.Context = cairo_context;
             cell_context.Layout = pango_layout;
 
-            OnMeasure ();
+            // FIXME: legacy list foo
+            if (ViewLayout == null) {
+                OnMeasure ();
+            }
 
             Theme.DrawFrameBackground (cairo_context, Allocation, true);
-            if (header_visible && LayoutStyle != DataViewLayoutStyle.Grid && column_controller != null) {
+
+            // FIXME: ViewLayout will never be null in the future but we'll need
+            // to deterministically render a header somehow...
+            if (header_visible && ViewLayout == null && column_controller != null) {
                 PaintHeader (damage);
             }
 
@@ -112,10 +123,12 @@ namespace Hyena.Data.Gui
             }
 
             if (Model != null) {
-                if (LayoutStyle == DataViewLayoutStyle.List) {
+                // FIXME: ViewLayout will never be null in
+                // the future, PaintList will go away
+                if (ViewLayout == null) {
                     PaintList (damage);
                 } else {
-                    PaintGrid (damage);
+                    PaintView (damage);
                 }
             }
 
@@ -445,14 +458,10 @@ namespace Hyena.Data.Gui
 
 #endregion
 
-#region Grid Rendering
+#region View Layout Rendering
 
-        private void PaintGrid (Rectangle clip)
+        private void PaintView (Rectangle clip)
         {
-            if (ChildSize.Width <= 0 || ChildSize.Height <= 0) {
-                return;
-            }
-            
             clip.Intersect (list_rendering_alloc);
             cairo_context.Rectangle (clip.X, clip.Y, clip.Width, clip.Height);
             cairo_context.Clip ();
@@ -460,28 +469,16 @@ namespace Hyena.Data.Gui
             cell_context.Clip = clip;
             cell_context.TextAsForeground = false;
 
-            int rows_in_view = RowsInView;
-            int columns_in_view = GridColumnsInView;
-            int cell_height = ChildSize.Height;
-            int cell_width = ChildSize.Width + ((list_rendering_alloc.Width -
-                columns_in_view * ChildSize.Width) / columns_in_view);
-            int vadjustment_value = VadjustmentValue;
-
-            int offset = list_rendering_alloc.Y - vadjustment_value % ChildSize.Height;
-            int first_model_row = (int)Math.Floor (vadjustment_value / (double)ChildSize.Height) * columns_in_view;
-            int last_model_row = Math.Min (model.Count, first_model_row + rows_in_view * columns_in_view);
-
-            var grid_cell_alloc = new Rectangle () {
-                X = list_rendering_alloc.X,
-                Y = offset,
-                Width = ChildSize.Width,
-                Height = ChildSize.Height
-            };
-
             selected_rows.Clear ();
 
-            for (int model_row_index = first_model_row, view_row_index = 0, view_column_index = 0;
-                model_row_index < last_model_row; model_row_index++) {
+            for (int layout_index = 0; layout_index < ViewLayout.ChildCount; layout_index++) {
+                var layout_child = ViewLayout[layout_index];
+                var model_row_index = layout_child.ModelRowIndex;
+                var child_allocation = layout_child.Allocation;
+
+                if (model_row_index < 0 || model_row_index >= Model.Count) {
+                    break;
+                }
 
                 if (Selection != null && Selection.Contains (model_row_index)) {
                     selected_rows.Add (model_row_index);
@@ -492,28 +489,18 @@ namespace Hyena.Data.Gui
                     }
 
                     Theme.DrawRowSelection (cairo_context,
-                        grid_cell_alloc.X, grid_cell_alloc.Y,
-                        grid_cell_alloc.Width, grid_cell_alloc.Height,
+                        child_allocation.X, child_allocation.Y,
+                        child_allocation.Width, child_allocation.Height,
                         true, true, selection_color, CairoCorners.All);
                 }
 
                 cell_context.ModelRowIndex = model_row_index;
-                cell_context.ViewRowIndex = view_row_index;
-                cell_context.ViewColumnIndex = view_column_index;
+                // cell_context.ViewRowIndex = view_row_index;
+                // cell_context.ViewColumnIndex = view_column_index;
 
                 var item = model[model_row_index];
-                PaintCell (item, 0, model_row_index, grid_cell_alloc,
+                PaintCell (item, 0, model_row_index, child_allocation,
                     IsRowOpaque (item), IsRowBold (item), StateType.Normal, false);
-
-                if (++view_column_index % columns_in_view == 0) {
-                    view_row_index++;
-                    view_column_index = 0;
-
-                    grid_cell_alloc.Y += cell_height;
-                    grid_cell_alloc.X = list_rendering_alloc.X;
-                } else {
-                    grid_cell_alloc.X += cell_width;
-                }
             }
 
             cairo_context.ResetClip ();
@@ -548,15 +535,7 @@ namespace Hyena.Data.Gui
             }
         }
 
-        private DataViewLayoutStyle layout_style = DataViewLayoutStyle.List;
-        public DataViewLayoutStyle LayoutStyle {
-            get { return layout_style; }
-            set {
-                layout_style = value;
-                OnInvalidateMeasure ();
-            }
-        }
-
+// FIXME: Obsolete all this measure stuff on the view since it's in the layout
 #region Measuring
 
         private Size child_size = Size.Empty;
@@ -576,8 +555,8 @@ namespace Hyena.Data.Gui
 
         protected virtual Size OnMeasureChild ()
         {
-            return LayoutStyle == DataViewLayoutStyle.Grid
-                ? new Size (48, 48)
+            return ViewLayout != null
+                ? ViewLayout.ChildSize
                 : new Size (0, ColumnCellText.ComputeRowHeight (this));
         }
 
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs
index 3bacd6d..206cc4d 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs
@@ -176,9 +176,15 @@ namespace Hyena.Data.Gui
                 model.RowsInView = RowsInView;
             }
 
+            OnInvalidateMeasure ();
             InvalidateList ();
+
+            if (ViewLayout != null) {
+                ViewLayout.Allocate (list_rendering_alloc);
+            }
         }
 
+        // FIXME: obsolete
         protected int RowsInView {
             get {
                 if (ChildSize.Height <= 0) {
@@ -190,13 +196,12 @@ namespace Hyena.Data.Gui
             }
         }
 
-        protected int GridColumnsInView {
-            get {
-                if (ChildSize.Width <= 0) {
-                    return 0;
-                }
-
-                return Math.Max (list_rendering_alloc.Width / ChildSize.Width, 1);
+        private DataViewLayout view_layout;
+        protected DataViewLayout ViewLayout {
+            get { return view_layout; }
+            set {
+                view_layout = value;
+                QueueResize ();
             }
         }
     }
diff --git a/src/Libraries/Hyena.Gui/Hyena.Gui.csproj b/src/Libraries/Hyena.Gui/Hyena.Gui.csproj
index ae8569b..a8162bc 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Gui.csproj
+++ b/src/Libraries/Hyena.Gui/Hyena.Gui.csproj
@@ -88,7 +88,6 @@
     <Compile Include="Hyena.Gui\GtkUtilities.cs" />
     <Compile Include="Hyena.Data.Gui\CellContext.cs" />
     <Compile Include="Hyena.Data.Gui\IHeaderCell.cs" />
-    <Compile Include="Hyena.Data.Gui\DataViewLayoutStyle.cs" />
     <Compile Include="Hyena.Widgets\ScrolledWindow.cs" />
     <Compile Include="Hyena.Widgets\RoundedFrame.cs" />
     <Compile Include="Hyena.Gui.Theatrics\Actor.cs" />
@@ -175,6 +174,8 @@
     <Compile Include="Hyena.Widgets\EntryPopup.cs" />
     <Compile Include="Hyena.Widgets\SimpleTable.cs" />
     <Compile Include="Hyena.Gui\PangoExtensions.cs" />
+    <Compile Include="Hyena.Data.Gui\DataViewLayout.cs" />
+    <Compile Include="Hyena.Data.Gui\DataViewLayoutGrid.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ProjectExtensions>
diff --git a/src/Libraries/Hyena.Gui/Makefile.am b/src/Libraries/Hyena.Gui/Makefile.am
index 0876faa..0eeedef 100644
--- a/src/Libraries/Hyena.Gui/Makefile.am
+++ b/src/Libraries/Hyena.Gui/Makefile.am
@@ -18,7 +18,8 @@ SOURCES =  \
 	Hyena.Data.Gui/ColumnCellText.cs \
 	Hyena.Data.Gui/ColumnController.cs \
 	Hyena.Data.Gui/ColumnHeaderCellText.cs \
-	Hyena.Data.Gui/DataViewLayoutStyle.cs \
+	Hyena.Data.Gui/DataViewLayout.cs \
+	Hyena.Data.Gui/DataViewLayoutGrid.cs \
 	Hyena.Data.Gui/IHeaderCell.cs \
 	Hyena.Data.Gui/IInteractiveCell.cs \
 	Hyena.Data.Gui/IListView.cs \
@@ -51,19 +52,19 @@ SOURCES =  \
 	Hyena.Gui.Theming/Theme.cs \
 	Hyena.Gui.Theming/ThemeContext.cs \
 	Hyena.Gui.Theming/ThemeEngine.cs \
+	Hyena.Gui/ActionManager.cs \
 	Hyena.Gui/BaseWidgetAccessible.cs \
 	Hyena.Gui/CairoExtensions.cs \
 	Hyena.Gui/CleanRoomStartup.cs \
 	Hyena.Gui/CompositeUtils.cs \
 	Hyena.Gui/Contrast.cs \
 	Hyena.Gui/DragDropList.cs \
-	Hyena.Gui/ActionManager.cs \
-	Hyena.Gui/HyenaActionGroup.cs \
 	Hyena.Gui/EditableEraseAction.cs \
 	Hyena.Gui/EditableInsertAction.cs \
 	Hyena.Gui/EditableUndoAdapter.cs \
 	Hyena.Gui/GtkUtilities.cs \
 	Hyena.Gui/GtkWorkarounds.cs \
+	Hyena.Gui/HyenaActionGroup.cs \
 	Hyena.Gui/PangoCairoHelper.cs \
 	Hyena.Gui/PangoExtensions.cs \
 	Hyena.Gui/PixbufImageSurface.cs \
@@ -91,9 +92,9 @@ SOURCES =  \
 	Hyena.Widgets/AnimatedVBox.cs \
 	Hyena.Widgets/AnimatedWidget.cs \
 	Hyena.Widgets/ComplexMenuItem.cs \
+	Hyena.Widgets/EntryPopup.cs \
 	Hyena.Widgets/GenericToolItem.cs \
 	Hyena.Widgets/HigMessageDialog.cs \
-	Hyena.Widgets/EntryPopup.cs \
 	Hyena.Widgets/ImageButton.cs \
 	Hyena.Widgets/MenuButton.cs \
 	Hyena.Widgets/MessageBar.cs \
diff --git a/src/Libraries/Hyena/Makefile.am b/src/Libraries/Hyena/Makefile.am
index 2f15824..0053030 100644
--- a/src/Libraries/Hyena/Makefile.am
+++ b/src/Libraries/Hyena/Makefile.am
@@ -117,10 +117,10 @@ SOURCES =  \
 	Hyena/Log.cs \
 	Hyena/PlatformUtil.cs \
 	Hyena/StringUtil.cs \
-	Hyena/ThreadAssist.cs \
 	Hyena/Tests/CryptoUtilTests.cs \
 	Hyena/Tests/StringUtilTests.cs \
 	Hyena/Tests/TestBase.cs \
+	Hyena/ThreadAssist.cs \
 	Hyena/Timer.cs \
 	Hyena/UndoManager.cs \
 	Hyena/XdgBaseDirectorySpec.cs



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