Improve label

This commit is contained in:
Miguel de Icaza
2017-12-16 21:27:23 -05:00
parent 2274866c47
commit 6883bc4692
4 changed files with 128 additions and 22 deletions

54
Core.cs
View File

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

View File

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

View File

@@ -1,5 +1,7 @@
 
using System; using System;
using System.Collections.Generic;
using System.Linq;
namespace Terminal { namespace Terminal {
public enum TextAlignment { public enum TextAlignment {
@@ -10,6 +12,8 @@ namespace Terminal {
/// Label widget, displays a string at a given position, can include multiple lines. /// Label widget, displays a string at a given position, can include multiple lines.
/// </summary> /// </summary>
public class Label : View { public class Label : View {
List<string> lines = new List<string> ();
bool recalcPending = true;
string text; string text;
TextAlignment textAlignment; TextAlignment textAlignment;
@@ -51,8 +55,64 @@ namespace Terminal {
this.text = text; 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) if (TextColor != -1)
Driver.SetColor (TextColor); Driver.SetColor (TextColor);
else else
@@ -60,7 +120,28 @@ namespace Terminal {
Clear (); Clear ();
Move (Frame.X, Frame.Y); 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> /// <summary>
@@ -70,6 +151,7 @@ namespace Terminal {
get => text; get => text;
set { set {
text = value; text = value;
recalcPending = true;
SetNeedsDisplay (); SetNeedsDisplay ();
} }
} }

View File

@@ -5,7 +5,13 @@ class Demo {
{ {
Application.Init (); Application.Init ();
var top = Application.Top; 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 (); Application.Run ();
} }
} }