[hyena] [Hyena.Data.Gui] Work to unify list and grid modes



commit 832077bc3e8515e065d0cbbd69b46b0484904444
Author: Gabriel Burt <gabriel burt gmail com>
Date:   Tue Oct 19 15:18:36 2010 -0500

    [Hyena.Data.Gui] Work to unify list and grid modes
    
    Make ColumnCell subclass from DataViewChild, and add DataViewChildBox
    that can be used to pack multiple DataViewChild objects (including other
    boxes) into a single cell.  Can be used to construct the album browser
    grid cells from an image cell and two text cells, instead of one
    monolithic cell with complicated, repetitive drawing/measuring logic.

 Hyena.Gui/Hyena.Data.Gui/CellContext.cs            |    1 +
 Hyena.Gui/Hyena.Data.Gui/Column.cs                 |    2 +-
 Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs             |   99 +++---------
 Hyena.Gui/Hyena.Data.Gui/ColumnCellCheckBox.cs     |   22 ++-
 Hyena.Gui/Hyena.Data.Gui/ColumnCellRating.cs       |   24 ++-
 Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs         |   62 +++++--
 Hyena.Gui/Hyena.Data.Gui/ColumnHeaderCellText.cs   |    6 +-
 Hyena.Gui/Hyena.Data.Gui/DataViewChild.cs          |   98 ++++++++++-
 Hyena.Gui/Hyena.Data.Gui/DataViewChildBox.cs       |  181 ++++++++++++++++++++
 Hyena.Gui/Hyena.Data.Gui/IInteractiveCell.cs       |    9 +-
 Hyena.Gui/Hyena.Data.Gui/ListView/ListView.cs      |    1 +
 Hyena.Gui/Hyena.Data.Gui/ListView/ListViewBase.cs  |    4 +-
 .../ListView/ListView_Interaction.cs               |    9 +-
 .../Hyena.Data.Gui/ListView/ListView_Rendering.cs  |   17 ++
 Hyena.Gui/Hyena.Gui.Canvas/Thickness.cs            |    2 +
 Hyena.Gui/Makefile.am                              |    1 +
 16 files changed, 407 insertions(+), 131 deletions(-)
---
diff --git a/Hyena.Gui/Hyena.Data.Gui/CellContext.cs b/Hyena.Gui/Hyena.Data.Gui/CellContext.cs
index 8927060..3bdb05e 100644
--- a/Hyena.Gui/Hyena.Data.Gui/CellContext.cs
+++ b/Hyena.Gui/Hyena.Data.Gui/CellContext.cs
@@ -52,5 +52,6 @@ namespace Hyena.Data.Gui
         public int ViewRowIndex { get; set; }
         public int ViewColumnIndex { get; set; }
         public int ModelRowIndex { get; set; }
+        public bool IsRtl { get; set; }
     }
 }
diff --git a/Hyena.Gui/Hyena.Data.Gui/Column.cs b/Hyena.Gui/Hyena.Data.Gui/Column.cs
index 5840764..90e4238 100644
--- a/Hyena.Gui/Hyena.Data.Gui/Column.cs
+++ b/Hyena.Gui/Hyena.Data.Gui/Column.cs
@@ -156,7 +156,7 @@ namespace Hyena.Data.Gui
 
                 // Pretty sure the 3* is needed here only b/c of the " - 8" in ColumnCellText;
                 // See TODO there
-                w += 3 * ColumnHeaderCellText.Spacing;
+                w += 3 * (int)header_cell.Padding.Left;
                 if (this is ISortableColumn) {
                     w += ColumnHeaderCellText.GetArrowWidth (headerHeight);
                 }
diff --git a/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs b/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs
index 20e8c57..89512e0 100644
--- a/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs
+++ b/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs
@@ -31,18 +31,13 @@ using System.Reflection;
 using Gtk;
 using Cairo;
 
+using Hyena.Gui.Canvas;
 using Hyena.Data.Gui.Accessibility;
 
 namespace Hyena.Data.Gui
 {
-    public abstract class ColumnCell
+    public abstract class ColumnCell : DataViewChild
     {
-        private bool expand;
-        private string property, sub_property;
-        private PropertyInfo property_info, sub_property_info;
-        private object bound_object;
-        private object bound_object_parent;
-
         public virtual Atk.Object GetAccessible (ICellAccessibleParent parent)
         {
             return new ColumnCellAccessible (BoundObject, this, parent);
@@ -61,98 +56,44 @@ namespace Hyena.Data.Gui
 
         public void BindListItem (object item)
         {
-            if (item == null) {
-                bound_object_parent = null;
-                bound_object = null;
-                return;
-            }
-
-            bound_object_parent = item;
-
-            if (property != null) {
-                EnsurePropertyInfo ();
-                bound_object = property_info.GetValue (bound_object_parent, null);
-
-                if (sub_property != null) {
-                    EnsurePropertyInfo (sub_property, ref sub_property_info, bound_object);
-                    bound_object = sub_property_info.GetValue (bound_object, null);
-                }
-            } else {
-                bound_object = bound_object_parent;
-            }
+            BindDataItem (item);
         }
 
-        private void EnsurePropertyInfo ()
+        public virtual void NotifyThemeChange ()
         {
-            EnsurePropertyInfo (property, ref property_info, bound_object_parent);
         }
 
-        private void EnsurePropertyInfo (string name, ref PropertyInfo prop, object obj)
+        public virtual Gdk.Size Measure (Widget widget)
         {
-            if (prop == null || prop.ReflectedType != obj.GetType ()) {
-                prop = obj.GetType ().GetProperty (name);
-                if (prop == null) {
-                    throw new Exception (String.Format (
-                        "In {0}, type {1} does not have property {2}",
-                        this, obj.GetType (), name
-                    ));
-                }
-            }
+            return Gdk.Size.Empty;
         }
 
-        public virtual void NotifyThemeChange ()
+        protected override void RenderCore (CellContext context)
         {
+            Render (context, context.State, Allocation.Width, Allocation.Height);
         }
 
-        protected Type BoundType {
-            get { return bound_object.GetType (); }
-        }
-
-        protected object BoundObject {
-            get { return bound_object; }
-            set {
-                if (property != null) {
-                    EnsurePropertyInfo ();
-                    property_info.SetValue (bound_object_parent, value, null);
-                }
-            }
-        }
+        public abstract void Render (CellContext context, StateType state, double cellWidth, double cellHeight);
 
-        protected object BoundObjectParent {
-            get { return bound_object_parent; }
-        }
+        public bool Expand { get; set; }
 
-        public abstract void Render (CellContext context, StateType state, double cellWidth, double cellHeight);
+        public Size? FixedSize { get; set; }
 
-        public virtual Gdk.Size Measure (Gtk.Widget widget)
+        public override Size Measure (Size available)
         {
-            return Gdk.Size.Empty;
+            // FIXME
+            return FixedSize ?? new Size (0, 0);
         }
 
-        public bool Expand {
-            get { return expand; }
-            set { expand = value; }
-        }
-
-        public DataViewLayout ViewLayout { get; set; }
-
-        public string Property {
-            get { return property; }
-            set {
-                property = value;
-                if (value != null) {
-                    int i = value.IndexOf (".");
-                    if (i != -1) {
-                        property = value.Substring (0, i);
-                        SubProperty = value.Substring (i + 1, value.Length - i - 1);
-                    }
-                }
+        public override void Invalidate ()
+        {
+            if (ParentLayout != null) {
+                base.Invalidate ();
             }
         }
 
-        public string SubProperty {
-            get { return sub_property; }
-            set { sub_property = value; }
+        public override void Arrange ()
+        {
         }
     }
 }
diff --git a/Hyena.Gui/Hyena.Data.Gui/ColumnCellCheckBox.cs b/Hyena.Gui/Hyena.Data.Gui/ColumnCellCheckBox.cs
index 0697080..444083a 100644
--- a/Hyena.Gui/Hyena.Data.Gui/ColumnCellCheckBox.cs
+++ b/Hyena.Gui/Hyena.Data.Gui/ColumnCellCheckBox.cs
@@ -29,6 +29,8 @@
 using System;
 using Gtk;
 
+using Hyena.Gui.Canvas;
+
 namespace Hyena.Data.Gui
 {
     public class ColumnCellCheckBox : ColumnCell, IInteractiveCell, ISizeRequestCell
@@ -39,10 +41,15 @@ namespace Hyena.Data.Gui
         {
         }
 
+        public override Size Measure (Size available)
+        {
+            return new Size (Size + 2 * Xpad, Size + 2 * Ypad);
+        }
+
         public override void Render (CellContext context, StateType state, double cellWidth, double cellHeight)
         {
-            int cell_width = context.Area.Width - 2 * Xpad;
-            int cell_height = context.Area.Height - 2 * Ypad;
+            int cell_width = (int)cellWidth - 2 * Xpad;
+            int cell_height = (int)cellHeight - 2 * Ypad;
             int x = context.Area.X + xpad + ((cell_width - Size) / 2);
             int y = context.Area.Y + ypad + ((cell_height - Size) / 2);
 
@@ -58,7 +65,7 @@ namespace Hyena.Data.Gui
         private object last_pressed_bound;
         private object last_hover_bound;
 
-        public bool ButtonEvent (int x, int y, bool pressed, Gdk.EventButton evnt)
+        public override bool ButtonEvent (Point press, bool pressed, uint button)
         {
             if (pressed) {
                 last_pressed_bound = BoundObjectParent;
@@ -73,24 +80,29 @@ namespace Hyena.Data.Gui
                 if (handler != null) {
                     handler (BoundObjectParent, EventArgs.Empty);
                 }
+
+                Invalidate ();
             }
 
             return true;
         }
 
-        public bool MotionEvent (int x, int y, Gdk.EventMotion evnt)
+        public override bool CursorMotionEvent (Point motion)
         {
             if (last_hover_bound == BoundObjectParent) {
                 return false;
             }
 
             last_hover_bound = BoundObjectParent;
+            Invalidate ();
             return true;
         }
 
-        public bool PointerLeaveEvent ()
+        public override bool CursorLeaveEvent ()
         {
+            base.CursorLeaveEvent ();
             last_hover_bound = null;
+            Invalidate ();
             return true;
         }
 
diff --git a/Hyena.Gui/Hyena.Data.Gui/ColumnCellRating.cs b/Hyena.Gui/Hyena.Data.Gui/ColumnCellRating.cs
index e1efeb2..640cd93 100644
--- a/Hyena.Gui/Hyena.Data.Gui/ColumnCellRating.cs
+++ b/Hyena.Gui/Hyena.Data.Gui/ColumnCellRating.cs
@@ -30,6 +30,7 @@ using System;
 using Gtk;
 
 using Hyena.Gui;
+using Hyena.Gui.Canvas;
 using Hyena.Gui.Theming;
 
 namespace Hyena.Data.Gui
@@ -47,9 +48,14 @@ namespace Hyena.Data.Gui
             Xpad = 0;
         }
 
+        public override Size Measure (Size available)
+        {
+            return new Size (renderer.Width, renderer.Height);
+        }
+
         public override void Render (CellContext context, StateType state, double cellWidth, double cellHeight)
         {
-            Gdk.Rectangle area = new Gdk.Rectangle (0, 0, context.Area.Width, context.Area.Height);
+            Gdk.Rectangle area = new Gdk.Rectangle (0, 0, (int)cellWidth, (int)cellHeight);
 
             // FIXME: Compute font height and set to renderer.Size
 
@@ -65,7 +71,7 @@ namespace Hyena.Data.Gui
             actual_area_hack = area;
         }
 
-        public bool ButtonEvent (int x, int y, bool pressed, Gdk.EventButton evnt)
+        public override bool ButtonEvent (Point press, bool pressed, uint button)
         {
             if (ReadOnly) {
                 return false;
@@ -76,21 +82,22 @@ namespace Hyena.Data.Gui
                 return false;
             }
 
-            if (last_pressed_bound == BoundObjectParent) {
-                Value = RatingFromPosition (x);
+            if (last_pressed_bound == BoundObjectParent && last_pressed_bound != null) {
+                Value = RatingFromPosition (press.X);
+                Invalidate ();
                 last_pressed_bound = null;
             }
 
             return true;
         }
 
-        public bool MotionEvent (int x, int y, Gdk.EventMotion evnt)
+        public override bool CursorMotionEvent (Point motion)
         {
             if (ReadOnly) {
                 return false;
             }
 
-            int value = RatingFromPosition (x);
+            int value = RatingFromPosition (motion.X);
 
             if (hover_bound == BoundObjectParent && value == hover_value) {
                 return false;
@@ -98,13 +105,16 @@ namespace Hyena.Data.Gui
 
             hover_bound = BoundObjectParent;
             hover_value = value;
+            Invalidate ();
             return true;
         }
 
-        public bool PointerLeaveEvent ()
+        public override bool CursorLeaveEvent ()
         {
+            base.CursorLeaveEvent ();
             hover_bound = null;
             hover_value = MinRating - 1;
+            Invalidate ();
             return true;
         }
 
diff --git a/Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs b/Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs
index 0a2c126..7662b69 100644
--- a/Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs
+++ b/Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs
@@ -31,6 +31,7 @@ using Gtk;
 using Cairo;
 
 using Hyena.Gui;
+using Hyena.Gui.Canvas;
 using Hyena.Gui.Theming;
 using Hyena.Data.Gui.Accessibility;
 
@@ -38,8 +39,6 @@ namespace Hyena.Data.Gui
 {
     public class ColumnCellText : ColumnCell, ISizeRequestCell, ITextCell, ITooltipCell
     {
-        internal const int Spacing = 4;
-
         public delegate string DataHandler ();
 
         private Pango.Weight font_weight = Pango.Weight.Normal;
@@ -54,6 +53,7 @@ namespace Hyena.Data.Gui
 
         public ColumnCellText (string property, bool expand) : base (property, expand)
         {
+            Padding = new Thickness (4, 0);
         }
 
         public override Atk.Object GetAccessible (ICellAccessibleParent parent)
@@ -79,6 +79,25 @@ namespace Hyena.Data.Gui
             RestrictSize = true;
         }
 
+        int? height;
+        public override Size Measure (Size available)
+        {
+            //int min, max;
+            //GetWidthRange (ParentLayout.View.PangoLayout, out min, out max);
+            //
+            if (height == null) {
+                using (var layout = new Pango.Layout (ParentLayout.View.PangoContext)) {
+                    if (layout.FontDescription == null) {
+                        layout.FontDescription = new Pango.FontDescription ();
+                    }
+                    UpdateText (layout, 100, "Woo Mar");
+                    height = TextHeight;
+                }
+            }
+
+            return FixedSize ?? new Size (0, (double)height + Padding.Y);
+        }
+
         public override void Render (CellContext context, StateType state, double cellWidth, double cellHeight)
         {
             UpdateText (context, cellWidth);
@@ -86,34 +105,37 @@ namespace Hyena.Data.Gui
                 return;
             }
 
-            context.Context.Rectangle (0, 0, cellWidth, cellHeight);
-            context.Context.Clip ();
-            context.Context.MoveTo (Spacing, ((int)cellHeight - text_height) / 2);
+            //context.Context.Rectangle (0, 0, cellWidth, cellHeight);
+            //context.Context.Clip ();
+            context.Context.MoveTo (Padding.Left, ((int)cellHeight - text_height) / 2);
             Cairo.Color color = context.Theme.Colors.GetWidgetColor (
                 context.TextAsForeground ? GtkColorClass.Foreground : GtkColorClass.Text, state);
-            color.A = context.Opaque ? 1.0 : 0.5;
+            color.A = Alpha ?? (context.Opaque ? 1.0 : 0.5);
             context.Context.Color = color;
 
             PangoCairoHelper.ShowLayout (context.Context, context.Layout);
-            context.Context.ResetClip ();
+            //context.Context.ResetClip ();
         }
 
         public void UpdateText (CellContext context, double cellWidth)
         {
             string text = last_text = GetText (BoundObject);
+            UpdateText (context.Layout, cellWidth, text);
+        }
+
+        public void UpdateText (Pango.Layout layout, double cellWidth, string text)
+        {
             if (String.IsNullOrEmpty (text)) {
                 return;
             }
 
-            // TODO why doesn't Spacing (eg 4 atm) work here instead of 8?  Rendering
-            // seems to be off when changed to Spacing/4
-            context.Layout.Width = (int)((cellWidth - 8) * Pango.Scale.PangoScale);
-            context.Layout.FontDescription.Weight = font_weight;
-            context.Layout.Ellipsize = EllipsizeMode;
-            context.Layout.Alignment = alignment;
-            UpdateLayout (context.Layout, text);
-            context.Layout.GetPixelSize (out text_width, out text_height);
-            is_ellipsized = context.Layout.IsEllipsized;
+            layout.Width = (int)((cellWidth - Padding.X) * Pango.Scale.PangoScale);
+            layout.FontDescription.Weight = font_weight;
+            layout.Ellipsize = EllipsizeMode;
+            layout.Alignment = alignment;
+            UpdateLayout (layout, text);
+            layout.GetPixelSize (out text_width, out text_height);
+            is_ellipsized = layout.IsEllipsized;
         }
 
         private static char[] lfcr = new char[] {'\n', '\r'};
@@ -146,7 +168,7 @@ namespace Hyena.Data.Gui
             if (text_format == null) {
                 return text;
             }
-            return String.Format (text_format, text);
+            return String.Format (text_format, UseMarkup ? GLib.Markup.EscapeText (text) : text);
         }
 
         private bool is_ellipsized = false;
@@ -206,14 +228,14 @@ namespace Hyena.Data.Gui
             if (!String.IsNullOrEmpty (MinString)) {
                 UpdateLayout (layout, MinString);
                 layout.GetPixelSize (out min, out height);
-                min += 2*Spacing;
+                min += (int)Padding.X;
                 //Console.WriteLine ("for {0} got min {1} for {2}", this, min, MinString);
             }
 
             if (!String.IsNullOrEmpty (MaxString)) {
                 UpdateLayout (layout, MaxString);
                 layout.GetPixelSize (out max, out height);
-                max += 2*Spacing;
+                max += (int)Padding.X;
                 //Console.WriteLine ("for {0} got max {1} for {2}", this, max, MaxString);
             }
         }
@@ -229,6 +251,8 @@ namespace Hyena.Data.Gui
             set { use_markup = value; }
         }
 
+        public double? Alpha { get; set; }
+
         #endregion
     }
 }
diff --git a/Hyena.Gui/Hyena.Data.Gui/ColumnHeaderCellText.cs b/Hyena.Gui/Hyena.Data.Gui/ColumnHeaderCellText.cs
index 167cd2f..e661627 100644
--- a/Hyena.Gui/Hyena.Data.Gui/ColumnHeaderCellText.cs
+++ b/Hyena.Gui/Hyena.Data.Gui/ColumnHeaderCellText.cs
@@ -66,10 +66,10 @@ namespace Hyena.Data.Gui
             Gdk.Rectangle arrow_alloc = new Gdk.Rectangle ();
             arrow_alloc.Width = (int)(cellHeight / 3.0);
             arrow_alloc.Height = (int)((double)arrow_alloc.Width / 1.6);
-            arrow_alloc.X = (int)cellWidth - arrow_alloc.Width - Spacing;
+            arrow_alloc.X = (int)cellWidth - arrow_alloc.Width - (int)Padding.Left;
             arrow_alloc.Y = ((int)cellHeight - arrow_alloc.Height) / 2;
 
-            double textWidth = arrow_alloc.X - Spacing;
+            double textWidth = arrow_alloc.X - Padding.Left;
             if (textWidth > 0) {
                 base.Render (context, state, textWidth, cellHeight);
             }
@@ -92,7 +92,7 @@ namespace Hyena.Data.Gui
 
         public static int GetArrowWidth (int headerHeight)
         {
-            return (int)(headerHeight / 3.0) + Spacing;
+            return (int)(headerHeight / 3.0) + 4;
         }
     }
 }
diff --git a/Hyena.Gui/Hyena.Data.Gui/DataViewChild.cs b/Hyena.Gui/Hyena.Data.Gui/DataViewChild.cs
index 30c3886..963b298 100644
--- a/Hyena.Gui/Hyena.Data.Gui/DataViewChild.cs
+++ b/Hyena.Gui/Hyena.Data.Gui/DataViewChild.cs
@@ -29,13 +29,28 @@
 using System;
 using System.Reflection;
 
+using Hyena.Gui;
 using Hyena.Gui.Canvas;
+using Hyena.Gui.Theatrics;
 
 namespace Hyena.Data.Gui
 {
     public abstract class DataViewChild : CanvasItem
     {
-        public DataViewLayout ParentLayout { get; set; }
+        private DataViewLayout layout;
+        public DataViewLayout ParentLayout {
+            get {
+                if (layout == null) {
+                    var parent = Parent as DataViewChild;
+                    if (parent != null) {
+                        layout = parent.ParentLayout;
+                    }
+                }
+                return layout;
+            }
+            set { layout = value; }
+        }
+
         public int ModelRowIndex { get; set; }
 
         protected override void OnInvalidate (Rect area)
@@ -43,12 +58,25 @@ namespace Hyena.Data.Gui
             ParentLayout.View.QueueDirtyRegion (area);
         }
 
+        protected Rect TopLevelAllocation {
+            get {
+                var alloc = Allocation;
+                var top = (CanvasItem)this;
+                while (top.Parent != null) {
+                    alloc.Offset (top.Parent.Allocation);
+                    top = top.Parent;
+                }
+
+                return alloc;
+            }
+        }
+
 #region Data Binding
 
         private PropertyInfo property_info;
         private PropertyInfo sub_property_info;
 
-        public void BindDataItem (object item)
+        public virtual void BindDataItem (object item)
         {
             if (item == null) {
                 BoundObjectParent = null;
@@ -130,7 +158,15 @@ namespace Hyena.Data.Gui
         public Thickness Margin { get; set; }
         public Thickness Padding { get; set; }
 
-        public abstract void Render (CellContext context);
+        public void Render (CellContext context)
+        {
+            RenderCore (context);
+            if (HasPrelight && prelight_opacity > 0) {
+                RenderPrelight (context);
+            }
+        }
+
+        protected abstract void RenderCore (CellContext context);
         public abstract void Arrange ();
         public abstract Size Measure (Size available);
 
@@ -140,11 +176,15 @@ namespace Hyena.Data.Gui
 
         public void Invalidate (Rect area)
         {
-            area.Offset (Allocation.X, Allocation.Y);
-            OnInvalidate (area);
+            if (Parent == null) {
+                OnInvalidate (area);
+            } else {
+                area.Offset (Parent.Allocation.X, Parent.Allocation.Y);
+                Parent.Invalidate (area);
+            }
         }
 
-        public void Invalidate ()
+        public virtual void Invalidate ()
         {
             Invalidate (Allocation);
         }
@@ -161,10 +201,54 @@ namespace Hyena.Data.Gui
 
         public virtual void CursorEnterEvent ()
         {
+            if (HasPrelight) {
+                prelight_in = true;
+                prelight_stage.AddOrReset (this);
+            }
         }
 
-        public virtual void CursorLeaveEvent ()
+        public virtual bool CursorLeaveEvent ()
+        {
+            if (HasPrelight) {
+                prelight_in = false;
+                prelight_stage.AddOrReset (this);
+            }
+            return false;
+        }
+
+        private static Stage<CanvasItem> prelight_stage = new Stage<CanvasItem> (250);
+        private bool prelight_in;
+        private double prelight_opacity;
+
+        static CanvasItem ()
+        {
+            prelight_stage.ActorStep += actor => {
+                var alpha = actor.Target.prelight_opacity;
+                alpha += actor.Target.prelight_in
+                    ? actor.StepDeltaPercent
+                    : -actor.StepDeltaPercent;
+                actor.Target.prelight_opacity = alpha = Math.Max (0.0, Math.Min (1.0, alpha));
+                actor.Target.Invalidate ();
+                return alpha > 0 && alpha < 1;
+            };
+        }
+
+        public bool HasPrelight { get; set; }
+
+        protected virtual void RenderPrelight (CellContext context)
         {
+            var cr = context.Context;
+
+            var x = Allocation.Width / 2.0;
+            var y = Allocation.Height / 2.0;
+            var grad = new Cairo.RadialGradient (x, y, 0, x, y, Allocation.Width / 2.0);
+            grad.AddColorStop (0, new Cairo.Color (0, 0, 0, 0.1 * prelight_opacity));
+            grad.AddColorStop (1, new Cairo.Color (0, 0, 0, 0.35 * prelight_opacity));
+            cr.Pattern = grad;
+            CairoExtensions.RoundedRectangle (cr, 0, 0,
+                Allocation.Width, Allocation.Height, context.Theme.Context.Radius);
+            cr.Fill ();
+            grad.Destroy ();
         }
     }
 }
diff --git a/Hyena.Gui/Hyena.Data.Gui/DataViewChildBox.cs b/Hyena.Gui/Hyena.Data.Gui/DataViewChildBox.cs
new file mode 100644
index 0000000..d70a48e
--- /dev/null
+++ b/Hyena.Gui/Hyena.Data.Gui/DataViewChildBox.cs
@@ -0,0 +1,181 @@
+//
+// DataViewChildBox.cs
+//
+// Author:
+//   Gabriel Burt <gburt 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.Linq;
+using System.Collections.Generic;
+using System.Reflection;
+
+using Hyena.Gui.Canvas;
+
+namespace Hyena.Data.Gui
+{
+    public class DataViewChildBox : DataViewChild
+    {
+        public bool Horizontal { get; set; }
+
+        private List<DataViewChild> children = new List<DataViewChild> ();
+        public IEnumerable<DataViewChild> Children { get { return children; } }
+
+        public DataViewChildBox (params DataViewChild [] children)
+        {
+            Add (children);
+        }
+
+        public void Add (params DataViewChild [] children)
+        {
+            this.children.AddRange (children);
+            ForEach (child => child.Parent = this);
+        }
+
+        private void ForEach (Action<DataViewChild> action)
+        {
+            foreach (var child in Children) {
+                action (child);
+            }
+        }
+
+        public override void BindDataItem (object item)
+        {
+            base.BindDataItem (item);
+            ForEach (child => child.BindDataItem (item));
+        }
+
+        protected override void RenderCore (CellContext context)
+        {
+            var itr = Horizontal && context.IsRtl ? Children.Reverse () : Children;
+            foreach (var child in itr) {
+                RenderChild (child, context);
+            }
+        }
+
+        private void RenderChild (DataViewChild child, CellContext context)
+        {
+            var cairo_context = context.Context;
+            var child_allocation = child.Allocation;
+
+            context.Area = (Gdk.Rectangle) TopLevelAllocation;
+            cairo_context.Save ();
+            cairo_context.Translate (child_allocation.X, child_allocation.Y);
+            //cairo_context.Rectangle (0, 0, child_allocation.Width, child_allocation.Height);
+            //cairo_context.Clip ();
+            child.Render (context);
+            cairo_context.Restore ();
+        }
+
+        public override void Arrange ()
+        {
+            ForEach (child => child.Arrange ());
+        }
+
+        public override Size Measure (Size available)
+        {
+            double x = Padding.Left, y = Padding.Top;
+            double width = 0, height = 0;
+            foreach (var child in Children) {
+                var size = child.Measure (available);
+                child.Allocation = new Rect (x, y, size.Width, size.Height);
+
+                // TODO account for childrens' padding/margin
+                if (Horizontal) {
+                    width  += size.Width;
+                    height = Math.Max (height, size.Height);
+                    x += size.Width;
+                } else {
+                    width  = Math.Max (width, size.Width);
+                    height += size.Height;
+                    y += size.Height;
+                }
+            }
+
+            foreach (var child in Children) {
+                var a = child.Allocation;
+                if (Horizontal) {
+                    child.Allocation = new Rect (a.X, a.Y, a.Width, height);
+                } else {
+                    child.Allocation = new Rect (a.X, a.Y, width, a.Height);
+                }
+            }
+
+            return new Size (width + Padding.X, height + Padding.Y);
+        }
+
+        public override bool ButtonEvent (Point cursor, bool pressed, uint button)
+        {
+            var child = FindChildAt (cursor);
+            return child == null ? false : child.ButtonEvent (ChildCoord (cursor, child), pressed, button);
+        }
+
+        DataViewChild last_motion_child;
+        public override bool CursorMotionEvent (Point cursor)
+        {
+            var child = FindChildAt (cursor);
+            
+            bool new_child = child != last_motion_child;
+            if (new_child) {
+                if (last_motion_child != null) {
+                    last_motion_child.CursorLeaveEvent ();
+                }
+                last_motion_child = child;
+            }
+
+            if (child == null) {
+                return false;
+            }
+
+            if (new_child) {
+                child.CursorEnterEvent ();
+            }
+
+            child.CursorMotionEvent (ChildCoord (cursor, child));
+            return true;
+        }
+
+        public override bool CursorLeaveEvent ()
+        {
+            bool ret = base.CursorLeaveEvent ();
+
+            if (last_motion_child != null) {
+                ret |= last_motion_child.CursorLeaveEvent ();
+                last_motion_child = null;
+            }
+
+            return ret;
+        }
+
+        private DataViewChild FindChildAt (Point pt)
+        {
+            return Children.LastOrDefault (c => pt.X >= c.Allocation.X && pt.Y >= c.Allocation.Y);
+        }
+
+        private Point ChildCoord (Point pt, DataViewChild child)
+        {
+            return new Point (pt.X - child.Allocation.X, pt.Y - child.Allocation.Y);
+        }
+    }
+}
diff --git a/Hyena.Gui/Hyena.Data.Gui/IInteractiveCell.cs b/Hyena.Gui/Hyena.Data.Gui/IInteractiveCell.cs
index f3ed4df..811afb3 100644
--- a/Hyena.Gui/Hyena.Data.Gui/IInteractiveCell.cs
+++ b/Hyena.Gui/Hyena.Data.Gui/IInteractiveCell.cs
@@ -27,14 +27,13 @@
 //
 
 using System;
-using Gdk;
 
 namespace Hyena.Data.Gui
 {
     public interface IInteractiveCell
     {
-        bool ButtonEvent (int x, int y, bool pressed, Gdk.EventButton evnt);
-        bool MotionEvent (int x, int y, Gdk.EventMotion evnt);
-        bool PointerLeaveEvent ();
+        bool ButtonEvent (Hyena.Gui.Canvas.Point cursor,  bool pressed, uint button);
+        bool CursorMotionEvent (Hyena.Gui.Canvas.Point cursor);
+        bool CursorLeaveEvent ();
     }
-}
\ No newline at end of file
+}
diff --git a/Hyena.Gui/Hyena.Data.Gui/ListView/ListView.cs b/Hyena.Gui/Hyena.Data.Gui/ListView/ListView.cs
index e5398c6..d1340fe 100644
--- a/Hyena.Gui/Hyena.Data.Gui/ListView/ListView.cs
+++ b/Hyena.Gui/Hyena.Data.Gui/ListView/ListView.cs
@@ -44,6 +44,7 @@ namespace Hyena.Data.Gui
 
             HasTooltip = true;
             QueryTooltip += OnQueryTooltip;
+            DirectionChanged += (o, a) => SetDirection ();
         }
 
         private void OnQueryTooltip (object o, Gtk.QueryTooltipArgs args)
diff --git a/Hyena.Gui/Hyena.Data.Gui/ListView/ListViewBase.cs b/Hyena.Gui/Hyena.Data.Gui/ListView/ListViewBase.cs
index da18c63..c9f59b0 100644
--- a/Hyena.Gui/Hyena.Data.Gui/ListView/ListViewBase.cs
+++ b/Hyena.Gui/Hyena.Data.Gui/ListView/ListViewBase.cs
@@ -31,7 +31,7 @@ using Gtk;
 
 namespace Hyena.Data.Gui
 {
-    public class ListViewBase : Widget
+    public abstract class ListViewBase : Widget
     {
         protected ListViewBase (IntPtr ptr) : base (ptr)
         {
@@ -61,5 +61,7 @@ namespace Hyena.Data.Gui
                 Height = (int)Math.Ceiling (region.Height)
             });
         }
+
+        public abstract Pango.Layout PangoLayout { get; }
     }
 }
diff --git a/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs b/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
index adc1df7..6b99953 100644
--- a/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
+++ b/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
@@ -337,6 +337,7 @@ namespace Hyena.Data.Gui
                 return false;
             }
 
+            point.Offset (-list_interaction_alloc.X, -list_interaction_alloc.Y);
             point.Offset (-child.VirtualAllocation.X, -child.VirtualAllocation.Y);
 
             if (evnt_motion != null) {
@@ -370,7 +371,7 @@ namespace Hyena.Data.Gui
 
         private void InvalidateLastIcell ()
         {
-            if (last_icell != null && last_icell.PointerLeaveEvent ()) {
+            if (last_icell != null && last_icell.CursorLeaveEvent ()) {
                 QueueDirtyRegion (last_icell_area);
                 last_icell = null;
                 last_icell_area = Gdk.Rectangle.Zero;
@@ -387,7 +388,7 @@ namespace Hyena.Data.Gui
             int yoffset = VadjustmentValue;
 
             if (last_icell_area != icell_area) {
-                if (last_icell != null && last_icell.PointerLeaveEvent ()) {
+                if (last_icell != null && last_icell.CursorLeaveEvent ()) {
                     QueueDirtyRegion (new Gdk.Rectangle () {
                         X = last_icell_area.X - xoffset,
                         Y = last_icell_area.Y - yoffset,
@@ -456,9 +457,9 @@ namespace Hyena.Data.Gui
 
             // Send the cell a synthesized input event
             if (evnt_motion != null) {
-                return icell.MotionEvent (x, y, evnt_motion);
+                return icell.CursorMotionEvent (new Hyena.Gui.Canvas.Point (x, y));
             } else {
-                return icell.ButtonEvent (x, y, press, evnt_button);
+                return icell.ButtonEvent (new Hyena.Gui.Canvas.Point (x, y), press, evnt_button.Button);
             }
         }
 
diff --git a/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs b/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
index 5f0288c..b89b3bd 100644
--- a/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
+++ b/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
@@ -47,6 +47,10 @@ namespace Hyena.Data.Gui
         private CellContext cell_context;
         private Pango.Layout pango_layout;
 
+        public override Pango.Layout PangoLayout {
+            get { return cell_context.Layout; }
+        }
+
         private List<int> selected_rows = new List<int> ();
 
         private Theme theme;
@@ -95,6 +99,19 @@ namespace Hyena.Data.Gui
             cell_context.Theme = theme;
             cell_context.Widget = this;
             cell_context.Drawable = drawable;
+            SetDirection ();
+        }
+
+        private void SetDirection ()
+        {
+            var dir = Direction;
+            if (dir == Gtk.TextDirection.None) {
+                dir = Gtk.Widget.DefaultDirection;
+            }
+
+            if (cell_context != null) {
+                cell_context.IsRtl = dir == Gtk.TextDirection.Rtl;
+            }
         }
 
         protected override bool OnExposeEvent (EventExpose evnt)
diff --git a/Hyena.Gui/Hyena.Gui.Canvas/Thickness.cs b/Hyena.Gui/Hyena.Gui.Canvas/Thickness.cs
index 9d25150..56fa88d 100644
--- a/Hyena.Gui/Hyena.Gui.Canvas/Thickness.cs
+++ b/Hyena.Gui/Hyena.Gui.Canvas/Thickness.cs
@@ -36,6 +36,8 @@ namespace Hyena.Gui.Canvas
         private double top;
         private double right;
         private double bottom;
+
+        public static readonly Thickness Zero = new Thickness (0);
         
         public Thickness (double thickness) 
             : this (thickness, thickness, thickness, thickness)
diff --git a/Hyena.Gui/Makefile.am b/Hyena.Gui/Makefile.am
index a3a5018..27faf5d 100644
--- a/Hyena.Gui/Makefile.am
+++ b/Hyena.Gui/Makefile.am
@@ -21,6 +21,7 @@ SOURCES =  \
 	Hyena.Data.Gui/ColumnController.cs \
 	Hyena.Data.Gui/ColumnHeaderCellText.cs \
 	Hyena.Data.Gui/DataViewChild.cs \
+	Hyena.Data.Gui/DataViewChildBox.cs \
 	Hyena.Data.Gui/DataViewLayout.cs \
 	Hyena.Data.Gui/DataViewLayoutGrid.cs \
 	Hyena.Data.Gui/IHeaderCell.cs \



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