diff --git a/Terminal.Gui/Core/Range.cs b/Terminal.Gui/Core/Range.cs
new file mode 100644
index 000000000..0cf10a1cd
--- /dev/null
+++ b/Terminal.Gui/Core/Range.cs
@@ -0,0 +1,4 @@
+namespace Terminal.Gui {
+ internal class Range {
+ }
+}
\ No newline at end of file
diff --git a/Terminal.Gui/Core/TextFormatter.cs b/Terminal.Gui/Core/TextFormatter.cs
index 36989bef1..e2a4b7221 100644
--- a/Terminal.Gui/Core/TextFormatter.cs
+++ b/Terminal.Gui/Core/TextFormatter.cs
@@ -5,6 +5,28 @@ using System.Linq;
using NStack;
namespace Terminal.Gui {
+ ///
+ /// Text alignment enumeration, controls how text is displayed.
+ ///
+ public enum TextAlignment {
+ ///
+ /// Aligns the text to the left of the frame.
+ ///
+ Left,
+ ///
+ /// Aligns the text to the right side of the frame.
+ ///
+ Right,
+ ///
+ /// Centers the text in the frame.
+ ///
+ Centered,
+ ///
+ /// Shows the text as justified text in the frame.
+ ///
+ Justified
+ }
+
///
/// Provides text formatting capabilites for console apps. Supports, hotkeys, horizontal alignment, multille lines, and word-based line wrap.
///
@@ -177,21 +199,20 @@ namespace Terminal.Gui {
return lines;
}
- var runes = StripCRLF (text).ToRunes ();
+ var runes = StripCRLF (text).ToRuneList();
- while ((end = start + width) < runes.Length) {
+ while ((end = start + width) < runes.Count) {
while (runes [end] != ' ' && end > start)
end -= 1;
if (end == start)
end = start + width;
-
-
- lines.Add (ustring.Make (runes [start..end]).TrimSpace ());
+ lines.Add (ustring.Make (runes.GetRange (start, end - start)).TrimSpace());
start = end;
}
- if (start < text.RuneCount)
- lines.Add (ustring.Make (runes [start..]).TrimSpace ());
+ if (start < text.RuneCount) {
+ lines.Add (ustring.Make (runes.GetRange (start, runes.Count - start)).TrimSpace ());
+ }
return lines;
}
@@ -212,10 +233,10 @@ namespace Terminal.Gui {
return text;
}
- var runes = text.ToRunes ();
- int slen = runes.Length;
+ var runes = text.ToRuneList ();
+ int slen = runes.Count;
if (slen > width) {
- return ustring.Make (runes [0..width]); // text [0, width];
+ return ustring.Make (runes.GetRange(0, width));
} else {
if (talign == TextAlignment.Justified) {
return Justify (text, width);
@@ -302,13 +323,13 @@ namespace Terminal.Gui {
return lineResult;
}
- var runes = text.ToRunes ();
- int runeCount = runes.Length;
+ var runes = text.ToRuneList ();
+ int runeCount = runes.Count;
int lp = 0;
for (int i = 0; i < runeCount; i++) {
Rune c = text [i];
if (c == '\n') {
- var wrappedLines = WordWrap (ustring.Make (runes [lp..i]), width);
+ var wrappedLines = WordWrap (ustring.Make (runes.GetRange(lp, i - lp)), width);
foreach (var line in wrappedLines) {
lineResult.Add (ClipAndJustify (line, width, talign));
}
@@ -318,7 +339,7 @@ namespace Terminal.Gui {
lp = i + 1;
}
}
- foreach (var line in WordWrap (ustring.Make (runes [lp..runeCount]), width)) {
+ foreach (var line in WordWrap (ustring.Make (runes.GetRange(lp, runeCount - lp)), width)) {
lineResult.Add (ClipAndJustify (line, width, talign));
}
@@ -516,7 +537,7 @@ namespace Terminal.Gui {
// Use "Lines" to ensure a Format (don't use "lines"))
for (int line = 0; line < Lines.Count; line++) {
- if (line < (bounds.Height - bounds.Top) || line >= bounds.Height)
+ if (line > bounds.Height)
continue;
var runes = lines [line].ToRunes ();
int x;
@@ -537,7 +558,7 @@ namespace Terminal.Gui {
throw new ArgumentOutOfRangeException ();
}
for (var col = bounds.Left; col < bounds.Left + bounds.Width; col++) {
- Application.Driver.Move (col, bounds.Y + line);
+ Application.Driver.Move (col, bounds.Top + line);
var rune = (Rune)' ';
if (col >= x && col < (x + runes.Length)) {
rune = runes [col - x];
@@ -552,5 +573,6 @@ namespace Terminal.Gui {
}
}
}
+
}
}
diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs
index 3d5424626..36b02cd06 100644
--- a/Terminal.Gui/Core/View.cs
+++ b/Terminal.Gui/Core/View.cs
@@ -18,28 +18,6 @@ using System.Linq;
using NStack;
namespace Terminal.Gui {
- ///
- /// Text alignment enumeration, controls how text is displayed.
- ///
- public enum TextAlignment {
- ///
- /// Aligns the text to the left of the frame.
- ///
- Left,
- ///
- /// Aligns the text to the right side of the frame.
- ///
- Right,
- ///
- /// Centers the text in the frame.
- ///
- Centered,
- ///
- /// Shows the text as justified text in the frame.
- ///
- Justified
- }
-
///
/// Determines the LayoutStyle for a view, if Absolute, during LayoutSubviews, the
/// value from the Frame will be used, if the value is Computed, then the Frame
@@ -172,12 +150,12 @@ namespace Terminal.Gui {
public Action MouseClick;
///
- /// The HotKey defined for this view. A user pressing HotKey on the keyboard while this view has focus will cause the Clicked event to fire.
+ /// Gets or sets the HotKey defined for this view. A user pressing HotKey on the keyboard while this view has focus will cause the Clicked event to fire.
///
public Key HotKey { get => viewText.HotKey; set => viewText.HotKey = value; }
///
- ///
+ /// Gets or sets the specifier character for the hotkey (e.g. '_'). Set to '\xffff' to disable hotkey support for this View instance. The default is '\xffff'.
///
public Rune HotKeySpecifier { get => viewText.HotKeySpecifier; set => viewText.HotKeySpecifier = value; }
@@ -860,21 +838,24 @@ namespace Terminal.Gui {
///
/// Utility function to draw strings that contain a hotkey.
///
- /// String to display, the underscoore before a letter flags the next letter as the hotkey.
+ /// String to display, the hotkey specifier before a letter flags the next letter as the hotkey.
/// Hot color.
/// Normal color.
///
- /// The hotkey is any character following an underscore ('_') character.
+ /// The hotkey is any character following the hotkey specifier, which is the underscore ('_') character by default.
+ /// The hotkey specifier can be changed via
+ ///
public void DrawHotString (ustring text, Attribute hotColor, Attribute normalColor)
{
- Driver.SetAttribute (normalColor);
+ var hotkeySpec = HotKeySpecifier == (Rune)0xffff ? (Rune)'_' : HotKeySpecifier;
+ Application.Driver.SetAttribute (normalColor);
foreach (var rune in text) {
- if (rune == '_') {
- Driver.SetAttribute (hotColor);
+ if (rune == hotkeySpec) {
+ Application.Driver.SetAttribute (hotColor);
continue;
}
- Driver.AddRune (rune);
- Driver.SetAttribute (normalColor);
+ Application.Driver.AddRune (rune);
+ Application.Driver.SetAttribute (normalColor);
}
}
@@ -1078,9 +1059,8 @@ namespace Terminal.Gui {
if (!ustring.IsNullOrEmpty (Text)) {
Clear ();
// Draw any Text
- // TODO: Figure out if this should go here or after OnDrawContent
viewText?.SetNeedsFormat ();
- viewText?.Draw (ViewToScreen (Bounds), ColorScheme.Normal, ColorScheme.HotNormal);
+ viewText?.Draw (ViewToScreen (Bounds), HasFocus ? ColorScheme.Focus : ColorScheme.Normal, HasFocus ? ColorScheme.HotFocus : ColorScheme.HotNormal);
}
// Invoke DrawContentEvent
@@ -1594,7 +1574,19 @@ namespace Terminal.Gui {
/// The text displayed by the .
///
///
- /// The text will only be displayed if the View has no subviews.
+ ///
+ /// If provided, the text will be drawn before any subviews are drawn.
+ ///
+ ///
+ /// The text will be drawn starting at the view origin (0, 0) and will be formatted according
+ /// to the property. If the view's height is greater than 1, the
+ /// text will word-wrap to additional lines if it does not fit horizontally. If the view's height
+ /// is 1, the text will be clipped.
+ ///
+ ///
+ /// Set the to enable hotkey support. To disable hotkey support set to
+ /// (Rune)0xffff.
+ ///
///
public virtual ustring Text {
get => viewText.Text;
@@ -1605,7 +1597,7 @@ namespace Terminal.Gui {
}
///
- /// Controls the text-alignment property of the View. Changing this property will redisplay the .
+ /// Gets or sets how the View's is aligned horizontally when drawn. Changing this property will redisplay the .
///
/// The text alignment.
public virtual TextAlignment TextAlignment {
diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj
index 144731b3d..907228f62 100644
--- a/Terminal.Gui/Terminal.Gui.csproj
+++ b/Terminal.Gui/Terminal.Gui.csproj
@@ -1,12 +1,11 @@
- netstandard2.1
+ net472;netstandard2.0
Terminal.Gui
Terminal.Gui
bin\Release\Terminal.Gui.xml
true
0.90.0.0
- 8.0
true
@@ -75,7 +74,7 @@
* Added a OpenSelectedItem event to the ListView #429. (Thanks @bdisp!)
* Fixes the return value of the position cursor in the TextField. (Thanks @bdisp!)
* Updates screen on Unix window resizing. (Thanks @bdisp!)
- * Fixes the functions of the Edit->Copy-Cut-Paste menu for the TextField that was not working well. (Thanks @bdisp!)
+ * Fixes the functions of the Edit-Copy-Cut-Paste menu for the TextField that was not working well. (Thanks @bdisp!)
* More robust error handing in Pos/Dim. Fixes #355 stack overflow with Pos based on the size of windows at startup. Added a OnResized action to set the Pos after the terminal are resized. (Thanks @bdisp!)
* Fixes #389 Window layouting breaks when resizing. (Thanks @bdisp!)
* Fixes #557 MessageBox needs to take ustrings (BREAKING CHANGE). (Thanks @tig!)
@@ -101,6 +100,15 @@
* ConsoleDriver and Drivers have new standard glyph definitions for things like right arrow. (Thanks @tig!)
* ScrollView updated to use pretty glyphs. (Thanks @tig!)
* Menubar now uses pretty arrow glyph for sub-menus. (Thanks @tig!)
+ * The project now has a growing set of unit tests (over 100 tests). (Thanks @tig!)
+ * View now has a Text property, implemented via the new TextFormatting class. (Thanks @tig!)
+ * TextAlignment is implemented once across all Views that support it.
+ * Unicode support is now much more robust and complete; dozens of bugs fixed.
+ * Any view dervied from View now has a Text property with multi-line text formatting, including word-wrap and hotkey support.
+ * Any view derived from View now gets mouse click (Clicked event) support for free.
+ * Label is now just an alias for View.
+ * Button is now a very thin class derived from View (no API changes).
+ * Dozens of unit tests for TextAlignment are provided reducing the chance of regressions.
0.81:
* Fix ncurses engine for macOS/Linux, it works again
diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs
index 877d1be76..b5fa6fec4 100644
--- a/Terminal.Gui/Views/Button.cs
+++ b/Terminal.Gui/Views/Button.cs
@@ -15,8 +15,14 @@ namespace Terminal.Gui {
///
///
/// Provides a button showing text invokes an when clicked on with a mouse
- /// or when the user presses SPACE, ENTER, or hotkey. The hotkey is specified by the first uppercase
- /// letter in the button.
+ /// or when the user presses SPACE, ENTER, or hotkey. The hotkey is the first letter or digit following the first underscore ('_')
+ /// in the button text.
+ ///
+ ///
+ /// Use to change the hotkey specifier from the default of ('_').
+ ///
+ ///
+ /// If no hotkey specifier is found, the first uppercase letter encountered will be used as the hotkey.
///
///
/// When the button is configured as the default () and the user presses
@@ -103,19 +109,8 @@ namespace Terminal.Gui {
CanFocus = true;
this.IsDefault = is_default;
Text = text ?? string.Empty;
- //int w = SetWidthHeight (text, is_default);
- //Frame = new Rect (Frame.Location, new Size (w, 1));
}
- //int SetWidthHeight (ustring text, bool is_default)
- //{
- // int w = text.RuneCount;// + 4 + (is_default ? 2 : 0);
- // Width = w;
- // Height = 1;
- // Frame = new Rect (Frame.Location, new Size (w, 1));
- // return w;
- //}
-
///
/// The text displayed by this .
///
@@ -153,7 +148,6 @@ namespace Terminal.Gui {
Width = w;
Height = 1;
Frame = new Rect (Frame.Location, new Size (w, 1));
-
SetNeedsDisplay ();
}
@@ -196,6 +190,5 @@ namespace Terminal.Gui {
}
return base.ProcessKey (kb);
}
-
}
}
diff --git a/UICatalog/Scenarios/AllViewsTester.cs b/UICatalog/Scenarios/AllViewsTester.cs
index ff689f3b2..a30a7eb42 100644
--- a/UICatalog/Scenarios/AllViewsTester.cs
+++ b/UICatalog/Scenarios/AllViewsTester.cs
@@ -76,9 +76,9 @@ namespace UICatalog {
_leftPane = new Window ("Classes") {
X = 0,
- Y = 0, // for menu
+ Y = 0,
Width = 15,
- Height = Dim.Fill (),
+ Height = Dim.Fill (1), // for status bar
CanFocus = false,
ColorScheme = Colors.TopLevel,
};
@@ -87,7 +87,7 @@ namespace UICatalog {
X = 0,
Y = 0,
Width = Dim.Fill (0),
- Height = Dim.Fill (), // for status bar
+ Height = Dim.Fill (0),
AllowsMarking = false,
ColorScheme = Colors.TopLevel,
};
diff --git a/UICatalog/Scenarios/Buttons.cs b/UICatalog/Scenarios/Buttons.cs
index 1b1b483fe..975ca9ee4 100644
--- a/UICatalog/Scenarios/Buttons.cs
+++ b/UICatalog/Scenarios/Buttons.cs
@@ -70,9 +70,6 @@ namespace UICatalog {
//prev = colorButton;
x += colorButton.Frame.Width + 2;
}
- // BUGBUG: For some reason these buttons don't move to correct locations initially.
- // This was the only way I find to resolves this with the View prev variable.
- //Top.Ready += () => Top.Redraw (Top.Bounds);
Button button;
Win.Add (button = new Button ("A super long _Button that will probably expose a bug in clipping or wrapping of text. Will it?") {
@@ -187,23 +184,26 @@ namespace UICatalog {
ustring MoveHotkey (ustring txt)
{
// Remove the '_'
- var i = txt.IndexOf ('_');
+ var runes = txt.ToRuneList ();
+
+ var i = runes.IndexOf ('_');
ustring start = "";
- if (i > -1)
- start = txt [0, i];
- txt = start + txt [i + 1, txt.RuneCount];
+ if (i > -1) {
+ start = ustring.Make (runes.GetRange (0, i));
+ }
+ txt = start + ustring.Make (runes.GetRange (i + 1, runes.Count - (i + 1)));
+
+ runes = txt.ToRuneList ();
// Move over one or go to start
i++;
- if (i >= txt.RuneCount) {
+ if (i >= runes.Count) {
i = 0;
}
// Slip in the '_'
- start = txt [0, i];
- txt = start + ustring.Make ('_') + txt [i, txt.RuneCount];
-
- return txt;
+ start = ustring.Make (runes.GetRange (0, i));
+ return start + ustring.Make ('_') + ustring.Make (runes.GetRange (i, runes.Count - i));
}
var mhkb = "Click to Change th_is Button's Hotkey";
@@ -218,7 +218,7 @@ namespace UICatalog {
};
Win.Add (moveHotKeyBtn);
- var muhkb = ustring.Make(" ~ s gui.cs master ↑10 = Сохранить");
+ var muhkb = ustring.Make (" ~ s gui.cs master ↑10 = Сохранить");
var moveUnicodeHotKeyBtn = new Button (muhkb) {
X = Pos.Left (absoluteFrame) + 1,
Y = Pos.Bottom (radioGroup) + 1,
diff --git a/UICatalog/Scenarios/Clipping.cs b/UICatalog/Scenarios/Clipping.cs
index 2817ebac7..d2b9e9dd7 100644
--- a/UICatalog/Scenarios/Clipping.cs
+++ b/UICatalog/Scenarios/Clipping.cs
@@ -34,7 +34,7 @@ namespace UICatalog {
//Win.Height = Dim.Fill () - 2;
var label = new Label ("ScrollView (new Rect (5, 5, 100, 60)) with a 200, 100 ContentSize...") {
X = 0, Y = 0,
- ColorScheme = Colors.Dialog
+ //ColorScheme = Colors.Dialog
};
Top.Add (label);
diff --git a/UICatalog/Scenarios/LabelsAsButtons.cs b/UICatalog/Scenarios/LabelsAsButtons.cs
index 9ab09f861..243735f5b 100644
--- a/UICatalog/Scenarios/LabelsAsButtons.cs
+++ b/UICatalog/Scenarios/LabelsAsButtons.cs
@@ -6,10 +6,10 @@ using System.Reflection;
using Terminal.Gui;
namespace UICatalog {
- [ScenarioMetadata (Name: "LabelsAsButtons", Description: "POC to see how making Label more a base class would work")]
+ [ScenarioMetadata (Name: "Labels As Buttons", Description: "Illustrates that Button is really just a Label++")]
[ScenarioCategory ("Controls")]
[ScenarioCategory ("POC")]
- class LabelsAsButtons : Scenario {
+ class LabelsAsLabels : Scenario {
public override void Setup ()
{
// Add a label & text field so we can demo IsDefault
@@ -22,107 +22,120 @@ namespace UICatalog {
var edit = new TextField (31, 0, 15, "");
Win.Add (edit);
- // This is the default button (IsDefault = true); if user presses ENTER in the TextField
+ // This is the default Label (IsDefault = true); if user presses ENTER in the TextField
// the scenario will quit
- var defaultButton = new Label ("_Quit") {
+ var defaultLabel = new Label ("_Quit") {
X = Pos.Center (),
//TODO: Change to use Pos.AnchorEnd()
Y = Pos.Bottom (Win) - 3,
//IsDefault = true,
Clicked = () => Application.RequestStop (),
+ HotKeySpecifier = (System.Rune)'_',
+ CanFocus = true,
};
- Win.Add (defaultButton);
+ Win.Add (defaultLabel);
- var swapButton = new Label (50, 0, "Swap Default (Absolute Layout)");
- swapButton.Clicked = () => {
- //defaultButton.IsDefault = !defaultButton.IsDefault;
- //swapButton.IsDefault = !swapButton.IsDefault;
+ var swapLabel = new Label (50, 0, "S_wap Default (Absolute Layout)") {
+ HotKeySpecifier = (System.Rune)'_',
+ CanFocus = true,
};
- Win.Add (swapButton);
+ swapLabel.Clicked = () => {
+ //defaultLabel.IsDefault = !defaultLabel.IsDefault;
+ //swapLabel.IsDefault = !swapLabel.IsDefault;
+ };
+ Win.Add (swapLabel);
- static void DoMessage (Label button, ustring txt)
+ static void DoMessage (Label Label, ustring txt)
{
- button.Clicked = () => {
- var btnText = button.Text.ToString ();
+ Label.Clicked = () => {
+ var btnText = Label.Text.ToString ();
MessageBox.Query ("Message", $"Did you click {txt}?", "Yes", "No");
};
}
- var colorButtonsLabel = new Label ("Color Buttons:") {
+ var colorLabelsLabel = new Label ("Color Labels:") {
X = 0,
Y = Pos.Bottom (editLabel) + 1,
};
- Win.Add (colorButtonsLabel);
-
- //View prev = colorButtonsLabel;
+ Win.Add (colorLabelsLabel);
//With this method there is no need to call Top.Ready += () => Top.Redraw (Top.Bounds);
- var x = Pos.Right (colorButtonsLabel) + 2;
+ var x = Pos.Right (colorLabelsLabel) + 2;
foreach (var colorScheme in Colors.ColorSchemes) {
- var colorButton = new Label ($"{colorScheme.Key}") {
+ var colorLabel = new Label ($"{colorScheme.Key}") {
ColorScheme = colorScheme.Value,
- //X = Pos.Right (prev) + 2,
X = x,
- Y = Pos.Y (colorButtonsLabel),
+ Y = Pos.Y (colorLabelsLabel),
+ HotKeySpecifier = (System.Rune)'_',
+ CanFocus = true,
};
- DoMessage (colorButton, colorButton.Text);
- Win.Add (colorButton);
- //prev = colorButton;
- x += colorButton.Frame.Width + 2;
+ DoMessage (colorLabel, colorLabel.Text);
+ Win.Add (colorLabel);
+ x += colorLabel.Text.Length + 2;
}
- // BUGBUG: For some reason these buttons don't move to correct locations initially.
- // This was the only way I find to resolves this with the View prev variable.
- //Top.Ready += () => Top.Redraw (Top.Bounds);
+ Top.Ready += () => Top.Redraw (Top.Bounds);
- Label button;
- Win.Add (button = new Label ("A super long _Button that will probably expose a bug in clipping or wrapping of text. Will it?") {
+ Label Label;
+ Win.Add (Label = new Label ("A super long _Label that will probably expose a bug in clipping or wrapping of text. Will it?") {
X = 2,
- Y = Pos.Bottom (colorButtonsLabel) + 1,
+ Y = Pos.Bottom (colorLabelsLabel) + 1,
+ HotKeySpecifier = (System.Rune)'_',
+ CanFocus = true,
});
- DoMessage (button, button.Text);
+ DoMessage (Label, Label.Text);
// Note the 'N' in 'Newline' will be the hotkey
- Win.Add (button = new Label ("a Newline\nin the button") {
+ Win.Add (Label = new Label ("a Newline\nin the Label") {
X = 2,
- Y = Pos.Bottom (button) + 1,
- Clicked = () => MessageBox.Query ("Message", "Question?", "Yes", "No")
+ Y = Pos.Bottom (Label) + 1,
+ Clicked = () => MessageBox.Query ("Message", "Question?", "Yes", "No"),
+ HotKeySpecifier = (System.Rune)'_',
+ CanFocus = true,
});
var textChanger = new Label ("Te_xt Changer") {
X = 2,
- Y = Pos.Bottom (button) + 1,
+ Y = Pos.Bottom (Label) + 1,
+ HotKeySpecifier = (System.Rune)'_',
+ CanFocus = true,
};
Win.Add (textChanger);
textChanger.Clicked = () => textChanger.Text += "!";
- Win.Add (button = new Label ("Lets see if this will move as \"Text Changer\" grows") {
+ Win.Add (Label = new Label ("Lets see if this will move as \"Text Changer\" grows") {
X = Pos.Right (textChanger) + 2,
Y = Pos.Y (textChanger),
+ HotKeySpecifier = (System.Rune)'_',
+ CanFocus = true,
});
- var removeButton = new Label ("Remove this button") {
+ var removeLabel = new Label ("Remove this Label") {
X = 2,
- Y = Pos.Bottom (button) + 1,
- ColorScheme = Colors.Error
+ Y = Pos.Bottom (Label) + 1,
+ ColorScheme = Colors.Error,
+ HotKeySpecifier = (System.Rune)'_',
+ CanFocus = true,
};
- Win.Add (removeButton);
+ Win.Add (removeLabel);
// This in intresting test case because `moveBtn` and below are laid out relative to this one!
- removeButton.Clicked = () => Win.Remove (removeButton);
+ removeLabel.Clicked = () => Win.Remove (removeLabel);
var computedFrame = new FrameView ("Computed Layout") {
X = 0,
- Y = Pos.Bottom (removeButton) + 1,
+ Y = Pos.Bottom (removeLabel) + 1,
Width = Dim.Percent (50),
Height = 5
};
Win.Add (computedFrame);
// Demonstrates how changing the View.Frame property can move Views
- var moveBtn = new Label ("Move This \u263b Button _via Pos") {
+ var moveBtn = new Label ("Move This \u263b Label _via Pos") {
X = 0,
Y = Pos.Center () - 1,
Width = 30,
ColorScheme = Colors.Error,
+ HotKeySpecifier = (System.Rune)'_',
+ CanFocus = true,
};
moveBtn.Clicked = () => {
moveBtn.X = moveBtn.Frame.X + 5;
@@ -132,12 +145,14 @@ namespace UICatalog {
computedFrame.Add (moveBtn);
// Demonstrates how changing the View.Frame property can SIZE Views (#583)
- var sizeBtn = new Label ("Size This \u263a Button _via Pos") {
- //var sizeBtn = new Label ("Size This x Button _via Pos") {
+ var sizeBtn = new Label ("Size This \u263a Label _via Pos") {
+ //var sizeBtn = new Label ("Size This x Label _via Pos") {
X = 0,
Y = Pos.Center () + 1,
Width = 30,
ColorScheme = Colors.Error,
+ HotKeySpecifier = (System.Rune)'_',
+ CanFocus = true,
};
sizeBtn.Clicked = () => {
sizeBtn.Width = sizeBtn.Frame.Width + 5;
@@ -147,15 +162,17 @@ namespace UICatalog {
var absoluteFrame = new FrameView ("Absolute Layout") {
X = Pos.Right (computedFrame),
- Y = Pos.Bottom (removeButton) + 1,
+ Y = Pos.Bottom (removeLabel) + 1,
Width = Dim.Fill (),
Height = 5
};
Win.Add (absoluteFrame);
// Demonstrates how changing the View.Frame property can move Views
- var moveBtnA = new Label (0, 0, "Move This Button via Frame") {
+ var moveBtnA = new Label (0, 0, "Move This Label via Frame") {
ColorScheme = Colors.Error,
+ HotKeySpecifier = (System.Rune)'_',
+ CanFocus = true,
};
moveBtnA.Clicked = () => {
moveBtnA.Frame = new Rect (moveBtnA.Frame.X + 5, moveBtnA.Frame.Y, moveBtnA.Frame.Width, moveBtnA.Frame.Height);
@@ -165,15 +182,19 @@ namespace UICatalog {
// Demonstrates how changing the View.Frame property can SIZE Views (#583)
var sizeBtnA = new Label (0, 2, " ~ s gui.cs master ↑10 = Со_хранить") {
ColorScheme = Colors.Error,
+ HotKeySpecifier = (System.Rune)'_',
+ CanFocus = true,
};
sizeBtnA.Clicked = () => {
sizeBtnA.Frame = new Rect (sizeBtnA.Frame.X, sizeBtnA.Frame.Y, sizeBtnA.Frame.Width + 5, sizeBtnA.Frame.Height);
};
absoluteFrame.Add (sizeBtnA);
- var label = new Label ("Text Alignment (changes the four buttons above): ") {
+ var label = new Label ("Text Alignment (changes the four Labels above): ") {
X = 2,
Y = Pos.Bottom (computedFrame) + 1,
+ HotKeySpecifier = (System.Rune)'_',
+ CanFocus = true,
};
Win.Add (label);
@@ -188,31 +209,36 @@ namespace UICatalog {
ustring MoveHotkey (ustring txt)
{
// Remove the '_'
- var i = txt.IndexOf ('_');
+ var runes = txt.ToRuneList ();
+
+ var i = runes.IndexOf ('_');
ustring start = "";
- if (i > -1)
- start = txt [0, i];
- txt = start + txt [i + 1, txt.RuneCount];
+ if (i > -1) {
+ start = ustring.Make (runes.GetRange (0, i));
+ }
+ txt = start + ustring.Make (runes.GetRange (i + 1, runes.Count - (i + 1)));
+
+ runes = txt.ToRuneList ();
// Move over one or go to start
i++;
- if (i >= txt.RuneCount) {
+ if (i >= runes.Count) {
i = 0;
}
// Slip in the '_'
- start = txt [0, i];
- txt = start + ustring.Make ('_') + txt [i, txt.RuneCount];
-
- return txt;
+ start = ustring.Make (runes.GetRange (0, i));
+ return start + ustring.Make ('_') + ustring.Make (runes.GetRange (i, runes.Count - i));
}
- var mhkb = "Click to Change th_is Button's Hotkey";
+ var mhkb = "Click to Change th_is Label's Hotkey";
var moveHotKeyBtn = new Label (mhkb) {
X = 2,
Y = Pos.Bottom (radioGroup) + 1,
Width = mhkb.Length + 10,
ColorScheme = Colors.TopLevel,
+ HotKeySpecifier = (System.Rune)'_',
+ CanFocus = true,
};
moveHotKeyBtn.Clicked = () => {
moveHotKeyBtn.Text = MoveHotkey (moveHotKeyBtn.Text);
@@ -225,6 +251,8 @@ namespace UICatalog {
Y = Pos.Bottom (radioGroup) + 1,
Width = muhkb.Length + 30,
ColorScheme = Colors.TopLevel,
+ HotKeySpecifier = (System.Rune)'_',
+ CanFocus = true,
};
moveUnicodeHotKeyBtn.Clicked = () => {
moveUnicodeHotKeyBtn.Text = MoveHotkey (moveUnicodeHotKeyBtn.Text);
diff --git a/UICatalog/Scenarios/TextAlignments.cs b/UICatalog/Scenarios/TextAlignments.cs
index ca8a4459c..9b0c6406d 100644
--- a/UICatalog/Scenarios/TextAlignments.cs
+++ b/UICatalog/Scenarios/TextAlignments.cs
@@ -10,7 +10,7 @@ namespace UICatalog {
public override void Setup ()
{
Win.X = 10;
- Win.Width = Dim.Fill (20);
+ Win.Width = Dim.Fill (10);
string txt = "Hello world, how are you today? Pretty neat!";
string unicodeSampleText = "A Unicode sentence (пÑРвеÑ) has words.";
@@ -22,8 +22,8 @@ namespace UICatalog {
var multiLineHeight = 5;
foreach (var alignment in alignments) {
- singleLines[(int)alignment] = new Label (txt) { TextAlignment = alignment, Width = Dim.Fill (), Height = 1, ColorScheme = Colors.Dialog };
- multipleLines [(int)alignment] = new Label (txt) { TextAlignment = alignment, Width = Dim.Fill (), Height = multiLineHeight, ColorScheme = Colors.Dialog };
+ singleLines [(int)alignment] = new Label (txt) { TextAlignment = alignment, X = 1, Width = Dim.Fill (1), Height = 1, ColorScheme = Colors.Dialog };
+ multipleLines [(int)alignment] = new Label (txt) { TextAlignment = alignment, X = 1, Width = Dim.Fill (1), Height = multiLineHeight, ColorScheme = Colors.Dialog };
}
// Add a label & text field so we can demo IsDefault
@@ -35,7 +35,7 @@ namespace UICatalog {
var edit = new TextView () {
X = Pos.Right (editLabel) + 1,
Y = Pos.Y (editLabel),
- Width = Dim.Fill("Text:".Length + " Unicode Sample".Length + 2),
+ Width = Dim.Fill ("Text:".Length + " Unicode Sample".Length + 2),
Height = 4,
ColorScheme = Colors.TopLevel,
Text = txt,
@@ -57,7 +57,7 @@ namespace UICatalog {
};
Win.Add (unicodeSample);
- var update = new Button ("_Update", is_default: true) {
+ var update = new Button ("_Update") {
X = Pos.Right (edit) + 1,
Y = Pos.Bottom (edit) - 1,
Clicked = () => {
@@ -69,7 +69,14 @@ namespace UICatalog {
};
Win.Add (update);
- var label = new Label ($"Demonstrating single-line (should clip):") { Y = Pos.Bottom (edit) + 1 };
+ var enableHotKeyCheckBox = new CheckBox ("Enable Hotkey (_)", false) {
+ X = 0,
+ Y = Pos.Bottom (edit),
+ };
+
+ Win.Add (enableHotKeyCheckBox);
+
+ var label = new Label ($"Demonstrating single-line (should clip):") { Y = Pos.Bottom (enableHotKeyCheckBox) + 1 };
Win.Add (label);
foreach (var alignment in alignments) {
label = new Label ($"{alignment}:") { Y = Pos.Bottom (label) };
@@ -80,7 +87,7 @@ namespace UICatalog {
}
txt += "\nSecond line\n\nFourth Line.";
- label = new Label ($"Demonstrating multi-line and word wrap:") { Y = Pos.Bottom (label) + 1 };
+ label = new Label ($"Demonstrating multi-line and word wrap:") { Y = Pos.Bottom (label) };
Win.Add (label);
foreach (var alignment in alignments) {
label = new Label ($"{alignment}:") { Y = Pos.Bottom (label) };
@@ -89,6 +96,15 @@ namespace UICatalog {
Win.Add (multipleLines [(int)alignment]);
label = multipleLines [(int)alignment];
}
+
+ enableHotKeyCheckBox.Toggled += (previous) => {
+ foreach (var alignment in alignments) {
+ singleLines [(int)alignment].HotKeySpecifier = previous ? (Rune)0xffff : (Rune)'_';
+ multipleLines [(int)alignment].HotKeySpecifier = previous ? (Rune)0xffff : (Rune)'_';
+ }
+ Win.SetNeedsDisplay ();
+ Win.LayoutSubviews ();
+ };
}
}
}
\ No newline at end of file
diff --git a/UICatalog/Scenarios/TextFormatterDemo.cs b/UICatalog/Scenarios/TextFormatterDemo.cs
new file mode 100644
index 000000000..05f11f10e
--- /dev/null
+++ b/UICatalog/Scenarios/TextFormatterDemo.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Terminal.Gui;
+
+namespace UICatalog {
+ [ScenarioMetadata (Name: "TextFormatter Demo", Description: "Demos and tests the TextFormatter class.")]
+ [ScenarioCategory ("Text")]
+ [ScenarioCategory ("POC")]
+ class TextFormatterDemo : Scenario {
+ public override void Init (Toplevel top, ColorScheme colorScheme)
+ {
+ Application.Init ();
+
+ Top = top;
+ if (Top == null) {
+ Top = Application.Top;
+ }
+ Win = null;
+ }
+
+ public override void Setup ()
+ {
+ Top.Text = "Press CTRL-Q to Quit. This is the Text for the TopLevel View. TextAlignment.Centered was specified. It is intentionally very long to illustrate word wrap.\n" +
+ "<-- There is a new line here to show a hard line break. You should see this text bleed underneath the subviews, which start at Y = 3.";
+ Top.TextAlignment = TextAlignment.Centered;
+ Top.ColorScheme = Colors.Base;
+
+ string text = "Hello world, how are you today? Pretty neat!\nSecond line\n\nFourth Line.";
+ string unicode = "Τὴ γλῶσσα μοῦ ἔδωσαν ἑλληνικὴ\nτὸ σπίτι φτωχικὸ στὶς ἀμμουδιὲς τοῦ Ὁμήρου.\nΜονάχη ἔγνοια ἡ γλῶσσα μου στὶς ἀμμουδιὲς τοῦ Ὁμήρου.";
+
+ var unicodeCheckBox = new CheckBox ("Unicode", Top.HotKeySpecifier == (Rune)' ') {
+ X = 0,
+ Y = 3,
+ };
+
+ Top.Add (unicodeCheckBox);
+
+ var alignments = Enum.GetValues (typeof (Terminal.Gui.TextAlignment)).Cast ().ToList ();
+ var singleLines = new Label [alignments.Count];
+ var multipleLines = new Label [alignments.Count];
+
+ var multiLineHeight = 5;
+
+ foreach (var alignment in alignments) {
+ singleLines [(int)alignment] = new Label (text) { TextAlignment = alignment, X = 0, Width = Dim.Fill (), Height = 1, ColorScheme = Colors.Dialog };
+ multipleLines [(int)alignment] = new Label (text) { TextAlignment = alignment, X = 0, Width = Dim.Fill (), Height = multiLineHeight, ColorScheme = Colors.Dialog };
+ }
+
+ var label = new Label ($"Demonstrating single-line (should clip):") { Y = Pos.Bottom (unicodeCheckBox) + 1 };
+ Top.Add (label);
+ foreach (var alignment in alignments) {
+ label = new Label ($"{alignment}:") { Y = Pos.Bottom (label) };
+ Top.Add (label);
+ singleLines [(int)alignment].Y = Pos.Bottom (label);
+ Top.Add (singleLines [(int)alignment]);
+ label = singleLines [(int)alignment];
+ }
+
+ label = new Label ($"Demonstrating multi-line and word wrap:") { Y = Pos.Bottom (label) };
+ Top.Add (label);
+ foreach (var alignment in alignments) {
+ label = new Label ($"{alignment}:") { Y = Pos.Bottom (label) };
+ Top.Add (label);
+ multipleLines [(int)alignment].Y = Pos.Bottom (label);
+ Top.Add (multipleLines [(int)alignment]);
+ label = multipleLines [(int)alignment];
+ }
+
+ unicodeCheckBox.Toggled += (previous) => {
+ foreach (var alignment in alignments) {
+ singleLines [(int)alignment].Text = previous ? text : unicode;
+ multipleLines [(int)alignment].Text = previous ? text : unicode;
+ }
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/UICatalog/Scenarios/Unicode.cs b/UICatalog/Scenarios/Unicode.cs
index 694a6b4da..0f764f5bb 100644
--- a/UICatalog/Scenarios/Unicode.cs
+++ b/UICatalog/Scenarios/Unicode.cs
@@ -10,6 +10,9 @@ namespace UICatalog {
class UnicodeInMenu : Scenario {
public override void Setup ()
{
+ //string text = "Hello world, how are you today? Pretty neat!\nSecond line\n\nFourth Line.";
+ string unicode = "Τὴ γλῶσσα μοῦ ἔδωσαν ἑλληνικὴ\nτὸ σπίτι φτωχικὸ στὶς ἀμμουδιὲς τοῦ Ὁμήρου.\nΜονάχη ἔγνοια ἡ γλῶσσα μου στὶς ἀμμουδιὲς τοῦ Ὁμήρου.";
+
var menu = new MenuBar (new MenuBarItem [] {
new MenuBarItem ("_Файл", new MenuItem [] {
new MenuItem ("_Создать", "Creates new file", null),
@@ -25,23 +28,32 @@ namespace UICatalog {
});
Top.Add (menu);
- var label = new Label ("Button:") { X = 0, Y = 1 };
+ var label = new Label ("Label:") { X = 0, Y = 1 };
Win.Add (label);
- var button2 = new Button ("Со_хранить") { X = 15, Y = Pos.Y (label), Width = Dim.Percent (50), };
- Win.Add (button2);
+ var testlabel = new Label ("Стоял _он, дум великих полн") { X = 20, Y = Pos.Y (label), Width = Dim.Percent (50), };
+ Win.Add (testlabel);
+
+ label = new Label ("Label (CanFocus):") { X = Pos.X (label), Y = Pos.Bottom (label) + 1 };
+ Win.Add (label);
+ testlabel = new Label ("Стоял &он, дум великих полн") { X = 20, Y = Pos.Y (label), Width = Dim.Percent (50), CanFocus = true, HotKeySpecifier = new System.Rune('&') };
+ Win.Add (testlabel);
+
+ label = new Label ("Button:") { X = Pos.X (label), Y = Pos.Bottom (label) + 1 };
+ Win.Add (label);
+ var button = new Button ("A123456789♥♦♣♠JQK") { X = 20, Y = Pos.Y (label) };
+ Win.Add (button);
label = new Label ("CheckBox:") { X = Pos.X (label), Y = Pos.Bottom (label) + 1 };
Win.Add (label);
- var checkBox = new CheckBox (" ~ s gui.cs master ↑10") { X = 15, Y = Pos.Y (label), Width = Dim.Percent (50) };
+ var checkBox = new CheckBox (" ~ s gui.cs master ↑10") { X = 20, Y = Pos.Y (label), Width = Dim.Percent (50) };
Win.Add (checkBox);
label = new Label ("ComboBox:") { X = Pos.X (label), Y = Pos.Bottom (label) + 1 };
Win.Add (label);
var comboBox = new ComboBox () {
- X = 15,
+ X = 20,
Y = Pos.Y (label),
Width = Dim.Percent (50),
- ColorScheme = Colors.Error
};
comboBox.SetSource (new List () { "item #1", " ~ s gui.cs master ↑10", "Со_хранить" });
@@ -51,7 +63,7 @@ namespace UICatalog {
label = new Label ("HexView:") { X = Pos.X (label), Y = Pos.Bottom (label) + 2 };
Win.Add (label);
var hexView = new HexView (new System.IO.MemoryStream (Encoding.ASCII.GetBytes (" ~ s gui.cs master ↑10 Со_хранить"))) {
- X = 15,
+ X = 20,
Y = Pos.Y (label),
Width = Dim.Percent (60),
Height = 5
@@ -60,56 +72,39 @@ namespace UICatalog {
label = new Label ("ListView:") { X = Pos.X (label), Y = Pos.Bottom (hexView) + 1 };
Win.Add (label);
- var listView = new ListView (new List () { "item #1", " ~ s gui.cs master ↑10", "Со_хранить" }) {
- X = 15,
+ var listView = new ListView (new List () { "item #1", " ~ s gui.cs master ↑10", "Со_хранить", unicode }) {
+ X = 20,
Y = Pos.Y (label),
Width = Dim.Percent (60),
Height = 3,
- ColorScheme = Colors.Menu
};
Win.Add (listView);
label = new Label ("RadioGroup:") { X = Pos.X (label), Y = Pos.Bottom (listView) + 1 };
Win.Add (label);
var radioGroup = new RadioGroup (new ustring [] { "item #1", " ~ s gui.cs master ↑10", "Со_хранить" }, selected: 0) {
- X = 15,
+ X = 20,
Y = Pos.Y (label),
Width = Dim.Percent (60),
- ColorScheme = Colors.Menu
};
Win.Add (radioGroup);
label = new Label ("TextField:") { X = Pos.X (label), Y = Pos.Bottom (radioGroup) + 1 };
Win.Add (label);
- var textField = new TextField (" ~ s gui.cs master ↑10 = Со_хранить") { X = 15, Y = Pos.Y (label), Width = Dim.Percent (60) };
+ var textField = new TextField (" ~ s gui.cs master ↑10 = Со_хранить") { X = 20, Y = Pos.Y (label), Width = Dim.Percent (60) };
Win.Add (textField);
label = new Label ("TextView:") { X = Pos.X (label), Y = Pos.Bottom (textField) + 1 };
Win.Add (label);
var textView = new TextView () {
- X = 15,
+ X = 20,
Y = Pos.Y (label),
Width = Dim.Percent (60),
- Height = 3,
- ColorScheme = Colors.Menu,
- Text = " ~ s gui.cs master ↑10\nСо_хранить",
+ Height = 5,
+ Text = unicode,
};
Win.Add (textView);
- //label = new Label ("Charset:") {
- // X = Pos.Percent(75) + 1,
- // Y = 0,
- //};
- //Win.Add (label);
- //var charset = new Label ("") {
- // X = Pos.Percent(75) + 1,
- // Y = Pos.Y (label) + 1,
- // Width = Dim.Fill (1),
- // Height = Dim.Fill (),
- // ColorScheme = Colors.Dialog
- //};
- //Win.Add (charset);
-
// Move Win down to row 1, below menu
Win.Y = 1;
Top.LayoutSubviews ();
diff --git a/UICatalog/Scenarios/ViewWithText.cs b/UICatalog/Scenarios/ViewWithText.cs
deleted file mode 100644
index b9f78e904..000000000
--- a/UICatalog/Scenarios/ViewWithText.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Terminal.Gui;
-
-namespace UICatalog {
- [ScenarioMetadata (Name: "View Text", Description: "Demos and tests View's Text capabilities.")]
- [ScenarioCategory ("Text")]
- [ScenarioCategory ("POC")]
- class ViewWithText : Scenario {
- public override void Setup ()
- {
- Win.Text = "This is the Te_xt for the host Win object. TextAlignment.Centered was specified. It is intentionally very long to illustrate word wrap.\n" +
- "<-- There is a new line here to show a hard line break. You should see this text bleed underneath the subviews, which start at Y = 3.";
- Win.TextAlignment = TextAlignment.Centered;
-#if true
- string txt = "Hello world, how are you today? Pretty neat!";
-#else
- string txt = "Hello world, how are you today? Unicode: ~ gui.cs . Neat?";
-#endif
- var alignments = Enum.GetValues (typeof (Terminal.Gui.TextAlignment)).Cast ().ToList ();
- var label = new View ($"Demonstrating single-line (should clip!):") { Y = 3 };
- Win.Add (label);
- foreach (var alignment in alignments) {
- label = new Label ($"{alignment}:") { Y = Pos.Bottom (label) };
- Win.Add (label);
- label = new Label (txt) {
- TextAlignment = alignment,
- Y = Pos.Bottom (label),
- Width = Dim.Fill (),
- Height = 1,
- ColorScheme = Colors.Dialog,
- };
- Win.Add (label);
- }
-
- txt += "\nSecond line\n\nFourth Line.";
- label = new View ($"Demonstrating multi-line and word wrap:") { Y = Pos.Bottom (label) + 1 };
- Win.Add (label);
- foreach (var alignment in alignments) {
- label = new View ($"{alignment}:") { Y = Pos.Bottom (label) };
- Win.Add (label);
- label = new View (txt) { TextAlignment = alignment, Width = Dim.Fill (), Height = 6, ColorScheme = Colors.Dialog, Y = Pos.Bottom (label) };
- Win.Add (label);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/UnitTests/TextFormatterTests.cs b/UnitTests/TextFormatterTests.cs
index 073d8519a..9bb8b0c57 100644
--- a/UnitTests/TextFormatterTests.cs
+++ b/UnitTests/TextFormatterTests.cs
@@ -1613,6 +1613,10 @@ namespace Terminal.Gui {
Assert.Equal ("se", wrappedLines [1].ToString ());
Assert.Equal ("nte", wrappedLines [2].ToString ());
Assert.Equal ("nce", wrappedLines [3].ToString ());
+ Assert.Equal ("ha", wrappedLines [4].ToString ());
+ Assert.Equal ("s", wrappedLines [5].ToString ());
+ Assert.Equal ("wo", wrappedLines [6].ToString ());
+ Assert.Equal ("rds", wrappedLines [7].ToString ());
Assert.Equal (".", wrappedLines [^1].ToString ());
maxWidth = 2;