Browse Source

Improve label

Miguel de Icaza 7 years ago
parent
commit
6883bc4692
4 changed files with 128 additions and 22 deletions
  1. 35 19
      Core.cs
  2. 2 0
      Driver.cs
  3. 84 2
      Views/Label.cs
  4. 7 1
      demo.cs

+ 35 - 19
Core.cs

@@ -7,6 +7,7 @@
 // Optimziations
 //   - Add rendering limitation to the exposed area
 using System;
+using System.Collections;
 using System.Collections.Generic;
 
 namespace Terminal {
@@ -22,7 +23,7 @@ namespace Terminal {
         public virtual void MouseEvent (Event.Mouse me) { }
     }
 
-    public class View : Responder {
+    public class View : Responder, IEnumerable {
         View container = null;
         View focused = null;
         public static ConsoleDriver Driver = Application.Driver;
@@ -34,9 +35,6 @@ namespace Terminal {
         // The frame for the object
         Rect frame;
 
-        // The offset of the first child view inside the view
-        Point offset;
-
         // The frame for this view
         public Rect Frame {
             get => frame;
@@ -46,6 +44,12 @@ namespace Terminal {
             }
         }
 
+        public IEnumerator GetEnumerator ()
+        {
+            foreach (var v in subviews)
+                yield return v;
+        }
+
         public Rect Bounds {
             get => new Rect (Point.Empty, Frame.Size);
             set {
@@ -75,7 +79,7 @@ namespace Terminal {
         /// </summary>
         /// <remarks>
         /// </remarks>
-        public void Add (View view)
+        public virtual void Add (View view)
         {
             if (view == null)
                 return;
@@ -150,8 +154,8 @@ namespace Terminal {
         internal void ViewToScreen (int col, int row, out int rcol, out int rrow, bool clipped = true)
         {
             // Computes the real row, col relative to the screen.
-            rrow = row + frame.X;
-            rcol = col + frame.Y;
+            rrow = row + frame.Y;
+            rcol = col + frame.X;
             var ccontainer = container;
             while (ccontainer != null) {
                 rrow += ccontainer.frame.Y;
@@ -238,15 +242,17 @@ namespace Terminal {
         /// <summary>
         /// Performs a redraw of this view and its subviews, only redraws the views that have been flagged for a re-display.
         /// </summary>
-        public virtual void Redraw ()
+        public virtual void Redraw (Rect region)
         {
-            var clipRect = new Rect (offset, frame.Size);
+            var clipRect = new Rect (Point.Empty, frame.Size);
 
             if (subviews != null) {
                 foreach (var view in subviews) {
                     if (view.NeedDisplay) {
-                        if (view.Frame.IntersectsWith (clipRect)) {
-                            view.Redraw ();
+                        if (view.Frame.IntersectsWith (clipRect) && view.Frame.IntersectsWith (region)) {
+
+                            // TODO: optimize this by computing the intersection of region and view.Bounds
+                            view.Redraw (view.Bounds);
                         }
                         view.NeedDisplay = false;
                     }
@@ -434,7 +440,7 @@ namespace Terminal {
     /// <summary>
     /// A toplevel view that draws a frame around its region
     /// </summary>
-    public class Window : Toplevel {
+    public class Window : Toplevel, IEnumerable {
         View contentView;
         string title;
 
@@ -451,7 +457,12 @@ namespace Terminal {
             this.Title = title;
             frame.Inflate (-1, -1);
             contentView = new View (frame);
-            Add(contentView);
+            base.Add(contentView);
+        }
+
+        public IEnumerator GetEnumerator ()
+        {
+            return contentView.GetEnumerator ();
         }
 
         void DrawFrame ()
@@ -459,7 +470,12 @@ namespace Terminal {
             DrawFrame (new Rect(0, 0, Frame.Width, Frame.Height), true);
         }
 
-        public override void Redraw ()
+        public override void Add (View view)
+        {
+            contentView.Add (view);
+        }
+
+        public override void Redraw (Rect bounds)
         {
             Driver.SetColor (Colors.Base.Normal);
             DrawFrame ();
@@ -474,7 +490,7 @@ namespace Terminal {
                 Driver.AddCh (' ');
             }
             Driver.SetColor (Colors.Dialog.Normal);
-            contentView.Redraw ();
+            contentView.Redraw (contentView.Bounds);
         }
     }
 
@@ -579,13 +595,13 @@ namespace Terminal {
 
         static void Redraw (View view)
         {
-            view.Redraw ();
+            view.Redraw (view.Bounds);
             Driver.Refresh ();
         }
 
         static void Refresh (View view)
         {
-            view.Redraw ();
+            view.Redraw (view.Bounds);
             Driver.Refresh ();
         }
 
@@ -594,7 +610,7 @@ namespace Terminal {
             Driver.RedrawTop ();
             View last = null;
             foreach (var v in toplevels){
-                v.Redraw ();
+                v.Redraw (v.Bounds);
                 last = v;
             }
             if (last != null)
@@ -635,7 +651,7 @@ namespace Terminal {
                 } else if (wait == false)
                     return;
                 if (state.Toplevel.NeedDisplay)
-                    state.Toplevel.Redraw ();
+                    state.Toplevel.Redraw (state.Toplevel.Bounds);
             }
         }
 

+ 2 - 0
Driver.cs

@@ -26,6 +26,8 @@ namespace Terminal {
 
     public static class Colors {
         public static ColorScheme Base, Dialog, Menu, Error;
+
+        public 
     }
 
     public abstract class ConsoleDriver {

+ 84 - 2
Views/Label.cs

@@ -1,5 +1,7 @@
 
 using System;
+using System.Collections.Generic;
+using System.Linq;
 
 namespace Terminal {
     public enum TextAlignment {
@@ -10,6 +12,8 @@ namespace Terminal {
     ///   Label widget, displays a string at a given position, can include multiple lines.
     /// </summary>
     public class Label : View {
+        List<string> lines = new List<string> ();
+        bool recalcPending = true;
         string text;
         TextAlignment textAlignment;
 
@@ -51,8 +55,64 @@ namespace Terminal {
             this.text = text;
         }
 
-        public override void Redraw ()
+        static char [] whitespace = new char [] { ' ', '\t' };
+
+        string ClipAndJustify (string str)
         {
+            int slen = str.Length;
+            if (slen > Frame.Width)
+                return str.Substring (0, Frame.Width);
+            else {
+                if (textAlignment == TextAlignment.Justified) {
+                    var words = str.Split (whitespace, StringSplitOptions.RemoveEmptyEntries);
+                    int textCount = words.Sum ((arg) => arg.Length);
+
+                    var spaces = (Frame.Width - textCount) / (words.Length - 1);
+                    var extras = (Frame.Width - textCount) % words.Length;
+                    var s = new System.Text.StringBuilder ();
+                    //s.Append ($"tc={textCount} sp={spaces},x={extras} - ");
+                    for (int w = 0; w < words.Length; w++) {
+                        var x = words [w];
+                        s.Append (x);
+                        if (w + 1 < words.Length)
+                            for (int i = 0; i < spaces; i++)
+                                s.Append (' ');
+                        if (extras > 0) {
+                            s.Append ('_');
+                            extras--;
+                        }
+                    }
+                    return s.ToString ();
+                }
+                return str;
+            }
+        }
+
+        void Recalc ()
+        {
+            lines.Clear ();
+            if (text.IndexOf ('\n') == -1) {
+                lines.Add (ClipAndJustify (text));
+                return;
+            }
+            int textLen = text.Length;
+            int lp = 0;
+            for (int i = 0; i < textLen; i++) {
+                char c = text [i];
+
+                if (c == '\n') {
+                    lines.Add (ClipAndJustify (text.Substring (lp, i - lp)));
+                    lp = i + 1;
+                }
+            }
+            recalcPending = false;
+        }
+
+        public override void Redraw (Rect region)
+        {
+            if (recalcPending)
+                Recalc ();
+            
             if (TextColor != -1)
                 Driver.SetColor (TextColor);
             else
@@ -60,7 +120,28 @@ namespace Terminal {
 
             Clear ();
             Move (Frame.X, Frame.Y);
-            Driver.AddStr (text);
+            for (int line = 0; line < lines.Count; line++) {
+                if (line < region.Top || line >= region.Bottom)
+                    continue;
+                var str = lines [line];
+                int x;
+                switch (textAlignment) {
+                case TextAlignment.Left:
+                case TextAlignment.Justified:
+                    x = 0;
+                    break;
+                case TextAlignment.Right:
+                    x = Frame.Right - str.Length;
+                    break;
+                case TextAlignment.Centered:
+                    x = Frame.Left + (Frame.Width - str.Length) / 2;
+                    break;
+                default:
+                    throw new ArgumentOutOfRangeException ();
+                }
+                Move (x, line);
+                Driver.AddStr (str);
+            }
         }
 
         /// <summary>
@@ -70,6 +151,7 @@ namespace Terminal {
             get => text;
             set {
                 text = value;
+                recalcPending = true;
                 SetNeedsDisplay ();
             }
         }

+ 7 - 1
demo.cs

@@ -5,7 +5,13 @@ class Demo {
     {
         Application.Init ();
         var top = Application.Top;
-        top.Add (new Window (new Rect (0, 0, 80, 24), "Hello"));
+        var win = new Window (new Rect (0, 0, 80, 24), "Hello") {
+            new Label (new Rect (0, 0, 40, 3), "1-Hello world, how are you doing today") { TextAlignment = TextAlignment.Left },
+            new Label (new Rect (0, 4, 40, 3), "2-Hello world, how are you doing today") { TextAlignment = TextAlignment.Right},
+            new Label (new Rect (0, 8, 40, 3), "3-Hello world, how are you doing today") { TextAlignment = TextAlignment.Centered },
+            new Label (new Rect (0, 12, 40, 3), "4-Hello world, how are you doing today") { TextAlignment = TextAlignment.Justified}
+        };
+        top.Add (win);
         Application.Run ();
     }
 }