diff --git a/Terminal.Gui/Drawing/Aligner.cs b/Terminal.Gui/Drawing/Aligner.cs new file mode 100644 index 000000000..29bb80b9c --- /dev/null +++ b/Terminal.Gui/Drawing/Aligner.cs @@ -0,0 +1,482 @@ +using System.ComponentModel; +using Microsoft.CodeAnalysis; +using static Terminal.Gui.Pos; + +namespace Terminal.Gui; + +/// +/// Controls how the aligns items within a container. +/// +public enum Alignment +{ + /// + /// The items will be aligned to the left. + /// Set to to ensure at least one space between + /// each item. + /// + /// + /// + /// If the container is smaller than the total size of the items, the right items will be clipped (their locations + /// will be greater than the container size). + /// + /// + /// and have equivalent behavior. + /// + /// + /// + /// + /// 111 2222 33333 + /// + /// + Left = 0, + + /// + /// The items will be aligned to the top. + /// Set to to ensure at least one line between + /// each item. + /// + /// + /// + /// If the container is smaller than the total size of the items, the bottom items will be clipped (their locations + /// will be greater than the container size). + /// + /// + /// and have equivalent behavior. + /// + /// + Top, + + /// + /// The items will be aligned to the right. + /// Set to to ensure at least one space between + /// each item. + /// + /// + /// + /// If the container is smaller than the total size of the items, the left items will be clipped (their locations + /// will be negative). + /// + /// + /// and have equivalent behavior. + /// + /// + /// + /// + /// 111 2222 33333 + /// + /// + Right, + + /// + /// The items will be aligned to the bottom. + /// Set to to ensure at least one line between + /// each item. + /// + /// + /// + /// If the container is smaller than the total size of the items, the top items will be clipped (their locations + /// will be negative). + /// + /// + /// and have equivalent behavior. + /// + /// + Bottom, + + /// + /// The group will be centered in the container. + /// If centering is not possible, the group will be left-aligned. + /// Set to to ensure at least one space between + /// each item. + /// + /// + /// + /// Extra space will be distributed between the items, biased towards the left. + /// + /// + /// + /// + /// 111 2222 33333 + /// + /// + Centered, + + /// + /// The items will be justified. Space will be added between the items such that the first item + /// is at the start and the right side of the last item against the end. + /// Set to to ensure at least one space between + /// each item. + /// + /// + /// + /// Extra space will be distributed between the items, biased towards the left. + /// + /// + /// + /// + /// 111 2222 33333 + /// + /// + Justified, + + /// + /// The first item will be aligned to the left and the remaining will aligned to the right. + /// Set to to ensure at least one space between + /// each item. + /// + /// + /// + /// If the container is smaller than the total size of the items, the right items will be clipped (their locations + /// will be greater than the container size). + /// + /// + /// and have equivalent behavior. + /// + /// + /// + /// + /// 111 2222 33333 + /// + /// + FirstLeftRestRight, + + /// + /// The first item will be aligned to the top and the remaining will aligned to the bottom. + /// Set to to ensure at least one line between + /// each item. + /// + /// + /// + /// If the container is smaller than the total size of the items, the bottom items will be clipped (their locations + /// will be greater than the container size). + /// + /// + /// and have equivalent behavior. + /// + /// + FirstTopRestBottom, + + /// + /// The last item will be aligned to the right and the remaining will aligned to the left. + /// Set to to ensure at least one space between + /// each item. + /// + /// + /// + /// If the container is smaller than the total size of the items, the left items will be clipped (their locations + /// will be negative). + /// + /// + /// and have equivalent behavior. + /// + /// + /// + /// + /// 111 2222 33333 + /// + /// + LastRightRestLeft, + + /// + /// The last item will be aligned to the bottom and the remaining will aligned to the left. + /// Set to to ensure at least one line between + /// each item. + /// + /// + /// + /// If the container is smaller than the total size of the items, the top items will be clipped (their locations + /// will be negative). + /// + /// + /// and have equivalent behavior. + /// + /// + LastBottomRestTop +} + +/// +/// Aligns items within a container based on the specified . Both horizontal and vertical alignments are supported. +/// +public class Aligner : INotifyPropertyChanged +{ + private Alignment _alignment; + + /// + /// Gets or sets how the aligns items within a container. + /// + public Alignment Alignment + { + get => _alignment; + set + { + _alignment = value; + PropertyChanged?.Invoke (this, new (nameof (Alignment))); + } + } + + private int _containerSize; + + /// + /// The size of the container. + /// + public int ContainerSize + { + get => _containerSize; + set + { + _containerSize = value; + PropertyChanged?.Invoke (this, new (nameof (ContainerSize))); + } + } + + private bool _spaceBetweenItems; + + /// + /// Gets or sets whether adds at least one space between items. Default is + /// . + /// + /// + /// + /// If the total size of the items is greater than the container size, the space between items will be ignored + /// starting from the right or bottom. + /// + /// + public bool SpaceBetweenItems + { + get => _spaceBetweenItems; + set + { + _spaceBetweenItems = value; + PropertyChanged?.Invoke (this, new (nameof (SpaceBetweenItems))); + } + } + + /// + public event PropertyChangedEventHandler PropertyChanged; + + /// + /// Takes a list of item sizes and returns a list of the positions of those items when aligned within + /// using the and settings. + /// + /// The sizes of the items to align. + /// The locations of the items, from left/top to right/bottom. + public int [] Align (int [] sizes) { return Align (Alignment, SpaceBetweenItems, ContainerSize, sizes); } + + /// + /// Takes a list of item sizes and returns a list of the positions of those items when aligned within + /// using specified parameters. + /// + /// The sizes of the items to align. + /// Specifies how the items will be aligned. + /// + /// + /// Indicates whether at least one space should be added between items. + /// + /// + /// If the total size of the items is greater than the container size, the space between items will be ignored + /// starting from the right or bottom. + /// + /// + /// The size of the container. + /// The positions of the items, from left/top to right/bottom. + public static int [] Align (Alignment alignment, bool spaceBetweenItems, int containerSize, int [] sizes) + { + if (sizes.Length == 0) + { + return new int [] { }; + } + + int maxSpaceBetweenItems = spaceBetweenItems ? 1 : 0; + + var positions = new int [sizes.Length]; // positions of the items. the return value. + int totalItemsSize = sizes.Sum (); + int totalGaps = sizes.Length - 1; // total gaps between items + int totalItemsAndSpaces = totalItemsSize + totalGaps * maxSpaceBetweenItems; // total size of items and spaces if we had enough room + + int spaces = totalGaps * maxSpaceBetweenItems; // We'll decrement this below to place one space between each item until we run out + + if (totalItemsSize >= containerSize) + { + spaces = 0; + } + else if (totalItemsAndSpaces > containerSize) + { + spaces = containerSize - totalItemsSize; + } + + switch (alignment) + { + case Alignment.Left: + case Alignment.Top: + var currentPosition = 0; + + for (var i = 0; i < sizes.Length; i++) + { + CheckSizeCannotBeNegative (i, sizes); + + if (i == 0) + { + positions [0] = 0; // first item position + + continue; + } + + int spaceBefore = spaces-- > 0 ? maxSpaceBetweenItems : 0; + + // subsequent items are placed one space after the previous item + positions [i] = positions [i - 1] + sizes [i - 1] + spaceBefore; + } + + break; + + case Alignment.Right: + case Alignment.Bottom: + + currentPosition = containerSize - totalItemsSize - spaces; + + for (var i = 0; i < sizes.Length; i++) + { + CheckSizeCannotBeNegative (i, sizes); + int spaceBefore = spaces-- > 0 ? maxSpaceBetweenItems : 0; + + positions [i] = currentPosition; + currentPosition += sizes [i] + spaceBefore; + } + + break; + + case Alignment.Centered: + if (sizes.Length > 1) + { + // remaining space to be distributed before first and after the items + int remainingSpace = Math.Max (0, containerSize - totalItemsSize - spaces); + + for (var i = 0; i < sizes.Length; i++) + { + CheckSizeCannotBeNegative (i, sizes); + + if (i == 0) + { + positions [i] = remainingSpace / 2; // first item position + + continue; + } + + int spaceBefore = spaces-- > 0 ? maxSpaceBetweenItems : 0; + + // subsequent items are placed one space after the previous item + positions [i] = positions [i - 1] + sizes [i - 1] + spaceBefore; + } + } + else if (sizes.Length == 1) + { + CheckSizeCannotBeNegative (0, sizes); + positions [0] = (containerSize - sizes [0]) / 2; // single item is centered + } + + break; + + case Alignment.Justified: + int spaceBetween = sizes.Length > 1 ? (containerSize - totalItemsSize) / (sizes.Length - 1) : 0; + int remainder = sizes.Length > 1 ? (containerSize - totalItemsSize) % (sizes.Length - 1) : 0; + currentPosition = 0; + + for (var i = 0; i < sizes.Length; i++) + { + CheckSizeCannotBeNegative (i, sizes); + positions [i] = currentPosition; + int extraSpace = i < remainder ? 1 : 0; + currentPosition += sizes [i] + spaceBetween + extraSpace; + } + + break; + + // 111 2222 33333 + case Alignment.LastRightRestLeft: + case Alignment.LastBottomRestTop: + if (sizes.Length > 1) + { + if (totalItemsSize > containerSize) + { + currentPosition = containerSize - totalItemsSize - spaces; + } + else + { + currentPosition = 0; + } + + for (var i = 0; i < sizes.Length; i++) + { + CheckSizeCannotBeNegative (i, sizes); + + if (i < sizes.Length - 1) + { + int spaceBefore = spaces-- > 0 ? maxSpaceBetweenItems : 0; + + positions [i] = currentPosition; + currentPosition += sizes [i] + spaceBefore; + } + } + + positions [sizes.Length - 1] = containerSize - sizes [^1]; + } + else if (sizes.Length == 1) + { + CheckSizeCannotBeNegative (0, sizes); + + positions [0] = containerSize - sizes [0]; // single item is flush right + } + + break; + + // 111 2222 33333 + case Alignment.FirstLeftRestRight: + case Alignment.FirstTopRestBottom: + if (sizes.Length > 1) + { + currentPosition = 0; + positions [0] = currentPosition; // first item is flush left + + for (int i = sizes.Length - 1; i >= 0; i--) + { + CheckSizeCannotBeNegative (i, sizes); + + if (i == sizes.Length - 1) + { + // start at right + currentPosition = Math.Max (totalItemsSize, containerSize) - sizes [i]; + positions [i] = currentPosition; + } + + if (i < sizes.Length - 1 && i > 0) + { + int spaceBefore = spaces-- > 0 ? maxSpaceBetweenItems : 0; + + positions [i] = currentPosition - sizes [i] - spaceBefore; + currentPosition = positions [i]; + } + } + } + else if (sizes.Length == 1) + { + CheckSizeCannotBeNegative (0, sizes); + positions [0] = 0; // single item is flush left + } + + break; + + default: + throw new ArgumentOutOfRangeException (nameof (alignment), alignment, null); + } + + return positions; + } + + private static void CheckSizeCannotBeNegative (int i, int [] sizes) + { + if (sizes [i] < 0) + { + throw new ArgumentException ("The size of an item cannot be negative."); + } + } +} diff --git a/Terminal.Gui/Drawing/Justification.cs b/Terminal.Gui/Drawing/Justification.cs deleted file mode 100644 index f1fba56a8..000000000 --- a/Terminal.Gui/Drawing/Justification.cs +++ /dev/null @@ -1,333 +0,0 @@ -namespace Terminal.Gui; - -/// -/// Controls how the justifies items within a container. -/// -public enum Justification -{ - /// - /// The items will be aligned to the left. - /// Set to to ensure at least one space between - /// each item. - /// - /// - /// - /// 111 2222 33333 - /// - /// - Left, - - /// - /// The items will be aligned to the right. - /// Set to to ensure at least one space between - /// each item. - /// - /// - /// - /// 111 2222 33333 - /// - /// - Right, - - /// - /// The group will be centered in the container. - /// If centering is not possible, the group will be left-justified. - /// Set to to ensure at least one space between - /// each item. - /// - /// - /// - /// 111 2222 33333 - /// - /// - Centered, - - /// - /// The items will be justified. Space will be added between the items such that the first item - /// is at the start and the right side of the last item against the end. - /// Set to to ensure at least one space between - /// each item. - /// - /// - /// - /// 111 2222 33333 - /// - /// - Justified, - - /// - /// The first item will be aligned to the left and the remaining will aligned to the right. - /// Set to to ensure at least one space between - /// each item. - /// - /// - /// - /// 111 2222 33333 - /// - /// - FirstLeftRestRight, - - /// - /// The last item will be aligned to the right and the remaining will aligned to the left. - /// Set to to ensure at least one space between - /// each item. - /// - /// - /// - /// 111 2222 33333 - /// - /// - LastRightRestLeft -} - -/// -/// Justifies items within a container based on the specified . -/// -public class Justifier -{ - /// - /// Gets or sets how the justifies items within a container. - /// - public Justification Justification { get; set; } - - /// - /// The size of the container. - /// - public int ContainerSize { get; set; } - - /// - /// Gets or sets whether puts a space is placed between items. Default is . If , a space will be - /// placed between each item, which is useful for justifying text. - /// - public bool PutSpaceBetweenItems { get; set; } - - /// - /// Takes a list of items and returns their positions when justified within a container wide based on the specified - /// . - /// - /// The sizes of the items to justify. - /// The locations of the items, from left to right. - public int [] Justify (int [] sizes) - { - return Justify (Justification, PutSpaceBetweenItems, ContainerSize, sizes); - } - - /// - /// Takes a list of items and returns their positions when justified within a container wide based on the specified - /// . - /// - /// The sizes of the items to justify. - /// The justification style. - /// - /// The size of the container. - /// The locations of the items, from left to right. - public static int [] Justify (Justification justification, bool putSpaceBetweenItems, int containerSize, int [] sizes) - { - if (sizes.Length == 0) - { - return new int [] { }; - } - - int maxSpaceBetweenItems = putSpaceBetweenItems ? 1 : 0; - - var positions = new int [sizes.Length]; // positions of the items. the return value. - int totalItemsSize = sizes.Sum (); - int totalGaps = sizes.Length - 1; // total gaps between items - int totalItemsAndSpaces = totalItemsSize + totalGaps * maxSpaceBetweenItems; // total size of items and spaces if we had enough room - - int spaces = totalGaps * maxSpaceBetweenItems; // We'll decrement this below to place one space between each item until we run out - if (totalItemsSize >= containerSize) - { - spaces = 0; - } - else if (totalItemsAndSpaces > containerSize) - { - spaces = containerSize - totalItemsSize; - } - - switch (justification) - { - case Justification.Left: - var currentPosition = 0; - - for (var i = 0; i < sizes.Length; i++) - { - if (sizes [i] < 0) - { - throw new ArgumentException ("The size of an item cannot be negative."); - } - - if (i == 0) - { - positions [0] = 0; // first item position - - continue; - } - - int spaceBefore = spaces-- > 0 ? maxSpaceBetweenItems : 0; - - // subsequent items are placed one space after the previous item - positions [i] = positions [i - 1] + sizes [i - 1] + spaceBefore; - } - - break; - case Justification.Right: - currentPosition = Math.Max (0, containerSize - totalItemsSize - spaces); - - for (var i = 0; i < sizes.Length; i++) - { - if (sizes [i] < 0) - { - throw new ArgumentException ("The size of an item cannot be negative."); - } - - int spaceBefore = spaces-- > 0 ? maxSpaceBetweenItems : 0; - - positions [i] = currentPosition; - currentPosition += sizes [i] + spaceBefore; - } - - break; - - case Justification.Centered: - if (sizes.Length > 1) - { - // remaining space to be distributed before first and after the items - int remainingSpace = Math.Max (0, containerSize - totalItemsSize - spaces); - - for (var i = 0; i < sizes.Length; i++) - { - if (sizes [i] < 0) - { - throw new ArgumentException ("The size of an item cannot be negative."); - } - - if (i == 0) - { - positions [i] = remainingSpace / 2; // first item position - - continue; - } - - int spaceBefore = spaces-- > 0 ? maxSpaceBetweenItems : 0; - - // subsequent items are placed one space after the previous item - positions [i] = positions [i - 1] + sizes [i - 1] + spaceBefore; - } - } - else if (sizes.Length == 1) - { - if (sizes [0] < 0) - { - throw new ArgumentException ("The size of an item cannot be negative."); - } - - positions [0] = (containerSize - sizes [0]) / 2; // single item is centered - } - - break; - - case Justification.Justified: - int spaceBetween = sizes.Length > 1 ? (containerSize - totalItemsSize) / (sizes.Length - 1) : 0; - int remainder = sizes.Length > 1 ? (containerSize - totalItemsSize) % (sizes.Length - 1) : 0; - currentPosition = 0; - - for (var i = 0; i < sizes.Length; i++) - { - if (sizes [i] < 0) - { - throw new ArgumentException ("The size of an item cannot be negative."); - } - - positions [i] = currentPosition; - int extraSpace = i < remainder ? 1 : 0; - currentPosition += sizes [i] + spaceBetween + extraSpace; - } - - break; - - // 111 2222 33333 - case Justification.LastRightRestLeft: - if (sizes.Length > 1) - { - currentPosition = 0; - - for (var i = 0; i < sizes.Length; i++) - { - if (sizes [i] < 0) - { - throw new ArgumentException ("The size of an item cannot be negative."); - } - - if (i < sizes.Length - 1) - { - int spaceBefore = spaces-- > 0 ? maxSpaceBetweenItems : 0; - - positions [i] = currentPosition; - currentPosition += sizes [i] + spaceBefore; - } - } - - positions [sizes.Length - 1] = containerSize - sizes [sizes.Length - 1]; - } - else if (sizes.Length == 1) - { - if (sizes [0] < 0) - { - throw new ArgumentException ("The size of an item cannot be negative."); - } - - positions [0] = containerSize - sizes [0]; // single item is flush right - } - - break; - - // 111 2222 33333 - case Justification.FirstLeftRestRight: - if (sizes.Length > 1) - { - currentPosition = 0; - positions [0] = currentPosition; // first item is flush left - - for (int i = sizes.Length - 1; i >= 0; i--) - { - if (sizes [i] < 0) - { - throw new ArgumentException ("The size of an item cannot be negative."); - } - - if (i == sizes.Length - 1) - { - // start at right - currentPosition = containerSize - sizes [i]; - positions [i] = currentPosition; - } - - if (i < sizes.Length - 1 && i > 0) - { - int spaceBefore = spaces-- > 0 ? maxSpaceBetweenItems : 0; - - positions [i] = currentPosition - sizes [i] - spaceBefore; - currentPosition = positions [i]; - } - } - } - else if (sizes.Length == 1) - { - if (sizes [0] < 0) - { - throw new ArgumentException ("The size of an item cannot be negative."); - } - - positions [0] = 0; // single item is flush left - } - - break; - - default: - throw new ArgumentOutOfRangeException (nameof (justification), justification, null); - } - - return positions; - } -} diff --git a/Terminal.Gui/Drawing/Thickness.cs b/Terminal.Gui/Drawing/Thickness.cs index 6070e6cbe..1060e21e7 100644 --- a/Terminal.Gui/Drawing/Thickness.cs +++ b/Terminal.Gui/Drawing/Thickness.cs @@ -230,8 +230,8 @@ public class Thickness : IEquatable var tf = new TextFormatter { Text = label is null ? string.Empty : $"{label} {this}", - Alignment = TextAlignment.Centered, - VerticalAlignment = VerticalTextAlignment.Bottom, + Alignment = Alignment.Centered, + VerticalAlignment = Alignment.Bottom, AutoSize = true }; tf.Draw (rect, Application.Driver.CurrentAttribute, Application.Driver.CurrentAttribute, rect); diff --git a/Terminal.Gui/Resources/config.json b/Terminal.Gui/Resources/config.json index 368ccd8bf..c2a63fe15 100644 --- a/Terminal.Gui/Resources/config.json +++ b/Terminal.Gui/Resources/config.json @@ -24,7 +24,7 @@ "Themes": [ { "Default": { - "Dialog.DefaultButtonAlignment": "Center", + "Dialog.DefaultButtonAlignment": "Right", "FrameView.DefaultBorderStyle": "Single", "Window.DefaultBorderStyle": "Single", "ColorSchemes": [ diff --git a/Terminal.Gui/Text/TextAlignment.cs b/Terminal.Gui/Text/TextAlignment.cs deleted file mode 100644 index 44950cfd5..000000000 --- a/Terminal.Gui/Text/TextAlignment.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Terminal.Gui; - -/// Text alignment enumeration, controls how text is displayed. -public enum TextAlignment -{ - /// The text will be left-aligned. - Left, - - /// The text will be right-aligned. - Right, - - /// The text will be centered horizontally. - Centered, - - /// - /// The text will be justified (spaces will be added to existing spaces such that the text fills the container - /// horizontally). - /// - Justified -} \ No newline at end of file diff --git a/Terminal.Gui/Text/TextFormatter.cs b/Terminal.Gui/Text/TextFormatter.cs index 5a6cbec5e..4aa9d6977 100644 --- a/Terminal.Gui/Text/TextFormatter.cs +++ b/Terminal.Gui/Text/TextFormatter.cs @@ -17,14 +17,14 @@ public class TextFormatter private Size _size; private int _tabWidth = 4; private string _text; - private TextAlignment _textAlignment; + private Alignment _textAlignment = Alignment.Left; private TextDirection _textDirection; - private VerticalTextAlignment _textVerticalAlignment; + private Alignment _textVerticalAlignment = Alignment.Top; private bool _wordWrap = true; - /// Controls the horizontal text-alignment property. + /// Get or sets the horizontal text alignment. /// The text alignment. - public TextAlignment Alignment + public Alignment Alignment { get => _textAlignment; set => _textAlignment = EnableNeedsFormat (value); @@ -34,8 +34,7 @@ public class TextFormatter /// /// Used when is using to resize the view's to fit . /// - /// AutoSize is ignored if and - /// are used. + /// AutoSize is ignored if is used. /// /// public bool AutoSize @@ -70,9 +69,8 @@ public class TextFormatter /// Only the first HotKey specifier found in is supported. /// /// - /// If (the default) the width required for the HotKey specifier is returned. Otherwise the - /// height - /// is returned. + /// If (the default) the width required for the HotKey specifier is returned. Otherwise, the + /// height is returned. /// /// /// The number of characters required for the . If the text @@ -99,8 +97,8 @@ public class TextFormatter /// public int CursorPosition { get; internal set; } - /// Controls the text-direction property. - /// The text vertical alignment. + /// Gets or sets the text-direction. + /// The text direction. public TextDirection Direction { get => _textDirection; @@ -114,8 +112,7 @@ public class TextFormatter } } } - - + /// /// Determines if the viewport width will be used or only the text width will be used, /// If all the viewport area will be filled with whitespaces and the same background color @@ -227,9 +224,9 @@ public class TextFormatter } } - /// Controls the vertical text-alignment property. + /// Gets or sets the vertical text-alignment. /// The text vertical alignment. - public VerticalTextAlignment VerticalAlignment + public Alignment VerticalAlignment { get => _textVerticalAlignment; set => _textVerticalAlignment = EnableNeedsFormat (value); @@ -322,10 +319,10 @@ public class TextFormatter // When text is justified, we lost left or right, so we use the direction to align. - int x, y; + int x = 0, y = 0; // Horizontal Alignment - if (Alignment is TextAlignment.Right) + if (Alignment is Alignment.Right) { if (isVertical) { @@ -340,7 +337,7 @@ public class TextFormatter CursorPosition = screen.Width - runesWidth + (_hotKeyPos > -1 ? _hotKeyPos : 0); } } - else if (Alignment is TextAlignment.Left) + else if (Alignment is Alignment.Left) { if (isVertical) { @@ -356,7 +353,7 @@ public class TextFormatter CursorPosition = _hotKeyPos > -1 ? _hotKeyPos : 0; } - else if (Alignment is TextAlignment.Justified) + else if (Alignment is Alignment.Justified) { if (isVertical) { @@ -379,7 +376,7 @@ public class TextFormatter CursorPosition = _hotKeyPos > -1 ? _hotKeyPos : 0; } - else if (Alignment is TextAlignment.Centered) + else if (Alignment is Alignment.Centered) { if (isVertical) { @@ -399,11 +396,13 @@ public class TextFormatter } else { - throw new ArgumentOutOfRangeException ($"{nameof (Alignment)}"); + Debug.WriteLine ($"Unsupported Alignment: {nameof (VerticalAlignment)}"); + + return; } // Vertical Alignment - if (VerticalAlignment is VerticalTextAlignment.Bottom) + if (VerticalAlignment is Alignment.Bottom) { if (isVertical) { @@ -414,7 +413,7 @@ public class TextFormatter y = screen.Bottom - linesFormatted.Count + line; } } - else if (VerticalAlignment is VerticalTextAlignment.Top) + else if (VerticalAlignment is Alignment.Top) { if (isVertical) { @@ -425,7 +424,7 @@ public class TextFormatter y = screen.Top + line; } } - else if (VerticalAlignment is VerticalTextAlignment.Justified) + else if (VerticalAlignment is Alignment.Justified) { if (isVertical) { @@ -439,7 +438,7 @@ public class TextFormatter line < linesFormatted.Count - 1 ? screen.Height - interval <= 1 ? screen.Top + 1 : screen.Top + line * interval : screen.Bottom - 1; } } - else if (VerticalAlignment is VerticalTextAlignment.Middle) + else if (VerticalAlignment is Alignment.Centered) { if (isVertical) { @@ -454,7 +453,9 @@ public class TextFormatter } else { - throw new ArgumentOutOfRangeException ($"{nameof (VerticalAlignment)}"); + Debug.WriteLine ($"Unsupported Alignment: {nameof (VerticalAlignment)}"); + + return; } int colOffset = screen.X < 0 ? Math.Abs (screen.X) : 0; @@ -475,8 +476,8 @@ public class TextFormatter { if (idx < 0 || (isVertical - ? VerticalAlignment != VerticalTextAlignment.Bottom && current < 0 - : Alignment != TextAlignment.Right && x + current + colOffset < 0)) + ? VerticalAlignment != Alignment.Bottom && current < 0 + : Alignment != Alignment.Right && x + current + colOffset < 0)) { current++; @@ -565,7 +566,7 @@ public class TextFormatter if (HotKeyPos > -1 && idx == HotKeyPos) { - if ((isVertical && VerticalAlignment == VerticalTextAlignment.Justified) || (!isVertical && Alignment == TextAlignment.Justified)) + if ((isVertical && VerticalAlignment == Alignment.Justified) || (!isVertical && Alignment == Alignment.Justified)) { CursorPosition = idx - start; } @@ -703,7 +704,7 @@ public class TextFormatter _lines = Format ( text, Size.Height, - VerticalAlignment == VerticalTextAlignment.Justified, + VerticalAlignment == Alignment.Justified, Size.Width > colsWidth && WordWrap, PreserveTrailingSpaces, TabWidth, @@ -727,7 +728,7 @@ public class TextFormatter _lines = Format ( text, Size.Width, - Alignment == TextAlignment.Justified, + Alignment == Alignment.Justified, Size.Height > 1 && WordWrap, PreserveTrailingSpaces, TabWidth, @@ -981,7 +982,7 @@ public class TextFormatter // if value is not wide enough if (text.EnumerateRunes ().Sum (c => c.GetColumns ()) < width) { - // pad it out with spaces to the given alignment + // pad it out with spaces to the given Alignment int toPad = width - text.EnumerateRunes ().Sum (c => c.GetColumns ()); return text + new string (' ', toPad); @@ -1003,7 +1004,7 @@ public class TextFormatter /// instance to access any of his objects. /// A list of word wrapped lines. /// - /// This method does not do any justification. + /// This method does not do any alignment. /// This method strips Newline ('\n' and '\r\n') sequences before processing. /// /// If is at most one space will be preserved @@ -1035,7 +1036,7 @@ public class TextFormatter List runes = StripCRLF (text).ToRuneList (); int start = Math.Max ( - !runes.Contains ((Rune)' ') && textFormatter is { VerticalAlignment: VerticalTextAlignment.Bottom } && IsVerticalDirection (textDirection) + !runes.Contains ((Rune)' ') && textFormatter is { VerticalAlignment: Alignment.Bottom } && IsVerticalDirection (textDirection) ? runes.Count - width : 0, 0); @@ -1253,7 +1254,7 @@ public class TextFormatter /// The number of columns to clip the text to. Text longer than will be /// clipped. /// - /// Alignment. + /// Alignment. /// The text direction. /// The number of columns used for a tab. /// instance to access any of his objects. @@ -1261,13 +1262,13 @@ public class TextFormatter public static string ClipAndJustify ( string text, int width, - TextAlignment talign, + Alignment textAlignment, TextDirection textDirection = TextDirection.LeftRight_TopBottom, int tabWidth = 0, TextFormatter textFormatter = null ) { - return ClipAndJustify (text, width, talign == TextAlignment.Justified, textDirection, tabWidth, textFormatter); + return ClipAndJustify (text, width, textAlignment == Alignment.Justified, textDirection, tabWidth, textFormatter); } /// Justifies text within a specified width. @@ -1308,12 +1309,12 @@ public class TextFormatter { if (IsHorizontalDirection (textDirection)) { - if (textFormatter is { Alignment: TextAlignment.Right }) + if (textFormatter is { Alignment: Alignment.Right }) { return GetRangeThatFits (runes, runes.Count - width, text, width, tabWidth, textDirection); } - if (textFormatter is { Alignment: TextAlignment.Centered }) + if (textFormatter is { Alignment: Alignment.Centered }) { return GetRangeThatFits (runes, Math.Max ((runes.Count - width) / 2, 0), text, width, tabWidth, textDirection); } @@ -1323,12 +1324,12 @@ public class TextFormatter if (IsVerticalDirection (textDirection)) { - if (textFormatter is { VerticalAlignment: VerticalTextAlignment.Bottom }) + if (textFormatter is { VerticalAlignment: Alignment.Bottom }) { return GetRangeThatFits (runes, runes.Count - width, text, width, tabWidth, textDirection); } - if (textFormatter is { VerticalAlignment: VerticalTextAlignment.Middle }) + if (textFormatter is { VerticalAlignment: Alignment.Centered }) { return GetRangeThatFits (runes, Math.Max ((runes.Count - width) / 2, 0), text, width, tabWidth, textDirection); } @@ -1346,14 +1347,14 @@ public class TextFormatter if (IsHorizontalDirection (textDirection)) { - if (textFormatter is { Alignment: TextAlignment.Right }) + if (textFormatter is { Alignment: Alignment.Right }) { if (GetRuneWidth (text, tabWidth, textDirection) > width) { return GetRangeThatFits (runes, runes.Count - width, text, width, tabWidth, textDirection); } } - else if (textFormatter is { Alignment: TextAlignment.Centered }) + else if (textFormatter is { Alignment: Alignment.Centered }) { return GetRangeThatFits (runes, Math.Max ((runes.Count - width) / 2, 0), text, width, tabWidth, textDirection); } @@ -1365,14 +1366,14 @@ public class TextFormatter if (IsVerticalDirection (textDirection)) { - if (textFormatter is { VerticalAlignment: VerticalTextAlignment.Bottom }) + if (textFormatter is { VerticalAlignment: Alignment.Bottom }) { if (runes.Count - zeroLength > width) { return GetRangeThatFits (runes, runes.Count - width, text, width, tabWidth, textDirection); } } - else if (textFormatter is { VerticalAlignment: VerticalTextAlignment.Middle }) + else if (textFormatter is { VerticalAlignment: Alignment.Centered }) { return GetRangeThatFits (runes, Math.Max ((runes.Count - width) / 2, 0), text, width, tabWidth, textDirection); } @@ -1479,7 +1480,7 @@ public class TextFormatter /// Formats text into lines, applying text alignment and optionally wrapping text to new lines on word boundaries. /// /// The number of columns to constrain the text to for word wrapping and clipping. - /// Specifies how the text will be aligned horizontally. + /// Specifies how the text will be aligned horizontally. /// /// If , the text will be wrapped to new lines no longer than /// . If , forces text to fit a single line. Line breaks are converted @@ -1502,7 +1503,7 @@ public class TextFormatter public static List Format ( string text, int width, - TextAlignment talign, + Alignment textAlignment, bool wordWrap, bool preserveTrailingSpaces = false, int tabWidth = 0, @@ -1514,7 +1515,7 @@ public class TextFormatter return Format ( text, width, - talign == TextAlignment.Justified, + textAlignment == Alignment.Justified, wordWrap, preserveTrailingSpaces, tabWidth, @@ -1888,7 +1889,7 @@ public class TextFormatter return lineIdx; } - /// Calculates the rectangle required to hold text, assuming no word wrapping or justification. + /// Calculates the rectangle required to hold text, assuming no word wrapping or alignment. /// /// This API will return incorrect results if the text includes glyphs who's width is dependent on surrounding /// glyphs (e.g. Arabic). diff --git a/Terminal.Gui/Text/VerticalTextAlignment.cs b/Terminal.Gui/Text/VerticalTextAlignment.cs deleted file mode 100644 index ef7788577..000000000 --- a/Terminal.Gui/Text/VerticalTextAlignment.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Terminal.Gui; - -/// Vertical text alignment enumeration, controls how text is displayed. -public enum VerticalTextAlignment -{ - /// The text will be top-aligned. - Top, - - /// The text will be bottom-aligned. - Bottom, - - /// The text will centered vertically. - Middle, - - /// - /// The text will be justified (spaces will be added to existing spaces such that the text fills the container - /// vertically). - /// - Justified -} \ No newline at end of file diff --git a/Terminal.Gui/View/Layout/Pos.cs b/Terminal.Gui/View/Layout/Pos.cs index e27fabb3c..affbea7aa 100644 --- a/Terminal.Gui/View/Layout/Pos.cs +++ b/Terminal.Gui/View/Layout/Pos.cs @@ -1,4 +1,6 @@ #nullable enable +using System.ComponentModel; + namespace Terminal.Gui; /// @@ -26,6 +28,14 @@ namespace Terminal.Gui; /// /// /// +/// +/// +/// +/// Creates a object that aligns a set of views. +/// +/// +/// +/// /// /// /// @@ -125,12 +135,17 @@ namespace Terminal.Gui; /// public abstract class Pos { - #region static Pos creation methods - /// Creates a object that is an absolute position based on the specified integer value. - /// The Absolute . - /// The value to convert to the . - public static Pos Absolute (int position) { return new PosAbsolute (position); } + /// + /// Creates a object that aligns a set of views according to the specified alignment setting. + /// + /// + /// + /// The optional, unique identifier for the set of views to align according to + /// . + /// + /// + public static Pos Align (Alignment alignment, int groupId = 0) { return new PosAlign (alignment, groupId); } /// /// Creates a object that is anchored to the end (right side or @@ -303,20 +318,85 @@ public abstract class Pos /// /// internal virtual bool ReferencesOtherViews () { return false; } +} - #endregion virtual methods +/// +/// Represents an absolute position in the layout. This is used to specify a fixed position in the layout. +/// +/// +/// +/// This is a low-level API that is typically used internally by the layout system. Use the various static +/// methods on the class to create objects instead. +/// +/// +/// +public class PosAbsolute (int position) : Pos +{ + /// + /// The position of the in the layout. + /// + public int Position { get; } = position; - #region operators + /// + public override bool Equals (object other) { return other is PosAbsolute abs && abs.Position == Position; } - /// Adds a to a , yielding a new . - /// The first to add. - /// The second to add. - /// The that is the sum of the values of left and right. - public static Pos operator + (Pos left, Pos right) + /// + public override int GetHashCode () { return Position.GetHashCode (); } + + /// + public override string ToString () { return $"Absolute({Position})"; } + + internal override int Anchor (int width) { return Position; } +} + +/// +/// Represents a position anchored to the end (right side or bottom). +/// +/// +/// +/// This is a low-level API that is typically used internally by the layout system. Use the various static +/// methods on the class to create objects instead. +/// +/// +public class PosAnchorEnd : Pos +{ + /// + /// Gets the offset of the position from the right/bottom. + /// + public int Offset { get; } + + /// + /// Constructs a new position anchored to the end (right side or bottom) of the SuperView, + /// minus the respective dimension of the View. This is equivalent to using , + /// with an offset equivalent to the View's respective dimension. + /// + public PosAnchorEnd () { UseDimForOffset = true; } + + /// + /// Constructs a new position anchored to the end (right side or bottom) of the SuperView, + /// + /// + public PosAnchorEnd (int offset) { Offset = offset; } + + /// + public override bool Equals (object other) { return other is PosAnchorEnd anchorEnd && anchorEnd.Offset == Offset; } + + /// + public override int GetHashCode () { return Offset.GetHashCode (); } + + /// + /// If true, the offset is the width of the view, if false, the offset is the offset value. + /// + public bool UseDimForOffset { get; } + + /// + public override string ToString () { return UseDimForOffset ? "AnchorEnd()" : $"AnchorEnd({Offset})"; } + + internal override int Anchor (int width) { - if (left is PosAbsolute && right is PosAbsolute) + if (UseDimForOffset) { - return new PosAbsolute (left.GetAnchor (0) + right.GetAnchor (0)); + return width; } var newPos = new PosCombine (AddOrSubtract.Add, left, right); diff --git a/Terminal.Gui/View/ViewText.cs b/Terminal.Gui/View/ViewText.cs index db526f67c..9a035362f 100644 --- a/Terminal.Gui/View/ViewText.cs +++ b/Terminal.Gui/View/ViewText.cs @@ -87,7 +87,7 @@ public partial class View /// or are using , the will be adjusted to fit the text. /// /// The text alignment. - public virtual TextAlignment TextAlignment + public virtual Alignment TextAlignment { get => TextFormatter.Alignment; set @@ -105,7 +105,7 @@ public partial class View /// /// or are using , the will be adjusted to fit the text. /// - /// The text alignment. + /// The text direction. public virtual TextDirection TextDirection { get => TextFormatter.Direction; @@ -129,8 +129,8 @@ public partial class View /// /// or are using , the will be adjusted to fit the text. /// - /// The text alignment. - public virtual VerticalTextAlignment VerticalTextAlignment + /// The vertical text alignment. + public virtual Alignment VerticalTextAlignment { get => TextFormatter.VerticalAlignment; set diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index 52c72ab5f..dc0afde5e 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -37,8 +37,8 @@ public class Button : View /// The width of the is computed based on the text length. The height will always be 1. public Button () { - TextAlignment = TextAlignment.Centered; - VerticalTextAlignment = VerticalTextAlignment.Middle; + TextAlignment = Alignment.Centered; + VerticalTextAlignment = Alignment.Centered; _leftBracket = Glyphs.LeftBracket; _rightBracket = Glyphs.RightBracket; diff --git a/Terminal.Gui/Views/CheckBox.cs b/Terminal.Gui/Views/CheckBox.cs index 5971c02ed..ab78b470d 100644 --- a/Terminal.Gui/Views/CheckBox.cs +++ b/Terminal.Gui/Views/CheckBox.cs @@ -155,13 +155,13 @@ public class CheckBox : View { switch (TextAlignment) { - case TextAlignment.Left: - case TextAlignment.Centered: - case TextAlignment.Justified: + case Alignment.Left: + case Alignment.Centered: + case Alignment.Justified: TextFormatter.Text = $"{GetCheckedState ()} {Text}"; break; - case TextAlignment.Right: + case Alignment.Right: TextFormatter.Text = $"{Text} {GetCheckedState ()}"; break; diff --git a/Terminal.Gui/Views/DatePicker.cs b/Terminal.Gui/Views/DatePicker.cs index 613051fbe..d0576f23d 100644 --- a/Terminal.Gui/Views/DatePicker.cs +++ b/Terminal.Gui/Views/DatePicker.cs @@ -215,7 +215,6 @@ public class DatePicker : View { X = Pos.Center () - 2, Y = Pos.Bottom (_calendar) - 1, - Height = 1, Width = 2, Text = GetBackButtonText (), WantContinuousButtonPressed = true, @@ -234,7 +233,6 @@ public class DatePicker : View { X = Pos.Right (_previousMonthButton) + 2, Y = Pos.Bottom (_calendar) - 1, - Height = 1, Width = 2, Text = GetForwardButtonText (), WantContinuousButtonPressed = true, diff --git a/Terminal.Gui/Views/Dialog.cs b/Terminal.Gui/Views/Dialog.cs index 96cc41f1b..fa84cbbdd 100644 --- a/Terminal.Gui/Views/Dialog.cs +++ b/Terminal.Gui/Views/Dialog.cs @@ -15,21 +15,6 @@ namespace Terminal.Gui; /// public class Dialog : Window { - /// Determines the horizontal alignment of the Dialog buttons. - public enum ButtonAlignments - { - /// Center-aligns the buttons (the default). - Center = 0, - - /// Justifies the buttons - Justify, - - /// Left-aligns the buttons - Left, - - /// Right-aligns the buttons - Right - } // TODO: Reenable once border/borderframe design is settled /// @@ -61,19 +46,22 @@ public class Dialog : Window Y = Pos.Center (); //ValidatePosDim = true; - Width = Dim.Percent (85); + Width = Dim.Percent (85); Height = Dim.Percent (85); ColorScheme = Colors.ColorSchemes ["Dialog"]; Modal = true; ButtonAlignment = DefaultButtonAlignment; - AddCommand (Command.QuitToplevel, () => - { - Canceled = true; - RequestStop (); - return true; - }); + AddCommand ( + Command.QuitToplevel, + () => + { + Canceled = true; + RequestStop (); + + return true; + }); KeyBindings.Add (Key.Esc, Command.QuitToplevel); Initialized += Dialog_Initialized; ; @@ -109,12 +97,14 @@ public class Dialog : Window } #endif _canceled = value; + return; } } + // TODO: Update button.X = Pos.Justify when alignment changes /// Determines how the s are aligned along the bottom of the dialog. - public ButtonAlignments ButtonAlignment { get; set; } + public Alignment ButtonAlignment { get; set; } /// Optional buttons to lay out at the bottom of the dialog. public Button [] Buttons @@ -134,11 +124,11 @@ public class Dialog : Window } } - /// The default for . + /// The default for . /// This property can be set in a Theme. [SerializableConfigurationProperty (Scope = typeof (ThemeScope))] [JsonConverter (typeof (JsonStringEnumConverter))] - public static ButtonAlignments DefaultButtonAlignment { get; set; } = ButtonAlignments.Center; + public static Alignment DefaultButtonAlignment { get; set; } = Alignment.Right; /// /// Adds a to the , its layout will be controlled by the @@ -152,6 +142,9 @@ public class Dialog : Window return; } + button.X = Pos.Align (ButtonAlignment); + button.Y = Pos.AnchorEnd (); + _buttons.Add (button); Add (button); @@ -191,109 +184,4 @@ public class Dialog : Window return widths.Sum (); } - - private void LayoutButtons () - { - if (_buttons.Count == 0 || !IsInitialized) - { - return; - } - - var shiftLeft = 0; - - int buttonsWidth = GetButtonsWidth (); - - switch (ButtonAlignment) - { - case ButtonAlignments.Center: - // Center Buttons - shiftLeft = (Viewport.Width - buttonsWidth - _buttons.Count - 1) / 2 + 1; - - for (int i = _buttons.Count - 1; i >= 0; i--) - { - Button button = _buttons [i]; - shiftLeft += button.Frame.Width + (i == _buttons.Count - 1 ? 0 : 1); - - if (shiftLeft > -1) - { - button.X = Pos.AnchorEnd (shiftLeft); - } - else - { - button.X = Viewport.Width - shiftLeft; - } - - button.Y = Pos.AnchorEnd (); - } - - break; - - case ButtonAlignments.Justify: - // Justify Buttons - // leftmost and rightmost buttons are hard against edges. The rest are evenly spaced. - - var spacing = (int)Math.Ceiling ((double)(Viewport.Width - buttonsWidth) / (_buttons.Count - 1)); - - for (int i = _buttons.Count - 1; i >= 0; i--) - { - Button button = _buttons [i]; - - if (i == _buttons.Count - 1) - { - shiftLeft += button.Frame.Width; - button.X = Pos.AnchorEnd (shiftLeft); - } - else - { - if (i == 0) - { - // first (leftmost) button - int left = Viewport.Width; - button.X = Pos.AnchorEnd (left); - } - else - { - shiftLeft += button.Frame.Width + spacing; - button.X = Pos.AnchorEnd (shiftLeft); - } - } - - button.Y = Pos.AnchorEnd (); - } - - break; - - case ButtonAlignments.Left: - // Left Align Buttons - Button prevButton = _buttons [0]; - prevButton.X = 0; - prevButton.Y = Pos.AnchorEnd (1); - - for (var i = 1; i < _buttons.Count; i++) - { - Button button = _buttons [i]; - button.X = Pos.Right (prevButton) + 1; - button.Y = Pos.AnchorEnd (1); - prevButton = button; - } - - break; - - case ButtonAlignments.Right: - // Right align buttons - shiftLeft = _buttons [_buttons.Count - 1].Frame.Width; - _buttons [_buttons.Count - 1].X = Pos.AnchorEnd (shiftLeft); - _buttons [_buttons.Count - 1].Y = Pos.AnchorEnd (1); - - for (int i = _buttons.Count - 2; i >= 0; i--) - { - Button button = _buttons [i]; - shiftLeft += button.Frame.Width + 1; - button.X = Pos.AnchorEnd (shiftLeft); - button.Y = Pos.AnchorEnd (); - } - - break; - } - } } diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index 725fe6bd8..0a028d5e4 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -1002,7 +1002,7 @@ public class ListWrapper : IListDataSource private void RenderUstr (ConsoleDriver driver, string ustr, int col, int line, int width, int start = 0) { string str = start > ustr.GetColumns () ? string.Empty : ustr.Substring (Math.Min (start, ustr.ToRunes ().Length - 1)); - string u = TextFormatter.ClipAndJustify (str, width, TextAlignment.Left); + string u = TextFormatter.ClipAndJustify (str, width, Alignment.Left); driver.AddStr (u); width -= u.GetColumns (); diff --git a/Terminal.Gui/Views/Menu/Menu.cs b/Terminal.Gui/Views/Menu/Menu.cs index 11bc71c7e..1673862a0 100644 --- a/Terminal.Gui/Views/Menu/Menu.cs +++ b/Terminal.Gui/Views/Menu/Menu.cs @@ -890,7 +890,7 @@ internal sealed class Menu : View var tf = new TextFormatter { AutoSize = true, - Alignment = TextAlignment.Centered, HotKeySpecifier = MenuBar.HotKeySpecifier, Text = textToDraw + Alignment = Alignment.Centered, HotKeySpecifier = MenuBar.HotKeySpecifier, Text = textToDraw }; // The -3 is left/right border + one space (not sure what for) diff --git a/Terminal.Gui/Views/MessageBox.cs b/Terminal.Gui/Views/MessageBox.cs index 74826cc41..a1f2c86de 100644 --- a/Terminal.Gui/Views/MessageBox.cs +++ b/Terminal.Gui/Views/MessageBox.cs @@ -325,7 +325,10 @@ public static class MessageBox foreach (string s in buttons) { - var b = new Button { Text = s }; + var b = new Button + { + Text = s, + }; if (count == defaultButton) { @@ -337,9 +340,9 @@ public static class MessageBox } } - Dialog d; - - d = new Dialog + Alignment buttonJust = Dialog.DefaultButtonAlignment; + Dialog.DefaultButtonAlignment = Alignment.Centered; + var d = new Dialog { Buttons = buttonList.ToArray (), Title = title, @@ -347,6 +350,7 @@ public static class MessageBox Width = Dim.Auto (DimAutoStyle.Content, minimumContentDim: Dim.Percent(60)), Height = Dim.Auto (DimAutoStyle.Content), }; + Dialog.DefaultButtonAlignment = buttonJust; if (width != 0) { @@ -370,7 +374,7 @@ public static class MessageBox var messageLabel = new Label { Text = message, - TextAlignment = TextAlignment.Centered, + TextAlignment = Alignment.Centered, X = Pos.Center (), Y = 0, // ColorScheme = Colors.ColorSchemes ["Error"] diff --git a/Terminal.Gui/Views/ProgressBar.cs b/Terminal.Gui/Views/ProgressBar.cs index f599b299f..7b73d62c0 100644 --- a/Terminal.Gui/Views/ProgressBar.cs +++ b/Terminal.Gui/Views/ProgressBar.cs @@ -175,7 +175,7 @@ public class ProgressBar : View if (ProgressBarFormat != ProgressBarFormat.Simple && !_isActivity) { - var tf = new TextFormatter { Alignment = TextAlignment.Centered, Text = Text, AutoSize = true }; + var tf = new TextFormatter { Alignment = Alignment.Centered, Text = Text, AutoSize = true }; var attr = new Attribute (ColorScheme.HotNormal.Foreground, ColorScheme.HotNormal.Background); if (_fraction > .5) diff --git a/Terminal.Gui/Views/Slider.cs b/Terminal.Gui/Views/Slider.cs index 992bc72fa..39361dc25 100644 --- a/Terminal.Gui/Views/Slider.cs +++ b/Terminal.Gui/Views/Slider.cs @@ -1002,7 +1002,7 @@ public class Slider : View } } - private string AlignText (string text, int width, TextAlignment textAlignment) + private string AlignText (string text, int width, Alignment alignment) { if (text is null) { @@ -1019,20 +1019,20 @@ public class Slider : View string s2 = new (' ', w % 2); // Note: The formatter doesn't handle all of this ??? - switch (textAlignment) + switch (alignment) { - case TextAlignment.Justified: + case Alignment.Justified: return TextFormatter.Justify (text, width); - case TextAlignment.Left: + case Alignment.Left: return text + s1 + s1 + s2; - case TextAlignment.Centered: + case Alignment.Centered: if (text.Length % 2 != 0) { return s1 + text + s1 + s2; } return s1 + s2 + text + s1; - case TextAlignment.Right: + case Alignment.Right: return s1 + s1 + s2 + text; default: return text; diff --git a/Terminal.Gui/Views/TableView/ColumnStyle.cs b/Terminal.Gui/Views/TableView/ColumnStyle.cs index cbbc2a3ac..2d277abd9 100644 --- a/Terminal.Gui/Views/TableView/ColumnStyle.cs +++ b/Terminal.Gui/Views/TableView/ColumnStyle.cs @@ -8,10 +8,10 @@ public class ColumnStyle { /// - /// Defines a delegate for returning custom alignment per cell based on cell values. When specified this will + /// Defines a delegate for returning custom alignment per cell based on cell values. When specified this will /// override /// - public Func AlignmentGetter; + public Func AlignmentGetter; /// /// Defines a delegate for returning a custom color scheme per cell based on cell values. Return null for the @@ -20,26 +20,26 @@ public class ColumnStyle public CellColorGetterDelegate ColorGetter; /// - /// Defines a delegate for returning custom representations of cell values. If not set then - /// is used. Return values from your delegate may be truncated e.g. based on + /// Defines a delegate for returning custom representations of cell values. If not set then + /// is used. Return values from your delegate may be truncated e.g. based on /// /// public Func RepresentationGetter; - private bool visible = true; + private bool _visible = true; /// - /// Defines the default alignment for all values rendered in this column. For custom alignment based on cell + /// Defines the default alignment for all values rendered in this column. For custom alignment based on cell /// contents use . /// - public TextAlignment Alignment { get; set; } + public Alignment Alignment { get; set; } /// Defines the format for values e.g. "yyyy-MM-dd" for dates public string Format { get; set; } /// - /// Set the maximum width of the column in characters. This value will be ignored if more than the tables - /// . Defaults to + /// Set the maximum width of the column in characters. This value will be ignored if more than the tables + /// . Defaults to /// public int MaxWidth { get; set; } = TableView.DefaultMaxCellWidth; @@ -47,7 +47,7 @@ public class ColumnStyle public int MinAcceptableWidth { get; set; } = TableView.DefaultMinAcceptableWidth; /// - /// Set the minimum width of the column in characters. Setting this will ensure that even when a column has short + /// Set the minimum width of the column in characters. Setting this will ensure that even when a column has short /// content/header it still fills a given width of the control. /// /// This value will be ignored if more than the tables or the @@ -64,8 +64,8 @@ public class ColumnStyle /// If is 0 then will always return false. public bool Visible { - get => MaxWidth >= 0 && visible; - set => visible = value; + get => MaxWidth >= 0 && _visible; + set => _visible = value; } /// @@ -74,7 +74,7 @@ public class ColumnStyle /// /// /// - public TextAlignment GetAlignment (object cellValue) + public Alignment GetAlignment (object cellValue) { if (AlignmentGetter is { }) { diff --git a/Terminal.Gui/Views/TableView/TableStyle.cs b/Terminal.Gui/Views/TableView/TableStyle.cs index 2cf258bee..4dd947734 100644 --- a/Terminal.Gui/Views/TableView/TableStyle.cs +++ b/Terminal.Gui/Views/TableView/TableStyle.cs @@ -15,11 +15,11 @@ public class TableStyle /// public bool AlwaysUseNormalColorForVerticalCellLines { get; set; } = false; - /// Collection of columns for which you want special rendering (e.g. custom column lengths, text alignment etc) + /// Collection of columns for which you want special rendering (e.g. custom column lengths, text justification, etc.) public Dictionary ColumnStyles { get; set; } = new (); /// - /// Determines rendering when the last column in the table is visible but it's content or + /// Determines rendering when the last column in the table is visible, but it's content or /// is less than the remaining space in the control. True (the default) will expand /// the column to fill the remaining bounds of the control. False will draw a column ending line and leave a blank /// column that cannot be selected in the remaining space. diff --git a/Terminal.Gui/Views/TableView/TableView.cs b/Terminal.Gui/Views/TableView/TableView.cs index 0c39895e3..099f4d1e2 100644 --- a/Terminal.Gui/Views/TableView/TableView.cs +++ b/Terminal.Gui/Views/TableView/TableView.cs @@ -2116,16 +2116,16 @@ public class TableView : View - (representation.EnumerateRunes ().Sum (c => c.GetColumns ()) + 1 /*leave 1 space for cell boundary*/); - switch (colStyle?.GetAlignment (originalCellValue) ?? TextAlignment.Left) + switch (colStyle?.GetAlignment (originalCellValue) ?? Alignment.Left) { - case TextAlignment.Left: + case Alignment.Left: return representation + new string (' ', toPad); - case TextAlignment.Right: + case Alignment.Right: return new string (' ', toPad) + representation; // TODO: With single line cells, centered and justified are the same right? - case TextAlignment.Centered: - case TextAlignment.Justified: + case Alignment.Centered: + case Alignment.Justified: return new string (' ', (int)Math.Floor (toPad / 2.0)) + // round down diff --git a/Terminal.Gui/Views/TextValidateField.cs b/Terminal.Gui/Views/TextValidateField.cs index 268bf0c73..eb6bdc5c3 100644 --- a/Terminal.Gui/Views/TextValidateField.cs +++ b/Terminal.Gui/Views/TextValidateField.cs @@ -539,7 +539,7 @@ namespace Terminal.Gui { int c = _provider.Cursor (mouseEvent.Position.X - GetMargins (Viewport.Width).left); - if (_provider.Fixed == false && TextAlignment == TextAlignment.Right && Text.Length > 0) + if (_provider.Fixed == false && TextAlignment == Alignment.Right && Text.Length > 0) { c++; } @@ -633,7 +633,7 @@ namespace Terminal.Gui // When it's right-aligned and it's a normal input, the cursor behaves differently. int curPos; - if (_provider?.Fixed == false && TextAlignment == TextAlignment.Right) + if (_provider?.Fixed == false && TextAlignment == Alignment.Right) { curPos = _cursorPosition + left - 1; } @@ -650,7 +650,7 @@ namespace Terminal.Gui /// private bool BackspaceKeyHandler () { - if (_provider.Fixed == false && TextAlignment == TextAlignment.Right && _cursorPosition <= 1) + if (_provider.Fixed == false && TextAlignment == Alignment.Right && _cursorPosition <= 1) { return false; } @@ -688,7 +688,7 @@ namespace Terminal.Gui /// private bool DeleteKeyHandler () { - if (_provider.Fixed == false && TextAlignment == TextAlignment.Right) + if (_provider.Fixed == false && TextAlignment == Alignment.Right) { _cursorPosition = _provider.CursorLeft (_cursorPosition); } @@ -719,11 +719,11 @@ namespace Terminal.Gui switch (TextAlignment) { - case TextAlignment.Left: + case Alignment.Left: return (0, total); - case TextAlignment.Centered: + case Alignment.Centered: return (total / 2, total / 2 + total % 2); - case TextAlignment.Right: + case Alignment.Right: return (total, 0); default: return (0, total); diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 7a70a94d0..09531f254 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -1784,7 +1784,7 @@ internal class WordWrapManager TextFormatter.Format ( TextModel.ToString (line), width, - TextAlignment.Left, + Alignment.Left, true, preserveTrailingSpaces, tabWidth @@ -4161,7 +4161,10 @@ public class TextView : View } else { - PositionCursor (); + if (IsInitialized) + { + PositionCursor (); + } } OnUnwrappedCursorPosition (); diff --git a/Terminal.Gui/Views/Wizard/Wizard.cs b/Terminal.Gui/Views/Wizard/Wizard.cs index 205739d44..c6334e434 100644 --- a/Terminal.Gui/Views/Wizard/Wizard.cs +++ b/Terminal.Gui/Views/Wizard/Wizard.cs @@ -54,23 +54,6 @@ public class Wizard : Dialog private readonly LinkedList _steps = new (); private WizardStep _currentStep; private bool _finishedPressed; - - ///// - ///// The title of the Wizard, shown at the top of the Wizard with " - currentStep.Title" appended. - ///// - ///// - ///// The Title is only displayed when the is set to false. - ///// - //public new string Title { - // get { - // // The base (Dialog) Title holds the full title ("Wizard Title - Step Title") - // return base.Title; - // } - // set { - // wizardTitle = value; - // base.Title = $"{wizardTitle}{(steps.Count > 0 && currentStep is { } ? " - " + currentStep.Title : string.Empty)}"; - // } - //} private string _wizardTitle = string.Empty; /// @@ -83,9 +66,8 @@ public class Wizard : Dialog /// public Wizard () { - // Using Justify causes the Back and Next buttons to be hard justified against - // the left and right edge - ButtonAlignment = ButtonAlignments.Justify; + // TODO: LastRightRestLeft will enable a "Quit" button to always appear at the far left + ButtonAlignment = Alignment.LastRightRestLeft; BorderStyle = LineStyle.Double; //// Add a horiz separator diff --git a/Terminal.sln.DotSettings b/Terminal.sln.DotSettings index ca15b5583..2bb065af1 100644 --- a/Terminal.sln.DotSettings +++ b/Terminal.sln.DotSettings @@ -439,5 +439,6 @@ Concurrency Issue (?<=\W|^)(?<TAG>CONCURRENCY:)(\W|$)(.*) Warning + True True diff --git a/UICatalog/Scenarios/BasicColors.cs b/UICatalog/Scenarios/BasicColors.cs index 1780d7cbb..58bbcce88 100644 --- a/UICatalog/Scenarios/BasicColors.cs +++ b/UICatalog/Scenarios/BasicColors.cs @@ -32,7 +32,7 @@ public class BasicColors : Scenario Y = 0, Width = 1, Height = 13, - VerticalTextAlignment = VerticalTextAlignment.Bottom, + VerticalTextAlignment = Alignment.Bottom, ColorScheme = new ColorScheme { Normal = attr }, Text = bg.ToString (), TextDirection = TextDirection.TopBottom_LeftRight @@ -45,7 +45,7 @@ public class BasicColors : Scenario Y = y, Width = 13, Height = 1, - TextAlignment = TextAlignment.Right, + TextAlignment = Alignment.Right, ColorScheme = new ColorScheme { Normal = attr }, Text = bg.ToString () }; diff --git a/UICatalog/Scenarios/Buttons.cs b/UICatalog/Scenarios/Buttons.cs index 6ba57e02f..1fd83c2b8 100644 --- a/UICatalog/Scenarios/Buttons.cs +++ b/UICatalog/Scenarios/Buttons.cs @@ -287,39 +287,39 @@ public class Buttons : Scenario switch (args.SelectedItem) { case 0: - moveBtn.TextAlignment = TextAlignment.Left; - sizeBtn.TextAlignment = TextAlignment.Left; - moveBtnA.TextAlignment = TextAlignment.Left; - sizeBtnA.TextAlignment = TextAlignment.Left; - moveHotKeyBtn.TextAlignment = TextAlignment.Left; - moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Left; + moveBtn.TextAlignment = Alignment.Left; + sizeBtn.TextAlignment = Alignment.Left; + moveBtnA.TextAlignment = Alignment.Left; + sizeBtnA.TextAlignment = Alignment.Left; + moveHotKeyBtn.TextAlignment = Alignment.Left; + moveUnicodeHotKeyBtn.TextAlignment = Alignment.Left; break; case 1: - moveBtn.TextAlignment = TextAlignment.Right; - sizeBtn.TextAlignment = TextAlignment.Right; - moveBtnA.TextAlignment = TextAlignment.Right; - sizeBtnA.TextAlignment = TextAlignment.Right; - moveHotKeyBtn.TextAlignment = TextAlignment.Right; - moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Right; + moveBtn.TextAlignment = Alignment.Right; + sizeBtn.TextAlignment = Alignment.Right; + moveBtnA.TextAlignment = Alignment.Right; + sizeBtnA.TextAlignment = Alignment.Right; + moveHotKeyBtn.TextAlignment = Alignment.Right; + moveUnicodeHotKeyBtn.TextAlignment = Alignment.Right; break; case 2: - moveBtn.TextAlignment = TextAlignment.Centered; - sizeBtn.TextAlignment = TextAlignment.Centered; - moveBtnA.TextAlignment = TextAlignment.Centered; - sizeBtnA.TextAlignment = TextAlignment.Centered; - moveHotKeyBtn.TextAlignment = TextAlignment.Centered; - moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Centered; + moveBtn.TextAlignment = Alignment.Centered; + sizeBtn.TextAlignment = Alignment.Centered; + moveBtnA.TextAlignment = Alignment.Centered; + sizeBtnA.TextAlignment = Alignment.Centered; + moveHotKeyBtn.TextAlignment = Alignment.Centered; + moveUnicodeHotKeyBtn.TextAlignment = Alignment.Centered; break; case 3: - moveBtn.TextAlignment = TextAlignment.Justified; - sizeBtn.TextAlignment = TextAlignment.Justified; - moveBtnA.TextAlignment = TextAlignment.Justified; - sizeBtnA.TextAlignment = TextAlignment.Justified; - moveHotKeyBtn.TextAlignment = TextAlignment.Justified; - moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Justified; + moveBtn.TextAlignment = Alignment.Justified; + sizeBtn.TextAlignment = Alignment.Justified; + moveBtnA.TextAlignment = Alignment.Justified; + sizeBtnA.TextAlignment = Alignment.Justified; + moveHotKeyBtn.TextAlignment = Alignment.Justified; + moveUnicodeHotKeyBtn.TextAlignment = Alignment.Justified; break; } @@ -418,9 +418,8 @@ public class Buttons : Scenario throw new InvalidOperationException ("T must be a numeric type that supports addition and subtraction."); } - // TODO: Use Dim.Auto for the Width and Height - Height = 1; - Width = Dim.Func (() => Digits + 2); // button + 3 for number + button + Width = Dim.Auto (DimAutoStyle.Content); //Dim.Function (() => Digits + 2); // button + 3 for number + button + Height = Dim.Auto (DimAutoStyle.Content); _down = new () { @@ -440,7 +439,7 @@ public class Buttons : Scenario Y = Pos.Top (_down), Width = Dim.Func (() => Digits), Height = 1, - TextAlignment = TextAlignment.Centered, + TextAlignment = Alignment.Centered, CanFocus = true }; diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index 0a32490de..07a99b69d 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -958,7 +958,7 @@ internal class CharMap : View Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1), - TextAlignment = TextAlignment.Centered + TextAlignment = Alignment.Centered }; var spinner = new SpinnerView { X = Pos.Center (), Y = Pos.Center (), Style = new Aesthetic () }; spinner.AutoSpin = true; diff --git a/UICatalog/Scenarios/CollectionNavigatorTester.cs b/UICatalog/Scenarios/CollectionNavigatorTester.cs index ef31ac6a7..0c9b3ab14 100644 --- a/UICatalog/Scenarios/CollectionNavigatorTester.cs +++ b/UICatalog/Scenarios/CollectionNavigatorTester.cs @@ -142,7 +142,7 @@ public class CollectionNavigatorTester : Scenario var label = new Label { Text = "ListView", - TextAlignment = TextAlignment.Centered, + TextAlignment = Alignment.Centered, X = 0, Y = 1, // for menu Width = Dim.Percent (50), @@ -171,7 +171,7 @@ public class CollectionNavigatorTester : Scenario var label = new Label { Text = "TreeView", - TextAlignment = TextAlignment.Centered, + TextAlignment = Alignment.Centered, X = Pos.Right (_listView) + 2, Y = 1, // for menu Width = Dim.Percent (50), diff --git a/UICatalog/Scenarios/ColorPicker.cs b/UICatalog/Scenarios/ColorPicker.cs index fe2b08fad..6d5f61814 100644 --- a/UICatalog/Scenarios/ColorPicker.cs +++ b/UICatalog/Scenarios/ColorPicker.cs @@ -69,8 +69,8 @@ public class ColorPickers : Scenario { Title = "Color Sample", Text = "Lorem Ipsum", - TextAlignment = TextAlignment.Centered, - VerticalTextAlignment = VerticalTextAlignment.Middle, + TextAlignment = Alignment.Centered, + VerticalTextAlignment = Alignment.Centered, BorderStyle = LineStyle.Heavy, X = Pos.Center (), Y = Pos.Center (), diff --git a/UICatalog/Scenarios/ComputedLayout.cs b/UICatalog/Scenarios/ComputedLayout.cs index 5e6eb5cd2..9c9901751 100644 --- a/UICatalog/Scenarios/ComputedLayout.cs +++ b/UICatalog/Scenarios/ComputedLayout.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using Terminal.Gui; +using static Terminal.Gui.Dialog; namespace UICatalog.Scenarios; @@ -85,12 +86,12 @@ public class ComputedLayout : Scenario var i = 1; var txt = "Resize the terminal to see computed layout in action."; List