Browse Source

Add support for clipping

Miguel de Icaza 7 years ago
parent
commit
cc88cb44e1
3 changed files with 140 additions and 70 deletions
  1. 84 50
      Application.cs
  2. 1 1
      demo.cs
  3. 55 19
      driver.cs

+ 84 - 50
Application.cs

@@ -16,19 +16,19 @@ namespace Terminal {
         public bool HasFocus { get; internal set; }
 
         // Key handling
-        public virtual void KeyDown (Event.Key kb) {}
+        public virtual void KeyDown (Event.Key kb) { }
 
         // Mouse events
-        public virtual void MouseEvent (Event.Mouse me) {}
+        public virtual void MouseEvent (Event.Mouse me) { }
     }
 
     public class View : Responder {
         View container = null;
         View focused = null;
         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;
-        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
@@ -38,7 +38,7 @@ namespace Terminal {
         Point offset;
 
         // The frame for this view
-        public Rect Frame { 
+        public Rect Frame {
             get => frame;
             set {
                 frame = value;
@@ -46,6 +46,13 @@ namespace Terminal {
             }
         }
 
+        public Rect Bounds {
+            get => new Rect (Point.Empty, Frame.Size);
+            set {
+                Frame = new Rect (frame.Location, value.Size);
+            }
+        }
+
         public View (Rect frame)
         {
             this.Frame = frame;
@@ -92,7 +99,7 @@ namespace Terminal {
                 var view = subviews [0];
                 Remove (view);
                 subviews.RemoveAt (0);
-            }        
+            }
         }
 
         /// <summary>
@@ -126,7 +133,7 @@ namespace Terminal {
             var w = Frame.Width;
             for (int line = 0; line < h; line++) {
                 Move (0, line);
-                for (int col = 0; col < w; col++) 
+                for (int col = 0; col < w; col++)
                     Driver.AddCh (' ');
             }
         }
@@ -144,7 +151,7 @@ namespace Terminal {
             rrow = row + frame.X;
             rcol = col + frame.Y;
             var ccontainer = container;
-            while (ccontainer != null){
+            while (ccontainer != null) {
                 rrow += ccontainer.frame.Y;
                 rcol += ccontainer.frame.X;
                 ccontainer = ccontainer.container;
@@ -157,7 +164,21 @@ namespace Terminal {
             }
         }
 
-      
+        Rect RectToScreen (Rect rect)
+        {
+            ViewToScreen (rect.X, rect.Y, out var x, out var y, clipped: false);
+            return new Rect (x, y, rect.Width, rect.Height);
+        }
+
+        Rect ScreenClip (Rect rect)
+        {
+            var x = rect.X < 0 ? 0 : rect.X;
+            var y = rect.Y < 0 ? 0 : rect.Y;
+            var w = rect.X + rect.Width >= Driver.Cols ? Driver.Cols - rect.X : rect.Width;
+            var h = rect.Y + rect.Height >= Driver.Rows ? Driver.Rows - rect.Y : rect.Height;
+
+            return new Rect (x, y, w, h);
+        }
 
         /// <summary>
         /// Draws a frame in the current view, clipped by the boundary of this view
@@ -166,8 +187,11 @@ namespace Terminal {
         /// <param name="fill">If set to <c>true</c> it fill will the contents.</param>
         public void DrawFrame (Rect rect, bool fill = false)
         {
-            ViewToScreen (rect.X, rect.Y, out var x, out var y, clipped: false);
-            Driver.DrawFrame (new Rect (x, y, rect.Width, rect.Height), fill);
+            var scrRect = RectToScreen (rect);
+            var savedClip = Driver.Clip;
+            Driver.Clip = ScreenClip (RectToScreen (Bounds));
+            Driver.DrawFrame (scrRect, fill);
+            Driver.Clip = savedClip;
         }
 
         /// <summary>
@@ -203,7 +227,7 @@ namespace Terminal {
         {
             if (row < 0 || col < 0)
                 return;
-            if (row > frame.Height-1 || col > frame.Width-1)
+            if (row > frame.Height - 1 || col > frame.Width - 1)
                 return;
             Move (col, row);
             Driver.AddCh (ch);
@@ -249,7 +273,7 @@ namespace Terminal {
                     break;
             if (c == null)
                 throw new ArgumentException ("the specified view is not part of the hierarchy of this view");
-            
+
             if (focused != null)
                 focused.HasFocus = false;
             focused = view;
@@ -273,8 +297,8 @@ namespace Terminal {
         /// </summary>
         public void FocusFirst ()
         {
-            foreach (var view in subviews){
-                if (view.CanFocus){
+            foreach (var view in subviews) {
+                if (view.CanFocus) {
                     SetFocus (view);
                     return;
                 }
@@ -286,11 +310,11 @@ namespace Terminal {
         /// </summary>
         public void FocusLast ()
         {
-            for (int i = subviews.Count; i > 0; ){
+            for (int i = subviews.Count; i > 0;) {
                 i--;
 
                 View v = subviews [i];
-                if (v.CanFocus){
+                if (v.CanFocus) {
                     SetFocus (v);
                     return;
                 }
@@ -303,32 +327,32 @@ namespace Terminal {
         /// <returns><c>true</c>, if previous was focused, <c>false</c> otherwise.</returns>
         public bool FocusPrev ()
         {
-            if (focused == null){
+            if (focused == null) {
                 FocusLast ();
                 return true;
             }
             int focused_idx = -1;
-            for (int i = subviews.Count; i > 0; ){
+            for (int i = subviews.Count; i > 0;) {
                 i--;
                 View w = subviews [i];
 
-                if (w.HasFocus){
+                if (w.HasFocus) {
                     if (w.FocusPrev ())
-                            return true;
+                        return true;
                     focused_idx = i;
                     continue;
                 }
-                if (w.CanFocus && focused_idx != -1){
+                if (w.CanFocus && focused_idx != -1) {
                     focused.HasFocus = false;
 
                     if (w.CanFocus)
-                        w.FocusLast (); 
-                
+                        w.FocusLast ();
+
                     SetFocus (w);
                     return true;
                 }
             }
-            if (focused != null){
+            if (focused != null) {
                 focused.HasFocus = false;
                 focused = null;
             }
@@ -340,35 +364,35 @@ namespace Terminal {
         /// </summary>
         /// <returns><c>true</c>, if next was focused, <c>false</c> otherwise.</returns>
         public bool FocusNext ()
-        {       
-            if (focused == null){
-                FocusFirst (); 
+        {
+            if (focused == null) {
+                FocusFirst ();
                 return focused != null;
             }
             int n = subviews.Count;
             int focused_idx = -1;
-            for (int i = 0; i < n; i++){
-                    View w = subviews [i];
-                    
-                    if (w.HasFocus){
-                            if (w.FocusNext ())
-                                return true;
-                            focused_idx = i;
-                            continue;
-                    }
-                    if (w.CanFocus && focused_idx != -1){
-                        focused.HasFocus = false;
+            for (int i = 0; i < n; i++) {
+                View w = subviews [i];
 
-                        if (w != null && w.CanFocus)
-                            w.FocusFirst ();
-                        
-                        SetFocus (w);
+                if (w.HasFocus) {
+                    if (w.FocusNext ())
                         return true;
-                    }
-            }
-            if (focused != null){
+                    focused_idx = i;
+                    continue;
+                }
+                if (w.CanFocus && focused_idx != -1) {
                     focused.HasFocus = false;
-                    focused = null;
+
+                    if (w != null && w.CanFocus)
+                        w.FocusFirst ();
+
+                    SetFocus (w);
+                    return true;
+                }
+            }
+            if (focused != null) {
+                focused.HasFocus = false;
+                focused = null;
             }
             return false;
         }
@@ -388,10 +412,21 @@ namespace Terminal {
         {
         }
 
-        public static Toplevel Create () 
+        public static Toplevel Create ()
         {
             return new Toplevel (new Rect (0, 0, Driver.Cols, Driver.Rows));
         }
+
+#if false
+        public override void Redraw ()
+        {
+            base.Redraw ();
+            for (int i = 0; i < Driver.Cols; i++) {
+                Driver.Move (0, i);
+                Driver.AddStr ("Line: " + i);
+            }
+        }
+#endif
     }
 
     /// <summary>
@@ -419,13 +454,12 @@ namespace Terminal {
 
         void DrawFrame ()
         {
-            DrawFrame (new Rect (Point.Empty, Frame.Size), true);
+            DrawFrame (new Rect(0, 0, Frame.Width, Frame.Height), true);
         }
 
         public override void Redraw ()
         {
             Driver.SetColor (Colors.Base.Normal);
-            Clear ();
             DrawFrame ();
             if (HasFocus)
                 Driver.SetColor (Colors.Dialog.Normal);

+ 1 - 1
demo.cs

@@ -5,7 +5,7 @@ class Demo {
     {
         Application.Init ();
         var top = Application.Top;
-        top.Add (new Window (new Rect (10, 10, 20, 10), "Hello"));
+        top.Add (new Window (new Rect (0, 0, 80, 24), "Hello"));
         Application.Run ();
     }
 }

+ 55 - 19
driver.cs

@@ -42,16 +42,53 @@ namespace Terminal {
         public abstract void SetColor (Color c);
         public abstract void DrawFrame (Rect region, bool fill);
 
-        // Colors used for widgets
+        Rect clip;
+        public Rect Clip {
+            get => clip;
+            set => this.clip = value;
+        }
     }
 
     public class CursesDriver : ConsoleDriver {
         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 override void AddStr (string str) => Curses.addstr (str);
+        // Current row, and current col, tracked by Move/AddCh only
+        int ccol, crow;
+        bool needMove;
+        public override void Move (int col, int row)
+        {
+            ccol = col;
+            crow = row;
+
+            if (Clip.Contains (col, row)) {
+                Curses.move (row, col);
+                needMove = false;
+            } else {
+                Curses.move (Clip.Y, Clip.X);
+                needMove = true;
+            }
+        }
+
+        public override void AddCh (int ch)
+        {
+            if (Clip.Contains (ccol, crow)) {
+                if (needMove) {
+                    Curses.move (crow, ccol);
+                    needMove = false;
+                }
+                Curses.addch (ch);
+            } else
+                needMove = true;
+            ccol++;
+        }
+
+        public override void AddStr (string str)
+        {
+            foreach (var c in str)
+                AddCh ((int) c);
+        }
+
         public override void Refresh() => Curses.refresh ();
         public override void End() => Curses.endwin ();
         public override void RedrawTop() => window.redrawwin ();
@@ -76,27 +113,26 @@ namespace Terminal {
             int height = region.Height;
             int b;
 
-            Curses.move (region.Y, region.X);
-            Curses.addch (Curses.ACS_ULCORNER);
+            Move (region.X, region.Y);
+            AddCh (Curses.ACS_ULCORNER);
             for (b = 0; b < width - 2; b++)
-                Curses.addch (Curses.ACS_HLINE);
-            Curses.addch (Curses.ACS_URCORNER);
-
+                AddCh (Curses.ACS_HLINE);
+            AddCh (Curses.ACS_URCORNER);
             for (b = 1; b < height - 1; b++) {
-                Curses.move (region.Y + b, region.X);
-                Curses.addch (Curses.ACS_VLINE);
+                Move (region.X, region.Y + b);
+                AddCh (Curses.ACS_VLINE);
                 if (fill) {
                     for (int x = 1; x < width - 1; x++)
-                        Curses.addch (' ');
+                        AddCh (' ');
                 } else
-                    Curses.move (region.Y + b, region.X + width - 1);
-                Curses.addch (Curses.ACS_VLINE);
+                    Move (region.X + width - 1, region.Y + b);
+                AddCh (Curses.ACS_VLINE);
             }
-            Curses.move (region.Y + height - 1, region.X);
-            Curses.addch (Curses.ACS_LLCORNER);
+            Move (region.X, region.Y + height - 1);
+            AddCh (Curses.ACS_LLCORNER);
             for (b = 0; b < width - 2; b++)
-                Curses.addch (Curses.ACS_HLINE);
-            Curses.addch (Curses.ACS_LRCORNER);
+                AddCh (Curses.ACS_HLINE);
+            AddCh (Curses.ACS_LRCORNER);
         }
 
         public override void Init()
@@ -117,7 +153,7 @@ namespace Terminal {
             Colors.Dialog = new ColorScheme ();
             Colors.Menu = new ColorScheme ();
             Colors.Error = new ColorScheme ();
-
+            Clip = new Rect (0, 0, Cols, Rows);
             if (Curses.HasColors){
                 Curses.StartColor ();
                 Curses.UseDefaultColors ();