[hyena] implement geo primitives, more canvas logic



commit 5ec9421325928515534d343c1b1eac9403a1935c
Author: Aaron Bockover <abockover novell com>
Date:   Sun Jan 24 00:43:53 2010 -0500

    implement geo primitives, more canvas logic
    
    This is another big refactor going forward with the new data view.
    We've got tons of mixed int/double/gdk/cairo code, and I'm tired of
    it. There are cases where we need the precision of Cairo (double)
    but the flexibility of Gdk (int) rectangle classes for instance.
    
    As the grid progresses to essentially a canvas API, and will continue
    to do so, modeled after the lovely Silverlight layout system, I've
    implemented our own geometry primitives and ported the data view
    parts to use these primitives:
    
     - Point
     - Rect
     - Size
     - Thickness
    
    These provide similar API as Silverlight, with a few additions pulled
    in from managed GDK.
    
    Additionally, the album item has been further simplified by implementing
    full Measure, Arrange, and Render support.
    
    Finally, data binding has been moved into the layout itself, since
    data binding should (and now does) occur during the arrange phase,
    not the render phase.

 src/Hyena.Gui/Hyena.Data.Gui/DataViewChild.cs      |   22 +-
 src/Hyena.Gui/Hyena.Data.Gui/DataViewLayout.cs     |   24 +-
 src/Hyena.Gui/Hyena.Data.Gui/DataViewLayoutGrid.cs |   21 +-
 .../Hyena.Data.Gui/ListView/ListViewBase.cs        |    5 +
 .../ListView/ListView_Interaction.cs               |   30 +--
 .../Hyena.Data.Gui/ListView/ListView_Model.cs      |    6 +-
 .../Hyena.Data.Gui/ListView/ListView_Rendering.cs  |   42 ++--
 .../Hyena.Data.Gui/ListView/ListView_Windowing.cs  |    2 +-
 src/Hyena.Gui/Hyena.Data.Gui/ListViewTestModule.cs |    5 +
 src/Hyena.Gui/Hyena.Gui.Canvas/Point.cs            |   86 ++++++
 src/Hyena.Gui/Hyena.Gui.Canvas/Rect.cs             |  276 ++++++++++++++++++++
 src/Hyena.Gui/Hyena.Gui.Canvas/Size.cs             |  117 +++++++++
 src/Hyena.Gui/Hyena.Gui.Canvas/Thickness.cs        |  126 +++++++++
 src/Hyena.Gui/Hyena.Gui.csproj                     |    8 +
 src/Hyena.Gui/Makefile.am                          |    4 +
 15 files changed, 704 insertions(+), 70 deletions(-)
---
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/DataViewChild.cs b/src/Hyena.Gui/Hyena.Data.Gui/DataViewChild.cs
index a224723..30c3886 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/DataViewChild.cs
+++ b/src/Hyena.Gui/Hyena.Data.Gui/DataViewChild.cs
@@ -29,7 +29,7 @@
 using System;
 using System.Reflection;
 
-using Cairo;
+using Hyena.Gui.Canvas;
 
 namespace Hyena.Data.Gui
 {
@@ -38,7 +38,7 @@ namespace Hyena.Data.Gui
         public DataViewLayout ParentLayout { get; set; }
         public int ModelRowIndex { get; set; }
 
-        protected override void OnInvalidate (Gdk.Rectangle area)
+        protected override void OnInvalidate (Rect area)
         {
             ParentLayout.View.QueueDirtyRegion (area);
         }
@@ -124,17 +124,21 @@ namespace Hyena.Data.Gui
     public abstract class CanvasItem
     {
         public CanvasItem Parent { get; set; }
-        public Gdk.Rectangle Allocation { get; set; }
-        public Gdk.Rectangle VirtualAllocation { get; set; }
+        public Rect Allocation { get; set; }
+        public Rect VirtualAllocation { get; set; }
+
+        public Thickness Margin { get; set; }
+        public Thickness Padding { get; set; }
 
         public abstract void Render (CellContext context);
-        public abstract Gdk.Size Measure ();
+        public abstract void Arrange ();
+        public abstract Size Measure (Size available);
 
-        protected virtual void OnInvalidate (Gdk.Rectangle area)
+        protected virtual void OnInvalidate (Rect area)
         {
         }
 
-        public void Invalidate (Gdk.Rectangle area)
+        public void Invalidate (Rect area)
         {
             area.Offset (Allocation.X, Allocation.Y);
             OnInvalidate (area);
@@ -145,12 +149,12 @@ namespace Hyena.Data.Gui
             Invalidate (Allocation);
         }
 
-        public virtual bool ButtonEvent (int x, int y, bool pressed, uint button)
+        public virtual bool ButtonEvent (Point cursor, bool pressed, uint button)
         {
             return false;
         }
 
-        public virtual bool CursorMotionEvent (int x, int y)
+        public virtual bool CursorMotionEvent (Point cursor)
         {
             return false;
         }
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/DataViewLayout.cs b/src/Hyena.Gui/Hyena.Data.Gui/DataViewLayout.cs
index c4791be..8ebb40c 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/DataViewLayout.cs
+++ b/src/Hyena.Gui/Hyena.Data.Gui/DataViewLayout.cs
@@ -29,6 +29,9 @@
 using System;
 using System.Collections.Generic;
 
+using Hyena.Data;
+using Hyena.Gui.Canvas;
+
 namespace Hyena.Data.Gui
 {
     public abstract class DataViewLayout
@@ -38,14 +41,14 @@ namespace Hyena.Data.Gui
             get { return children; }
         }
 
+        public IListModel Model { get; set; }
         public 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 Rect ActualAllocation { get; protected set; }
+        public Size VirtualSize { get; protected set; }
+        public 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; }
@@ -62,13 +65,12 @@ namespace Hyena.Data.Gui
             InvalidateChildLayout ();
         }
 
-        public void UpdateModelRowCount (int modelRowCount)
+        public void ModelUpdated ()
         {
-            ModelRowCount = modelRowCount;
             InvalidateVirtualSize ();
         }
 
-        public virtual void Allocate (Gdk.Rectangle actualAllocation)
+        public virtual void Allocate (Rect actualAllocation)
         {
             ActualAllocation = actualAllocation;
 
@@ -77,10 +79,10 @@ namespace Hyena.Data.Gui
             InvalidateChildLayout ();
         }
 
-        public virtual DataViewChild FindChildAtPoint (int x, int y)
+        public virtual DataViewChild FindChildAtPoint (Point point)
         {
             return Children.Find (child => child.Allocation.Contains (
-                ActualAllocation.X + x, ActualAllocation.Y + y));
+                ActualAllocation.X + point.X, ActualAllocation.Y + point.Y));
         }
 
         public virtual DataViewChild FindChildAtModelRowIndex (int modelRowIndex)
@@ -93,9 +95,9 @@ namespace Hyena.Data.Gui
         protected abstract void InvalidateChildCollection ();
         protected abstract void InvalidateChildLayout ();
 
-        protected Gdk.Rectangle GetChildVirtualAllocation (Gdk.Rectangle childAllocation)
+        protected Rect GetChildVirtualAllocation (Rect childAllocation)
         {
-            return new Gdk.Rectangle () {
+            return new Rect () {
                 X = childAllocation.X - ActualAllocation.X,
                 Y = childAllocation.Y - ActualAllocation.Y,
                 Width = childAllocation.Width,
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/DataViewLayoutGrid.cs b/src/Hyena.Gui/Hyena.Data.Gui/DataViewLayoutGrid.cs
index 5b36409..b75b80f 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/DataViewLayoutGrid.cs
+++ b/src/Hyena.Gui/Hyena.Data.Gui/DataViewLayoutGrid.cs
@@ -29,6 +29,8 @@
 using System;
 using System.Collections.Generic;
 
+using Hyena.Gui.Canvas;
+
 namespace Hyena.Data.Gui
 {
     public class DataViewLayoutGrid : DataViewLayout
@@ -44,16 +46,18 @@ namespace Hyena.Data.Gui
                 Children.Add (CreateChild ());
             }
 
-            ChildSize = Children[0].Measure ();
+            ChildSize = Children[0].Measure (Size.Empty);
         }
 
         protected override void InvalidateVirtualSize ()
         {
+            int model_rows = Model == null ? 0 : Model.Count;
+
             // FIXME: this is too simplistic and can result in
             // an extra row of allocation, check the model size
-            VirtualSize = new Gdk.Size (
+            VirtualSize = new Size (
                 ChildSize.Width * Math.Max (Columns, 1),
-                (ChildSize.Height * ModelRowCount) / Math.Max (Rows, 1));
+                (ChildSize.Height * model_rows) / Math.Max (Rows, 1));
         }
 
         protected override void InvalidateChildCollection ()
@@ -77,8 +81,8 @@ namespace Hyena.Data.Gui
             }
 
             // 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;
+            double offset = ActualAllocation.Y - YPosition % ChildSize.Height;
+            int first_model_row = (int)Math.Floor (YPosition / ChildSize.Height) * Columns;
             int last_model_row = first_model_row + Rows * Columns;
 
             // Setup for the layout iteration
@@ -89,7 +93,7 @@ namespace Hyena.Data.Gui
 
             // Allocation of the first child in the layout, this
             // will change as we iterate the layout children
-            var child_allocation = new Gdk.Rectangle () {
+            var child_allocation = new Rect () {
                 X = ActualAllocation.X,
                 Y = offset,
                 Width = ChildSize.Width,
@@ -103,6 +107,11 @@ namespace Hyena.Data.Gui
                 child.Allocation = child_allocation;
                 child.VirtualAllocation = GetChildVirtualAllocation (child_allocation);
                 child.ModelRowIndex = model_row_index;
+                if (Model != null) {
+                    child.BindDataItem (Model.GetItem (model_row_index));
+                }
+                child.Measure (ChildSize); // FIXME: Should not do this here...
+                child.Arrange ();
 
                 // Update the allocation for the next child
                 if (++view_column_index % Columns == 0) {
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListViewBase.cs b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListViewBase.cs
index d6c3722..ca4c281 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListViewBase.cs
+++ b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListViewBase.cs
@@ -39,6 +39,11 @@ namespace Hyena.Data.Gui
             QueueDrawArea (region.X, region.Y, region.Width, region.Height);
         }
 
+        public void QueueDirtyRegion (Hyena.Gui.Canvas.Rect region)
+        {
+            QueueDirtyRegion ((Gdk.Rectangle)region);
+        }
+
         public void QueueDirtyRegion (Cairo.Rectangle region)
         {
             QueueDirtyRegion (new Gdk.Rectangle () {
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
index 689e2e7..ca0b51a 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
+++ b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
@@ -33,6 +33,7 @@ using System;
 using Gtk;
 
 using Hyena.Collections;
+using Hyena.Gui.Canvas;
 using Selection = Hyena.Collections.Selection;
 
 namespace Hyena.Data.Gui
@@ -310,32 +311,28 @@ namespace Hyena.Data.Gui
                 return false;
             }
 
-            int x = 0;
-            int y = 0;
+            var point = new Point (0, 0);
             bool handled = false;
 
             var evnt_button = evnt as Gdk.EventButton;
             var evnt_motion = evnt as Gdk.EventMotion;
 
             if (evnt_motion != null) {
-                x = (int)evnt_motion.X;
-                y = (int)evnt_motion.Y;
+                point = new Point (evnt_motion.X, evnt_motion.Y);
             } else if (evnt_button != null) {
-                x = (int)evnt_button.X;
-                y = (int)evnt_button.Y;
+                point = new Point (evnt_button.X, evnt_button.Y);
             } else if (evnt is Gdk.EventCrossing && last_layout_child != null) {
                 last_layout_child.CursorLeaveEvent ();
                 last_layout_child = null;
                 return false;
             }
 
-            var child = GetLayoutChildAt (x, y);
+            var child = GetLayoutChildAt (point);
             if (child == null) {
                 return false;
             }
 
-            x -= child.VirtualAllocation.X;
-            y -= child.VirtualAllocation.Y;
+            point.Offset (-child.VirtualAllocation.X, -child.VirtualAllocation.Y);
 
             if (evnt_motion != null) {
                 if (last_layout_child != child) {
@@ -345,19 +342,18 @@ namespace Hyena.Data.Gui
                     last_layout_child = child;
                     child.CursorEnterEvent ();
                 }
-                handled = child.CursorMotionEvent (x, y);
+                handled = child.CursorMotionEvent (point);
             } else if (evnt_button != null) {
-                handled = child.ButtonEvent (x, y, press, evnt_button.Button);
+                handled = child.ButtonEvent (point, press, evnt_button.Button);
             }
 
             return handled;
         }
 
-        private DataViewChild GetLayoutChildAt (int x, int y)
+        private DataViewChild GetLayoutChildAt (Point point)
         {
-            x -= list_interaction_alloc.X;
-            y -= list_interaction_alloc.Y;
-            return ViewLayout.FindChildAtPoint (x, y);
+            point.Offset (-list_interaction_alloc.X, -list_interaction_alloc.Y);
+            return ViewLayout.FindChildAtPoint (point);
         }
 
 #endregion
@@ -861,7 +857,7 @@ namespace Hyena.Data.Gui
         protected int GetModelRowAt (int x, int y)
         {
             if (ViewLayout != null) {
-                var child = ViewLayout.FindChildAtPoint (x, y);
+                var child = ViewLayout.FindChildAtPoint (new Point (x, y));
                 return child == null ? -1 : child.ModelRowIndex;
             } else {
                 if (y < 0 || ChildSize.Height <= 0) {
@@ -882,7 +878,7 @@ namespace Hyena.Data.Gui
                 var child = ViewLayout.FindChildAtModelRowIndex (row);
                 return child == null
                     ? new Gdk.Point (0, 0)
-                    : new Gdk.Point (child.Allocation.X, child.Allocation.Y);
+                    : new Gdk.Point ((int)child.Allocation.X, (int)child.Allocation.Y);
             } else {
                 return new Gdk.Point (0, ChildSize.Height * row);
             }
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
index 1aa8b58..62c7382 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
+++ b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
@@ -76,6 +76,10 @@ namespace Hyena.Data.Gui
                 }
             }
 
+            if (ViewLayout != null) {
+                ViewLayout.Model = Model;
+            }
+
             RefreshViewForModel (vpos);
 
             var handler = ModelChanged;
@@ -91,7 +95,7 @@ namespace Hyena.Data.Gui
             }
 
             if (ViewLayout != null) {
-                ViewLayout.UpdateModelRowCount (Model.Count);
+                ViewLayout.ModelUpdated ();
             }
 
             UpdateAdjustments ();
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
index f872340..f15e8ee 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
+++ b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
@@ -34,6 +34,7 @@ using Gdk;
 
 using Hyena.Gui;
 using Hyena.Gui.Theming;
+using Hyena.Gui.Canvas;
 using GtkColorClass=Hyena.Gui.Theming.GtkColorClass;
 
 namespace Hyena.Data.Gui
@@ -129,7 +130,7 @@ namespace Hyena.Data.Gui
                 if (ViewLayout == null) {
                     PaintList (damage);
                 } else {
-                    PaintView (damage);
+                    PaintView ((Rect)damage);
                 }
             }
 
@@ -461,30 +462,27 @@ namespace Hyena.Data.Gui
 
 #region View Layout Rendering
 
-        private void PaintView (Rectangle clip)
+        private void PaintView (Rect clip)
         {
-            clip.Intersect (list_rendering_alloc);
-            cairo_context.Rectangle (clip.X, clip.Y, clip.Width, clip.Height);
+            clip.Intersect ((Rect)list_rendering_alloc);
+            cairo_context.Rectangle ((Cairo.Rectangle)clip);
             cairo_context.Clip ();
 
-            cell_context.Clip = clip;
+            cell_context.Clip = (Gdk.Rectangle)clip;
             cell_context.TextAsForeground = false;
 
             selected_rows.Clear ();
 
             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;
-                } else if (!layout_child.Allocation.IntersectsWith (clip)) {
+                if (!child_allocation.IntersectsWith (clip)) {
                     continue;
                 }
 
-                if (Selection != null && Selection.Contains (model_row_index)) {
-                    selected_rows.Add (model_row_index);
+                if (Selection != null && Selection.Contains (layout_child.ModelRowIndex)) {
+                    selected_rows.Add (layout_child.ModelRowIndex);
 
                     var selection_color = Theme.Colors.GetWidgetColor (GtkColorClass.Background, StateType.Selected);
                     if (!HasFocus || HeaderFocused) {
@@ -492,8 +490,8 @@ namespace Hyena.Data.Gui
                     }
 
                     Theme.DrawRowSelection (cairo_context,
-                        child_allocation.X, child_allocation.Y,
-                        child_allocation.Width, child_allocation.Height,
+                        (int)child_allocation.X, (int)child_allocation.Y,
+                        (int)child_allocation.Width, (int)child_allocation.Height,
                         true, true, selection_color, CairoCorners.All);
 
                     cell_context.State = StateType.Selected;
@@ -501,14 +499,8 @@ namespace Hyena.Data.Gui
                     cell_context.State = StateType.Normal;
                 }
 
-                cell_context.ModelRowIndex = model_row_index;
-                // cell_context.ViewRowIndex = view_row_index;
-                // cell_context.ViewColumnIndex = view_column_index;
-
-                layout_child.BindDataItem (model[model_row_index]);
-
                 cairo_context.Save ();
-                cairo_context.Translate (layout_child.Allocation.X, layout_child.Allocation.Y);
+                cairo_context.Translate (child_allocation.X, child_allocation.Y);
                 layout_child.Render (cell_context);
                 cairo_context.Restore ();
             }
@@ -553,8 +545,8 @@ namespace Hyena.Data.Gui
 // FIXME: Obsolete all this measure stuff on the view since it's in the layout
 #region Measuring
 
-        private Size child_size = Size.Empty;
-        public Size ChildSize {
+        private Gdk.Size child_size = Gdk.Size.Empty;
+        public Gdk.Size ChildSize {
             get { return child_size; }
         }
 
@@ -568,11 +560,11 @@ namespace Hyena.Data.Gui
             }
         }
 
-        protected virtual Size OnMeasureChild ()
+        protected virtual Gdk.Size OnMeasureChild ()
         {
             return ViewLayout != null
-                ? ViewLayout.ChildSize
-                : new Size (0, ColumnCellText.ComputeRowHeight (this));
+                ? new Gdk.Size ((int)ViewLayout.ChildSize.Width, (int)ViewLayout.ChildSize.Height)
+                : new Gdk.Size (0, ColumnCellText.ComputeRowHeight (this));
         }
 
         private void OnMeasure ()
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs
index 206cc4d..804cb3e 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs
+++ b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs
@@ -180,7 +180,7 @@ namespace Hyena.Data.Gui
             InvalidateList ();
 
             if (ViewLayout != null) {
-                ViewLayout.Allocate (list_rendering_alloc);
+                ViewLayout.Allocate ((Hyena.Gui.Canvas.Rect)list_rendering_alloc);
             }
         }
 
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/ListViewTestModule.cs b/src/Hyena.Gui/Hyena.Data.Gui/ListViewTestModule.cs
index 148fc13..e79a861 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/ListViewTestModule.cs
+++ b/src/Hyena.Gui/Hyena.Data.Gui/ListViewTestModule.cs
@@ -104,6 +104,11 @@ namespace Hyena.Data.Gui
             {
             }
 
+            public object GetItem (int index)
+            {
+                return this[index];
+            }
+
             public int Count {
                 get { return store.Count; }
             }
diff --git a/src/Hyena.Gui/Hyena.Gui.Canvas/Point.cs b/src/Hyena.Gui/Hyena.Gui.Canvas/Point.cs
new file mode 100644
index 0000000..35f3037
--- /dev/null
+++ b/src/Hyena.Gui/Hyena.Gui.Canvas/Point.cs
@@ -0,0 +1,86 @@
+//
+// Point.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright 2009-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;
+
+namespace Hyena.Gui.Canvas
+{
+    public struct Point
+    {
+        public double X { get; set; }
+        public double Y { get; set; }
+
+        public Point (double x, double y)
+        {
+            X = x;
+            Y = y;
+        }
+
+        public void Offset (double dx, double dy)
+        {
+            X += dx;
+            Y += dy;
+        }
+
+        public void Offset (Point delta)
+        {
+            X += delta.X;
+            Y += delta.Y;
+        }
+
+        public override bool Equals (object o)
+        {
+            return o is Point ? Equals ((Point)o) : false;
+        }
+
+        public bool Equals (Point value)
+        {
+            return value.X == X && value.Y == Y;
+        }
+
+        public static bool operator == (Point point1, Point point2)
+        {
+            return point1.X == point2.X && point1.Y == point2.Y;
+        }
+
+        public static bool operator != (Point point1, Point point2)
+        {
+            return !(point1 == point2);
+        }
+
+        public override int GetHashCode ()
+        {
+            return X.GetHashCode () ^ Y.GetHashCode ();
+        }
+
+        public override string ToString ()
+        {
+            return String.Format ("{0},{1}", X, Y);
+        }
+    }
+}
diff --git a/src/Hyena.Gui/Hyena.Gui.Canvas/Rect.cs b/src/Hyena.Gui/Hyena.Gui.Canvas/Rect.cs
new file mode 100644
index 0000000..44e8616
--- /dev/null
+++ b/src/Hyena.Gui/Hyena.Gui.Canvas/Rect.cs
@@ -0,0 +1,276 @@
+//
+// Rect.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright 2009-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;
+
+namespace Hyena.Gui.Canvas
+{
+    public struct Rect 
+    {
+        private double x, y, w, h;
+
+        public Rect (double x, double y, double width, double height)
+        {
+            X = x;
+            Y = y;
+            Width = width;
+            Height = height;
+        }
+
+        public Rect (Point point1, Point point2)
+        {
+            X = Math.Min (point1.X, point2.X);
+            Y = Math.Min (point1.Y, point2.Y);
+            Width = Math.Abs (point2.X - point1.X);
+            Height = Math.Abs (point2.Y - point1.Y);
+        }
+
+        public Rect (Point location, Size size)
+        {
+            X = location.X;
+            Y = location.Y;
+            Width = size.Width;
+            Height = size.Height;
+        }
+
+        public override string ToString ()
+        {
+            if (IsEmpty) {
+                return "Empty";
+            }
+            
+            return String.Format ("{0}+{1},{2}x{3}", x, y, w, h);
+        }
+        
+        public double X {
+            get { return x; }
+            set { x = value; }
+        }
+
+        public double Y {
+            get { return y; }
+            set { y = value; } 
+        }
+
+        public double Width {
+            get { return w; }
+            set { 
+                if (value < 0) {
+                    throw new ArgumentException ();
+                }
+                
+                w = value; 
+            } 
+        }
+
+        public double Height {
+            get { return h; }
+            set { 
+                if (value < 0) {
+                    throw new ArgumentException ();
+                }
+                
+                h = value; 
+            }
+        }
+
+        public bool Contains (double px, double py)
+        {
+            return !(px < x || px > x + w || py < y || py > y + h);
+        }
+
+        public bool Contains (Point point)
+        {
+            return Contains (point.X, point.Y);
+        }
+
+        public bool IntersectsWith (Rect rect)
+        {
+            return !(Left > rect.Right ||
+                Right < rect.Left ||
+                Top > rect.Bottom ||
+                Bottom < rect.Top);
+        }
+        
+        public static Rect Empty { 
+            get { 
+                var empty = new Rect (Double.PositiveInfinity, Double.PositiveInfinity, 0, 0);
+                empty.w = empty.h = Double.NegativeInfinity;
+                return empty;
+            } 
+        }
+        
+        public bool IsEmpty { 
+            get { return w < 0 && h < 0; }
+        }
+        
+        public double Left { 
+            get { return x; }
+        }
+        
+        public double Top { 
+            get { return y; }
+        }
+
+        public double Right {
+            get { return IsEmpty ? Double.NegativeInfinity : x + w; }
+        }
+        
+        public double Bottom { 
+            get { return IsEmpty ? Double.NegativeInfinity : y + h; }
+        }
+        
+        public void Intersect (Rect rect)
+        {
+            if (IsEmpty || rect.IsEmpty) {
+                this = Rect.Empty;
+                return;
+            }
+
+            double new_x = Math.Max (x, rect.x);
+            double new_y = Math.Max (y, rect.y);
+            double new_w = Math.Min (Right, rect.Right) - new_x;
+            double new_h = Math.Min (Bottom, rect.Bottom) - new_y; 
+
+            x = new_x;
+            y = new_y;
+            w = new_w;
+            h = new_h;
+
+            if (w < 0 || h < 0) {
+                this = Rect.Empty;
+            }
+        }
+
+        public void Union (Rect rect)
+        {
+            if (IsEmpty) {
+                x = rect.x;
+                y = rect.y;
+                h = rect.h;
+                w = rect.w;
+            } else if (!rect.IsEmpty) {
+                double new_x = Math.Min (Left, rect.Left);
+                double new_y = Math.Min (Top, rect.Top);
+                double new_w = Math.Max (Right, rect.Right) - new_x;
+                double new_h = Math.Max (Bottom, rect.Bottom) - new_y;
+    
+                x = new_x;
+                y = new_y;
+                w = new_w;
+                h = new_h;
+            }
+        }
+        
+        public void Union (Point point)
+        {
+            Union (new Rect (point, point));
+        }
+
+        public void Offset (Rect rect)
+        {
+            x += rect.X;
+            y += rect.Y;
+        }
+
+        public void Offset (Point point)
+        {
+            x += point.X;
+            y += point.Y;
+        }
+
+        public void Offset (double dx, double dy)
+        {
+            x += dx;
+            y += dy;
+        }
+        
+        public static bool operator == (Rect rect1, Rect rect2)
+        {
+            return rect1.x == rect2.x && rect1.y == rect2.y && rect1.w == rect2.w && rect1.h == rect2.h;
+        }
+        
+        public static bool operator != (Rect rect1, Rect rect2)
+        {
+            return !(rect1 == rect2);
+        }
+        
+        public override bool Equals (object o)
+        {
+            if (o is Rect) {
+                return this == (Rect)o;
+            }
+            return false;
+        }
+        
+        public bool Equals (Rect value)
+        {
+            return this == value;
+        }
+        
+        public override int GetHashCode ()
+        {
+            return x.GetHashCode () ^ y.GetHashCode () ^ w.GetHashCode () ^ h.GetHashCode ();
+        }
+
+#region GDK/Cairo extensions
+
+        public static explicit operator Rect (Gdk.Rectangle rect)
+        {
+            return new Rect () {
+                X = rect.X,
+                Y = rect.Y,
+                Width = rect.Width,
+                Height = rect.Height
+            };
+        }
+
+        public static explicit operator Gdk.Rectangle (Rect rect)
+        {
+            return new Gdk.Rectangle () {
+                X = (int)Math.Floor (rect.X),
+                Y = (int)Math.Floor (rect.Y),
+                Width = (int)Math.Ceiling (rect.Width),
+                Height = (int)Math.Ceiling (rect.Height)
+            };
+        }
+
+        public static explicit operator Rect (Cairo.Rectangle rect)
+        {
+            return new Rect (rect.X, rect.Y, rect.Width, rect.Height);
+        }
+
+        public static explicit operator Cairo.Rectangle (Rect rect)
+        {
+            return new Cairo.Rectangle (rect.X, rect.Y, rect.Width, rect.Height);
+        }
+
+#endregion
+
+    }
+}
diff --git a/src/Hyena.Gui/Hyena.Gui.Canvas/Size.cs b/src/Hyena.Gui/Hyena.Gui.Canvas/Size.cs
new file mode 100644
index 0000000..6e3a759
--- /dev/null
+++ b/src/Hyena.Gui/Hyena.Gui.Canvas/Size.cs
@@ -0,0 +1,117 @@
+//
+// Size.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright 2009-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;
+
+namespace Hyena.Gui.Canvas
+{
+    public struct Size 
+    {
+        private double width;
+        private double height;
+
+        public Size (double width, double height)
+        {
+            Width = width;
+            Height = height;
+        }
+        
+        public override bool Equals (object o)
+        {
+            if (!(o is Size)) {
+                return false;
+            }
+            
+            return Equals ((Size)o);
+        }
+        
+        public bool Equals (Size value)
+        {
+            return value.width == width && value.height == height;
+        }
+        
+        public override int GetHashCode ()
+        {
+            return ((int)width) ^ ((int)height);
+        }
+        
+        public static bool operator == (Size size1, Size size2)
+        {
+            return size1.width == size2.width && size1.height == size2.height;
+        }
+            
+        public static bool operator != (Size size1, Size size2)
+        {
+            return size1.width != size2.width || size1.height != size2.height;
+        }
+        
+        public double Height {
+            get { return height; } 
+            set {
+                if (value < 0) {
+                    throw new ArgumentException ();
+                }
+                
+                height = value; 
+            }
+        }
+
+        public double Width {
+            get { return width; }
+            set {
+                if (value < 0) {
+                    throw new ArgumentException ();
+                }
+                
+                width = value;
+            }
+        }
+
+        public bool IsEmpty {
+            get { return width == Double.NegativeInfinity && height == Double.NegativeInfinity; }
+        }
+        
+        public static Size Empty {
+            get {
+                Size size = new Size ();
+                size.width = Double.NegativeInfinity;
+                size.height = Double.NegativeInfinity;
+                return size;
+            }
+        }
+
+        public override string ToString ()
+        {
+            if (IsEmpty) {
+                return "Empty";
+            }
+            
+            return String.Format ("{0}x{1}", width, height);
+        }
+    }
+}
diff --git a/src/Hyena.Gui/Hyena.Gui.Canvas/Thickness.cs b/src/Hyena.Gui/Hyena.Gui.Canvas/Thickness.cs
new file mode 100644
index 0000000..9d25150
--- /dev/null
+++ b/src/Hyena.Gui/Hyena.Gui.Canvas/Thickness.cs
@@ -0,0 +1,126 @@
+//
+// Thickness.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright 2009-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;
+
+namespace Hyena.Gui.Canvas
+{
+    public struct Thickness
+    {
+        private double left;
+        private double top;
+        private double right;
+        private double bottom;
+        
+        public Thickness (double thickness) 
+            : this (thickness, thickness, thickness, thickness)
+        {
+        }
+        
+        public Thickness (double xthickness, double ythickness) 
+            : this (xthickness, ythickness, xthickness, ythickness)
+        {
+        }
+        
+        public Thickness (double left, double top, double right, double bottom)
+        {
+            this.left = left;
+            this.top = top;
+            this.right = right;
+            this.bottom = bottom;
+        }
+        
+        public override string ToString ()
+        {
+            return string.Format ("{0},{1},{2},{3}", Double.IsNaN (left) ? "Auto" : left.ToString (),
+                Double.IsNaN (top) ? "Auto" : top.ToString (), 
+                Double.IsNaN (right) ? "Auto" : right.ToString (), 
+                Double.IsNaN (bottom) ? "Auto" : bottom.ToString ()); 
+        }
+
+        public override bool Equals (object o)
+        {
+            if (!(o is Thickness)) {
+                return false;
+            }
+            
+            return this == (Thickness)o;
+        }
+        
+        public bool Equals (Thickness thickness)
+        {
+            return this == thickness;
+        }
+        
+        public override int GetHashCode ()
+        {
+            return left.GetHashCode () ^ top.GetHashCode () ^ right.GetHashCode () ^ bottom.GetHashCode ();
+        }
+        
+        public static bool operator == (Thickness t1, Thickness t2)
+        {
+            return t1.left == t2.left &&
+                t1.right == t2.right &&
+                t1.top == t2.top &&
+                t1.bottom == t2.bottom;
+        }
+        
+        public static bool operator != (Thickness t1, Thickness t2)
+        {
+            return !(t1 == t2);
+        }
+        
+        public double Left {
+            get { return left; }
+            set { left = value; }
+        }
+        
+        public double Top {
+            get { return top; }
+            set { top = value; }    
+        }
+        
+        public double Right { 
+            get { return right; }
+            set { right = value; }
+        }
+        
+        public double Bottom { 
+            get { return bottom; }
+            set { bottom = value; }
+        }
+
+        public double X {
+            get { return Left + Right; }
+        }
+
+        public double Y {
+            get { return Top + Bottom; }
+        }
+    }
+}
diff --git a/src/Hyena.Gui/Hyena.Gui.csproj b/src/Hyena.Gui/Hyena.Gui.csproj
index 6ada0e0..381312f 100644
--- a/src/Hyena.Gui/Hyena.Gui.csproj
+++ b/src/Hyena.Gui/Hyena.Gui.csproj
@@ -159,6 +159,10 @@
     <Compile Include="Hyena.Data.Gui\DataViewLayoutGrid.cs" />
     <Compile Include="Hyena.Gui\CairoDamageDebugger.cs" />
     <Compile Include="Hyena.Data.Gui\DataViewChild.cs" />
+    <Compile Include="Hyena.Gui.Canvas\Rect.cs" />
+    <Compile Include="Hyena.Gui.Canvas\Size.cs" />
+    <Compile Include="Hyena.Gui.Canvas\Thickness.cs" />
+    <Compile Include="Hyena.Gui.Canvas\Point.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ProjectExtensions>
@@ -177,4 +181,8 @@
       </Properties>
     </MonoDevelop>
   </ProjectExtensions>
+   <ItemGroup>
+     <Folder Include="Hyena.Data.Gui\Accessibility\" />
+     <Folder Include="Hyena.Gui.Canvas\" />
+   </ItemGroup>
 </Project>
diff --git a/src/Hyena.Gui/Makefile.am b/src/Hyena.Gui/Makefile.am
index 7ad3751..613a1d5 100644
--- a/src/Hyena.Gui/Makefile.am
+++ b/src/Hyena.Gui/Makefile.am
@@ -42,6 +42,10 @@ SOURCES =  \
 	Hyena.Data.Gui/ObjectListView.cs \
 	Hyena.Data.Gui/RowActivatedHandler.cs \
 	Hyena.Data.Gui/SortableColumn.cs \
+	Hyena.Gui.Canvas/Point.cs \
+	Hyena.Gui.Canvas/Rect.cs \
+	Hyena.Gui.Canvas/Size.cs \
+	Hyena.Gui.Canvas/Thickness.cs \
 	Hyena.Gui.Dialogs/ExceptionDialog.cs \
 	Hyena.Gui.Dialogs/VersionInformationDialog.cs \
 	Hyena.Gui.Theatrics/Actor.cs \



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