diff --git a/Core.cs b/Core.cs
index 2d92c0cff..fb753e4a2 100644
--- a/Core.cs
+++ b/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 {
///
///
///
- 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 {
///
/// Performs a redraw of this view and its subviews, only redraws the views that have been flagged for a re-display.
///
- 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 {
///
/// A toplevel view that draws a frame around its region
///
- 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);
}
}
diff --git a/Driver.cs b/Driver.cs
index d795d5f71..a03ece147 100644
--- a/Driver.cs
+++ b/Driver.cs
@@ -26,6 +26,8 @@ namespace Terminal {
public static class Colors {
public static ColorScheme Base, Dialog, Menu, Error;
+
+ public
}
public abstract class ConsoleDriver {
diff --git a/Views/Label.cs b/Views/Label.cs
index 056acd0ed..9e992e6e1 100644
--- a/Views/Label.cs
+++ b/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.
///
public class Label : View {
+ List lines = new List ();
+ 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);
+ }
}
///
@@ -70,6 +151,7 @@ namespace Terminal {
get => text;
set {
text = value;
+ recalcPending = true;
SetNeedsDisplay ();
}
}
diff --git a/demo.cs b/demo.cs
index 5df80176e..243257280 100644
--- a/demo.cs
+++ b/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 ();
}
}
\ No newline at end of file