Miguel de Icaza 7 years ago
parent
commit
0241b4175d
8 changed files with 1140 additions and 33 deletions
  1. 111 22
      Application.cs
  2. 28 0
      Event.cs
  3. 3 0
      Terminal.csproj
  4. 227 0
      Types/Point.cs
  5. 457 0
      Types/Rect.cs
  6. 225 0
      Types/Size.cs
  7. 9 0
      demo.cs
  8. 80 11
      driver.cs

+ 111 - 22
Application.cs

@@ -1,20 +1,15 @@
+//
+// 
+// Pending:
+//   - Check for NeedDisplay on the hierarchy and repaint
+//   - Layout support
+//
+// Optimziations
+//   - Add rendering limitation to the exposed area
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 
 
 namespace Terminal {
 namespace Terminal {
-    public struct Rect {
-        public int X, Y, Width, Height;
-
-        public Rect (int x, int y, int width, int height)
-        {
-            X = x;
-            Y = y;
-            Width = width;
-            Height = height;
-        }
-
-        public override string ToString() => $"[{X},{Y}:{Width},{Height}]";
-    }
 
 
     public class Responder {
     public class Responder {
         public virtual Responder Next { get; set; }
         public virtual Responder Next { get; set; }
@@ -32,16 +27,36 @@ namespace Terminal {
     }
     }
 
 
     public class View : Responder {
     public class View : Responder {
+        View container = null;
         public static ConsoleDriver Driver = Application.Driver;
         public static ConsoleDriver Driver = Application.Driver;
         public static IList<View> empty = new List<View>(0).AsReadOnly ();
         public static IList<View> empty = new List<View>(0).AsReadOnly ();
         List<View> subviews;
         List<View> subviews;
         public IList<View> Subviews  => subviews == null ? empty : subviews.AsReadOnly ();
         public IList<View> Subviews  => subviews == null ? empty : subviews.AsReadOnly ();
+        internal bool NeedDisplay { get; private set; } = true;
 
 
+        // The frame for the object
         Rect frame;
         Rect frame;
 
 
+        // The offset of the first child view inside the view
+        Point offset;
+
+        // The frame for this view
+        public Rect Frame { 
+            get => frame;
+            set {
+                frame = value;
+                SetNeedsDisplay ();
+            }
+        }
+
         public View (Rect frame)
         public View (Rect frame)
         {
         {
-            this.frame = frame;
+            this.Frame = frame;
+        }
+
+        public void SetNeedsDisplay ()
+        {
+            NeedDisplay = true;
         }
         }
 
 
         public void AddSubview (View view)
         public void AddSubview (View view)
@@ -53,7 +68,53 @@ namespace Terminal {
             subviews.Add (view);
             subviews.Add (view);
         }
         }
 
 
-        
+        public void GetRealRowCol (int col, int row, out int rcol, out int rrow)
+        {
+            // Computes the real row, col relative to the screen.
+            rrow = row;
+            rcol = col;
+            var ccontainer = container;
+            while (ccontainer != null){
+                rrow += container.frame.Y;
+                rcol += container.frame.X;
+                ccontainer = ccontainer.container;
+            }
+
+            // The following ensures that the cursor is always in the screen boundaries.
+            rrow = Math.Max (0, Math.Min (rrow, Driver.Rows-1));
+            rcol = Math.Max (0, Math.Min (rcol, Driver.Cols-1));
+        }
+
+        public void Move (int col, int row)
+        {
+            GetRealRowCol (col, row, out var rcol, out var rrow);
+            Driver.Move (rcol, rrow);
+        }
+
+        public void AddCh (int col, int row, int ch)
+        {
+            if (row < 0 || col < 0)
+                return;
+            if (row > frame.Height-1 || col > frame.Width-1)
+                return;
+            Move (col, row);
+            Driver.AddCh (ch);
+        }
+
+        public virtual void Redraw ()
+        {
+            var clipRect = new Rect (offset, frame.Size);
+
+            foreach (var view in subviews){
+                if (view.NeedDisplay){
+                    if (view.Frame.IntersectsWith (clipRect)){
+                        view.Redraw ();
+                    }
+                    view.NeedDisplay = false;
+                }
+            }
+            NeedDisplay = false;
+        }
     }
     }
 
 
     public class ViewController : Responder {
     public class ViewController : Responder {
@@ -66,10 +127,12 @@ namespace Terminal {
         }
         }
     }
     }
 
 
-    public class Window : View {
+    public class Toplevel : View {
         public ViewController RootViewController;
         public ViewController RootViewController;
+        public bool ShowFrame;
+        
 
 
-        public Window (Rect frame) : base (frame)
+        public Toplevel (Rect frame) : base (frame)
         {
         {
         }
         }
 
 
@@ -78,15 +141,41 @@ namespace Terminal {
             Application.MakeFirstResponder (this);
             Application.MakeFirstResponder (this);
         }
         }
 
 
-        public static Window Toplevel () 
+        public static Toplevel Create () 
         {
         {
             return new Window (new Rect (0, 0, Driver.Cols, Driver.Rows));
             return new Window (new Rect (0, 0, Driver.Cols, Driver.Rows));
         }
         }
     }
     }
 
 
+    public class Window : Toplevel {
+        View contentView;
+        string title;
+
+        public string Title {
+            get => title;
+            set {
+                title = value;
+                SetNeedsDisplay ();
+            }
+        }
+
+        public Window (Rect frame, string title = null) : base (frame)
+        {
+            frame.Inflate (-1, -1);
+            contentView = new View (frame);
+            AddSubview (contentView);
+        }
+
+        public override void Redraw ()
+        {
+
+            base.Redraw ();
+        }
+    }
+
     public class Application {
     public class Application {
         public static ConsoleDriver Driver = new CursesDriver ();
         public static ConsoleDriver Driver = new CursesDriver ();
-        public Window MainWindow { get; private set; }
+        public Toplevel Top { get; private set; }
         public Mono.Terminal.MainLoop MainLoop { get; private set; }
         public Mono.Terminal.MainLoop MainLoop { get; private set; }
 
 
         static Stack<Responder> responders = new Stack<Responder> ();
         static Stack<Responder> responders = new Stack<Responder> ();
@@ -103,12 +192,12 @@ namespace Terminal {
 
 
         public void Init ()
         public void Init ()
         {
         {
-            if (MainWindow != null)
+            if (Top != null)
                 return;
                 return;
 
 
             MainLoop = new Mono.Terminal.MainLoop ();
             MainLoop = new Mono.Terminal.MainLoop ();
-            MainWindow = Window.Toplevel ();  
-            responder = MainWindow;
+            Top = Toplevel.Create ();  
+            responder = Top;
 
 
             MainLoop.AddWatch (0, Mono.Terminal.MainLoop.Condition.PollIn, x => {
             MainLoop.AddWatch (0, Mono.Terminal.MainLoop.Condition.PollIn, x => {
                 //ProcessChar ();
                 //ProcessChar ();

+ 28 - 0
Event.cs

@@ -0,0 +1,28 @@
+namespace Terminal {
+
+    public class Event {
+        public class Key : Event {
+            public int Code { get; private set; }
+
+            public Key (int code)
+            {
+                Code = code;
+            }
+        }
+
+        public class Mouse : Event {
+        }
+
+        public static Event CreateMouseEvent ()
+        {
+            return new Mouse ();
+        }
+
+        public static Event CreateKeyEvent (int code)
+        {
+            return new Key (code);
+        }
+
+    }
+
+}

+ 3 - 0
Terminal.csproj

@@ -35,6 +35,9 @@
     <Compile Include="Application.cs" />
     <Compile Include="Application.cs" />
     <Compile Include="driver.cs" />
     <Compile Include="driver.cs" />
     <Compile Include="Event.cs" />
     <Compile Include="Event.cs" />
+    <Compile Include="Types\Point.cs"/>
+    <Compile Include="Types\Rect.cs"/>
+    <Compile Include="Types\Size.cs"/>
     <Compile Include="demo.cs" />
     <Compile Include="demo.cs" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>

+ 227 - 0
Types/Point.cs

@@ -0,0 +1,227 @@
+//
+// System.Drawing.Point.cs
+//
+// Author:
+//   Mike Kestner ([email protected])
+//
+// Copyright (C) 2001 Mike Kestner
+// Copyright (C) 2004 Novell, Inc.  http://www.novell.com 
+//
+
+using System;
+using System.Globalization;
+
+namespace Terminal
+{
+	public struct Point
+	{
+		// Private x and y coordinate fields.
+		public int X, Y;
+
+		// -----------------------
+		// Public Shared Members
+		// -----------------------
+
+		/// <summary>
+		///	Empty Shared Field
+		/// </summary>
+		///
+		/// <remarks>
+		///	An uninitialized Point Structure.
+		/// </remarks>
+		
+		public static readonly Point Empty;
+
+		/// <summary>
+		///	Addition Operator
+		/// </summary>
+		///
+		/// <remarks>
+		///	Translates a Point using the Width and Height
+		///	properties of the given <typeref>Size</typeref>.
+		/// </remarks>
+
+		public static Point operator + (Point pt, Size sz)
+		{
+			return new Point (pt.X + sz.Width, pt.Y + sz.Height);
+		}
+		
+		/// <summary>
+		///	Equality Operator
+		/// </summary>
+		///
+		/// <remarks>
+		///	Compares two Point objects. The return value is
+		///	based on the equivalence of the X and Y properties 
+		///	of the two points.
+		/// </remarks>
+
+		public static bool operator == (Point left, Point right)
+		{
+			return ((left.X == right.X) && (left.Y == right.Y));
+		}
+		
+		/// <summary>
+		///	Inequality Operator
+		/// </summary>
+		///
+		/// <remarks>
+		///	Compares two Point objects. The return value is
+		///	based on the equivalence of the X and Y properties 
+		///	of the two points.
+		/// </remarks>
+
+		public static bool operator != (Point left, Point right)
+		{
+			return ((left.X != right.X) || (left.Y != right.Y));
+		}
+		
+		/// <summary>
+		///	Subtraction Operator
+		/// </summary>
+		///
+		/// <remarks>
+		///	Translates a Point using the negation of the Width 
+		///	and Height properties of the given Size.
+		/// </remarks>
+
+		public static Point operator - (Point pt, Size sz)
+		{
+			return new Point (pt.X - sz.Width, pt.Y - sz.Height);
+		}
+		
+		/// <summary>
+		///	Point to Size Conversion
+		/// </summary>
+		///
+		/// <remarks>
+		///	Returns a Size based on the Coordinates of a given 
+		///	Point. Requires explicit cast.
+		/// </remarks>
+
+		public static explicit operator Size (Point p)
+		{
+			return new Size (p.X, p.Y);
+		}
+
+		// -----------------------
+		// Public Constructors
+		// -----------------------
+		/// <summary>
+		///	Point Constructor
+		/// </summary>
+		///
+		/// <remarks>
+		///	Creates a Point from a Size value.
+		/// </remarks>
+		
+		public Point (Size sz)
+		{
+			X = sz.Width;
+			Y = sz.Height;
+		}
+
+		/// <summary>
+		///	Point Constructor
+		/// </summary>
+		///
+		/// <remarks>
+		///	Creates a Point from a specified x,y coordinate pair.
+		/// </remarks>
+		
+		public Point (int x, int y)
+		{
+			this.X = x;
+			this.Y = y;
+		}
+
+		// -----------------------
+		// Public Instance Members
+		// -----------------------
+
+		/// <summary>
+		///	IsEmpty Property
+		/// </summary>
+		///
+		/// <remarks>
+		///	Indicates if both X and Y are zero.
+		/// </remarks>		
+		public bool IsEmpty {
+			get {
+				return ((X == 0) && (Y == 0));
+			}
+		}
+
+		/// <summary>
+		///	Equals Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Checks equivalence of this Point and another object.
+		/// </remarks>
+		
+		public override bool Equals (object obj)
+		{
+			if (!(obj is Point))
+				return false;
+
+			return (this == (Point) obj);
+		}
+
+		/// <summary>
+		///	GetHashCode Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Calculates a hashing value.
+		/// </remarks>
+		
+		public override int GetHashCode ()
+		{
+			return X^Y;
+		}
+
+		/// <summary>
+		///	Offset Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Moves the Point a specified distance.
+		/// </remarks>
+
+		public void Offset (int dx, int dy)
+		{
+			X += dx;
+			Y += dy;
+		}
+		
+		/// <summary>
+		///	ToString Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Formats the Point as a string in coordinate notation.
+		/// </remarks>
+		
+		public override string ToString ()
+		{
+			return string.Format ("{{X={0},Y={1}}}", X.ToString (CultureInfo.InvariantCulture), 
+				Y.ToString (CultureInfo.InvariantCulture));
+		}
+		public static Point Add (Point pt, Size sz)
+		{
+			return new Point (pt.X + sz.Width, pt.Y + sz.Height);
+		}
+
+		public void Offset (Point p)
+		{
+			Offset (p.X, p.Y);
+		}
+
+		public static Point Subtract (Point pt, Size sz)
+		{
+			return new Point (pt.X - sz.Width, pt.Y - sz.Height);
+		}
+
+	}
+}

+ 457 - 0
Types/Rect.cs

@@ -0,0 +1,457 @@
+//
+// System.Drawing.Rectangle.cs
+//
+// Author:
+//   Mike Kestner ([email protected])
+//
+// Copyright (C) 2001 Mike Kestner
+// Copyright (C) 2004 Novell, Inc.  http://www.novell.com 
+//
+
+using System;
+
+namespace Terminal
+{
+	public struct Rect
+	{
+		public int X, Y, Width, Height;
+
+		/// <summary>
+		///	Empty Shared Field
+		/// </summary>
+		///
+		/// <remarks>
+		///	An uninitialized Rectangle Structure.
+		/// </remarks>
+		
+		public static readonly Rect Empty;
+
+		/// <summary>
+		///	FromLTRB Shared Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Produces a Rectangle structure from left, top, right
+		///	and bottom coordinates.
+		/// </remarks>
+		
+		public static Rect FromLTRB (int left, int top,
+						  int right, int bottom)
+		{
+			return new Rect (left, top, right - left,
+					      bottom - top);
+		}
+
+		/// <summary>
+		///	Inflate Shared Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Produces a new Rectangle by inflating an existing 
+		///	Rectangle by the specified coordinate values.
+		/// </remarks>
+		
+		public static Rect Inflate (Rect rect, int x, int y)
+		{
+			Rect r = new Rect (rect.Location, rect.Size);
+			r.Inflate (x, y);
+			return r;
+		}
+
+		/// <summary>
+		///	Inflate Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Inflates the Rectangle by a specified width and height.
+		/// </remarks>
+		
+		public void Inflate (int width, int height)
+		{
+			Inflate (new Size (width, height));
+		}
+
+		/// <summary>
+		///	Inflate Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Inflates the Rectangle by a specified Size.
+		/// </remarks>
+		
+		public void Inflate (Size size)
+		{
+			X -= size.Width;
+			Y -= size.Height;
+			Width += size.Width * 2;
+			Height += size.Height * 2;
+		}
+
+		/// <summary>
+		///	Intersect Shared Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Produces a new Rectangle by intersecting 2 existing 
+		///	Rectangles. Returns null if there is no	intersection.
+		/// </remarks>
+		
+		public static Rect Intersect (Rect a, Rect b)
+		{
+			// MS.NET returns a non-empty rectangle if the two rectangles
+			// touch each other
+			if (!a.IntersectsWithInclusive (b))
+				return Empty;
+
+			return Rect.FromLTRB (
+				Math.Max (a.Left, b.Left),
+				Math.Max (a.Top, b.Top),
+				Math.Min (a.Right, b.Right),
+				Math.Min (a.Bottom, b.Bottom));
+		}
+
+		/// <summary>
+		///	Intersect Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Replaces the Rectangle with the intersection of itself
+		///	and another Rectangle.
+		/// </remarks>
+		
+		public void Intersect (Rect rect)
+		{
+			this = Rect.Intersect (this, rect);
+		}
+
+		/// <summary>
+		///	Union Shared Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Produces a new Rectangle from the union of 2 existing 
+		///	Rectangles.
+		/// </remarks>
+		
+		public static Rect Union (Rect a, Rect b)
+		{
+			return FromLTRB (Math.Min (a.Left, b.Left),
+					 Math.Min (a.Top, b.Top),
+					 Math.Max (a.Right, b.Right),
+					 Math.Max (a.Bottom, b.Bottom));
+		}
+
+		/// <summary>
+		///	Equality Operator
+		/// </summary>
+		///
+		/// <remarks>
+		///	Compares two Rectangle objects. The return value is
+		///	based on the equivalence of the Location and Size 
+		///	properties of the two Rectangles.
+		/// </remarks>
+
+		public static bool operator == (Rect left, Rect right)
+		{
+			return ((left.Location == right.Location) && 
+				(left.Size == right.Size));
+		}
+		
+		/// <summary>
+		///	Inequality Operator
+		/// </summary>
+		///
+		/// <remarks>
+		///	Compares two Rectangle objects. The return value is
+		///	based on the equivalence of the Location and Size 
+		///	properties of the two Rectangles.
+		/// </remarks>
+
+		public static bool operator != (Rect left, Rect right)
+		{
+			return ((left.Location != right.Location) || 
+				(left.Size != right.Size));
+		}
+		
+
+		// -----------------------
+		// Public Constructors
+		// -----------------------
+
+		/// <summary>
+		///	Rectangle Constructor
+		/// </summary>
+		///
+		/// <remarks>
+		///	Creates a Rectangle from Point and Size values.
+		/// </remarks>
+		
+		public Rect (Point location, Size size)
+		{
+			X = location.X;
+			Y = location.Y;
+			Width = size.Width;
+			Height = size.Height;
+		}
+
+		/// <summary>
+		///	Rectangle Constructor
+		/// </summary>
+		///
+		/// <remarks>
+		///	Creates a Rectangle from a specified x,y location and
+		///	width and height values.
+		/// </remarks>
+		
+		public Rect (int x, int y, int width, int height)
+		{
+			this.X = x;
+			this.Y = y;
+			this.Width = width;
+			this.Height = height;
+		}
+
+
+
+		/// <summary>
+		///	Bottom Property
+		/// </summary>
+		///
+		/// <remarks>
+		///	The Y coordinate of the bottom edge of the Rectangle.
+		///	Read only.
+		/// </remarks>		
+		public int Bottom {
+			get {
+				return Y + Height;
+			}
+		}
+
+		/// <summary>
+		///	IsEmpty Property
+		/// </summary>
+		///
+		/// <remarks>
+		///	Indicates if the width or height are zero. Read only.
+		/// </remarks>		
+		public bool IsEmpty {
+			get {
+				return ((X == 0) && (Y == 0) && (Width == 0) && (Height == 0));
+			}
+		}
+
+		/// <summary>
+		///	Left Property
+		/// </summary>
+		///
+		/// <remarks>
+		///	The X coordinate of the left edge of the Rectangle.
+		///	Read only.
+		/// </remarks>
+		
+		public int Left {
+			get {
+				return X;
+			}
+		}
+
+		/// <summary>
+		///	Location Property
+		/// </summary>
+		///
+		/// <remarks>
+		///	The Location of the top-left corner of the Rectangle.
+		/// </remarks>
+		
+		public Point Location {
+			get {
+				return new Point (X, Y);
+			}
+			set {
+				X = value.X;
+				Y = value.Y;
+			}
+		}
+
+		/// <summary>
+		///	Right Property
+		/// </summary>
+		///
+		/// <remarks>
+		///	The X coordinate of the right edge of the Rectangle.
+		///	Read only.
+		/// </remarks>
+		
+		public int Right {
+			get {
+				return X + Width;
+			}
+		}
+
+		/// <summary>
+		///	Size Property
+		/// </summary>
+		///
+		/// <remarks>
+		///	The Size of the Rectangle.
+		/// </remarks>
+		
+		public Size Size {
+			get {
+				return new Size (Width, Height);
+			}
+			set {
+				Width = value.Width;
+				Height = value.Height;
+			}
+		}
+
+		/// <summary>
+		///	Top Property
+		/// </summary>
+		///
+		/// <remarks>
+		///	The Y coordinate of the top edge of the Rectangle.
+		///	Read only.
+		/// </remarks>
+		
+		public int Top {
+			get {
+				return Y;
+			}
+		}
+
+		/// <summary>
+		///	Contains Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Checks if an x,y coordinate lies within this Rectangle.
+		/// </remarks>
+		
+		public bool Contains (int x, int y)
+		{
+			return ((x >= Left) && (x < Right) && 
+				(y >= Top) && (y < Bottom));
+		}
+
+		/// <summary>
+		///	Contains Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Checks if a Point lies within this Rectangle.
+		/// </remarks>
+		
+		public bool Contains (Point pt)
+		{
+			return Contains (pt.X, pt.Y);
+		}
+
+		/// <summary>
+		///	Contains Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Checks if a Rectangle lies entirely within this 
+		///	Rectangle.
+		/// </remarks>
+		
+		public bool Contains (Rect rect)
+		{
+			return (rect == Intersect (this, rect));
+		}
+
+		/// <summary>
+		///	Equals Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Checks equivalence of this Rectangle and another object.
+		/// </remarks>
+		
+		public override bool Equals (object obj)
+		{
+			if (!(obj is Rect))
+				return false;
+
+			return (this == (Rect) obj);
+		}
+
+		/// <summary>
+		///	GetHashCode Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Calculates a hashing value.
+		/// </remarks>
+		
+		public override int GetHashCode ()
+		{
+			return (Height + Width) ^ X + Y;
+		}
+
+		/// <summary>
+		///	IntersectsWith Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Checks if a Rectangle intersects with this one.
+		/// </remarks>
+		
+		public bool IntersectsWith (Rect rect)
+		{
+			return !((Left >= rect.Right) || (Right <= rect.Left) ||
+			    (Top >= rect.Bottom) || (Bottom <= rect.Top));
+		}
+
+		private bool IntersectsWithInclusive (Rect r)
+		{
+			return !((Left > r.Right) || (Right < r.Left) ||
+			    (Top > r.Bottom) || (Bottom < r.Top));
+		}
+
+		/// <summary>
+		///	Offset Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Moves the Rectangle a specified distance.
+		/// </remarks>
+
+		public void Offset (int x, int y)
+		{
+			this.X += x;
+			this.Y += y;
+		}
+		
+		/// <summary>
+		///	Offset Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Moves the Rectangle a specified distance.
+		/// </remarks>
+
+		public void Offset (Point pos)
+		{
+			X += pos.X;
+			Y += pos.Y;
+		}
+		
+		/// <summary>
+		///	ToString Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Formats the Rectangle as a string in (x,y,w,h) notation.
+		/// </remarks>
+		
+		public override string ToString ()
+		{
+			return String.Format ("{{X={0},Y={1},Width={2},Height={3}}}",
+						 X, Y, Width, Height);
+		}
+
+	}
+}

+ 225 - 0
Types/Size.cs

@@ -0,0 +1,225 @@
+//
+// System.Drawing.Size.cs
+//
+// Author:
+//   Mike Kestner ([email protected])
+//
+// Copyright (C) 2001 Mike Kestner
+// Copyright (C) 2004 Novell, Inc. http://www.novell.com
+//
+
+using System;
+
+namespace Terminal {
+	public struct Size
+	{ 
+		private int width, height;
+		public static readonly Size Empty;
+
+		/// <summary>
+		///	Addition Operator
+		/// </summary>
+		///
+		/// <remarks>
+		///	Addition of two Size structures.
+		/// </remarks>
+
+		public static Size operator + (Size sz1, Size sz2)
+		{
+			return new Size (sz1.Width + sz2.Width, 
+					 sz1.Height + sz2.Height);
+		}
+		
+		/// <summary>
+		///	Equality Operator
+		/// </summary>
+		///
+		/// <remarks>
+		///	Compares two Size objects. The return value is
+		///	based on the equivalence of the Width and Height 
+		///	properties of the two Sizes.
+		/// </remarks>
+
+		public static bool operator == (Size sz1, Size sz2)
+		{
+			return ((sz1.Width == sz2.Width) && 
+				(sz1.Height == sz2.Height));
+		}
+		
+		/// <summary>
+		///	Inequality Operator
+		/// </summary>
+		///
+		/// <remarks>
+		///	Compares two Size objects. The return value is
+		///	based on the equivalence of the Width and Height 
+		///	properties of the two Sizes.
+		/// </remarks>
+
+		public static bool operator != (Size sz1, Size sz2)
+		{
+			return ((sz1.Width != sz2.Width) || 
+				(sz1.Height != sz2.Height));
+		}
+		
+		/// <summary>
+		///	Subtraction Operator
+		/// </summary>
+		///
+		/// <remarks>
+		///	Subtracts two Size structures.
+		/// </remarks>
+
+		public static Size operator - (Size sz1, Size sz2)
+		{
+			return new Size (sz1.Width - sz2.Width, 
+					 sz1.Height - sz2.Height);
+		}
+		
+		/// <summary>
+		///	Size to Point Conversion
+		/// </summary>
+		///
+		/// <remarks>
+		///	Returns a Point based on the dimensions of a given 
+		///	Size. Requires explicit cast.
+		/// </remarks>
+
+		public static explicit operator Point (Size size)
+		{
+			return new Point (size.Width, size.Height);
+		}
+
+		/// <summary>
+		///	Size Constructor
+		/// </summary>
+		///
+		/// <remarks>
+		///	Creates a Size from a Point value.
+		/// </remarks>
+		
+		public Size (Point pt)
+		{
+			width = pt.X;
+			height = pt.Y;
+		}
+
+		/// <summary>
+		///	Size Constructor
+		/// </summary>
+		///
+		/// <remarks>
+		///	Creates a Size from specified dimensions.
+		/// </remarks>
+		
+		public Size (int width, int height)
+		{
+			this.width = width;
+			this.height = height;
+		}
+
+		/// <summary>
+		///	IsEmpty Property
+		/// </summary>
+		///
+		/// <remarks>
+		///	Indicates if both Width and Height are zero.
+		/// </remarks>
+		
+		public bool IsEmpty {
+			get {
+				return ((width == 0) && (height == 0));
+			}
+		}
+
+		/// <summary>
+		///	Width Property
+		/// </summary>
+		///
+		/// <remarks>
+		///	The Width coordinate of the Size.
+		/// </remarks>
+		
+		public int Width {
+			get {
+				return width;
+			}
+			set {
+				width = value;
+			}
+		}
+
+		/// <summary>
+		///	Height Property
+		/// </summary>
+		///
+		/// <remarks>
+		///	The Height coordinate of the Size.
+		/// </remarks>
+		
+		public int Height {
+			get {
+				return height;
+			}
+			set {
+				height = value;
+			}
+		}
+
+		/// <summary>
+		///	Equals Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Checks equivalence of this Size and another object.
+		/// </remarks>
+		
+		public override bool Equals (object obj)
+		{
+			if (!(obj is Size))
+				return false;
+
+			return (this == (Size) obj);
+		}
+
+		/// <summary>
+		///	GetHashCode Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Calculates a hashing value.
+		/// </remarks>
+		
+		public override int GetHashCode ()
+		{
+			return width^height;
+		}
+
+		/// <summary>
+		///	ToString Method
+		/// </summary>
+		///
+		/// <remarks>
+		///	Formats the Size as a string in coordinate notation.
+		/// </remarks>
+		
+		public override string ToString ()
+		{
+			return String.Format ("{{Width={0}, Height={1}}}", width, height);
+		}
+
+		public static Size Add (Size sz1, Size sz2)
+		{
+			return new Size (sz1.Width + sz2.Width, 
+					 sz1.Height + sz2.Height);
+
+		}
+		
+		public static Size Subtract (Size sz1, Size sz2)
+		{
+			return new Size (sz1.Width - sz2.Width, 
+					 sz1.Height - sz2.Height);
+		}
+
+	}
+}

+ 9 - 0
demo.cs

@@ -0,0 +1,9 @@
+using Terminal;
+
+class Demo {
+    static void Main ()
+    {
+        var app = new Application ();
+        
+    }
+}

+ 80 - 11
driver.cs

@@ -1,20 +1,45 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using C=Unix.Terminal;
+using Unix.Terminal;
 
 
 namespace Terminal {
 namespace Terminal {
+    
+    public class ColorScheme {
+        public int Normal;
+        public int Focus;
+        public int HotNormal;
+        public int HotFocus;
+        public int Marked => HotNormal;
+        public int MarkedSelected => HotFocus;
+
+    }
 
 
     public abstract class ConsoleDriver {
     public abstract class ConsoleDriver {
         public abstract int Cols {get;}
         public abstract int Cols {get;}
         public abstract int Rows {get;}
         public abstract int Rows {get;}
         public abstract void Init ();
         public abstract void Init ();
+        public abstract void Move (int line, int col);
+        public abstract void AddCh (int ch);
+
+        // Colors used for widgets
+        public static ColorScheme ColorBase, ColorDialog, ColorMenu, ColorError;
     }
     }
 
 
     public class CursesDriver : ConsoleDriver {
     public class CursesDriver : ConsoleDriver {
-        public override int Cols => C.Curses.Cols;
-        public override int Rows => C.Curses.Lines;
+        public override int Cols => Curses.Cols;
+        public override int Rows => Curses.Lines;
+
+        public override void Move(int col, int row) => Curses.move (row, col);
+        public override void AddCh(int ch) => Curses.addch (ch);
 
 
-        public C.Curses.Window window;
+        public Curses.Window window;
+
+        static short last_color_pair;
+        static int MakeColor (short f, short b)
+        {
+            Curses.InitColorPair (++last_color_pair, f, b);
+            return Curses.ColorPair (last_color_pair);
+        }
 
 
         public override void Init()
         public override void Init()
         {
         {
@@ -22,17 +47,61 @@ namespace Terminal {
                 return;
                 return;
 
 
             try {
             try {
-                window = C.Curses.initscr ();
+                window = Curses.initscr ();
             } catch (Exception e){
             } catch (Exception e){
                 Console.WriteLine ("Curses failed to initialize, the exception is: " + e);
                 Console.WriteLine ("Curses failed to initialize, the exception is: " + e);
             }
             }
-            C.Curses.raw ();
-            C.Curses.noecho ();
-            C.Curses.Window.Standard.keypad (true);
+            Curses.raw ();
+            Curses.noecho ();
+            Curses.Window.Standard.keypad (true);
         
         
-            if (C.Curses.HasColors){
-                C.Curses.StartColor ();
-                C.Curses.UseDefaultColors ();
+            ColorBase = new ColorScheme ();
+            ColorDialog = new ColorScheme ();
+            ColorMenu = new ColorScheme ();
+            ColorError = new ColorScheme ();
+
+            if (Curses.HasColors){
+                Curses.StartColor ();
+                Curses.UseDefaultColors ();
+
+                ColorBase.Normal = MakeColor (Curses.COLOR_WHITE, Curses.COLOR_BLUE);
+                ColorBase.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_CYAN);
+                ColorBase.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_BLUE);
+                ColorBase.HotFocus = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_CYAN);
+
+                ColorMenu.Normal = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_CYAN);
+                ColorMenu.Focus = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_CYAN);
+                ColorMenu.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_BLACK);
+                ColorMenu.HotFocus = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_BLACK);
+                ColorDialog.Normal    = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_WHITE);
+                ColorDialog.Focus     = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_CYAN);
+                ColorDialog.HotNormal = MakeColor (Curses.COLOR_BLUE,  Curses.COLOR_WHITE);
+                ColorDialog.HotFocus  = MakeColor (Curses.COLOR_BLUE,  Curses.COLOR_CYAN);
+
+                ColorError.Normal = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_RED);
+                ColorError.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_WHITE);
+                ColorError.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_RED);
+                ColorError.HotFocus = ColorError.HotNormal;
+            } else {
+                ColorBase.Normal = Curses.A_NORMAL;
+                ColorBase.Focus = Curses.A_REVERSE;
+                ColorBase.HotNormal = Curses.A_BOLD;
+                ColorBase.HotFocus = Curses.A_BOLD | Curses.A_REVERSE;
+
+                ColorMenu.Normal = Curses.A_REVERSE;
+                ColorMenu.Focus = Curses.A_NORMAL;
+                ColorMenu.HotNormal = Curses.A_BOLD;
+                ColorMenu.HotFocus = Curses.A_NORMAL;
+
+                ColorDialog.Normal    = Curses.A_REVERSE;
+                ColorDialog.Focus     = Curses.A_NORMAL;
+                ColorDialog.HotNormal = Curses.A_BOLD;
+                ColorDialog.HotFocus  = Curses.A_NORMAL;
+
+                ColorError.Normal = Curses.A_BOLD;
+                ColorError.Focus = Curses.A_BOLD | Curses.A_REVERSE;
+                ColorError.HotNormal = Curses.A_BOLD | Curses.A_REVERSE;
+                ColorError.HotFocus = Curses.A_REVERSE;
             }
             }
         }
         }
     }
     }