using System;
using System.Collections.Generic;
namespace Terminal.Gui;
public partial class View {
string _text;
///
/// The text displayed by the .
///
///
///
/// 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 and .
///
///
/// The text will word-wrap to additional lines if it does not fit horizontally. If 's height
/// is 1, the text will be clipped.
///
///
/// Set the to enable hotkey support. To disable hotkey support set
/// to
/// (Rune)0xffff.
///
///
/// If is true, the will be adjusted to fit the text.
///
///
public virtual string Text {
get => _text;
set {
_text = value;
SetHotKey ();
UpdateTextFormatterText ();
OnResizeNeeded ();
#if DEBUG
if (_text != null && string.IsNullOrEmpty (Id)) {
Id = _text;
}
#endif
}
}
///
/// Gets or sets the used to format .
///
public TextFormatter TextFormatter { get; set; }
///
/// Gets or sets whether trailing spaces at the end of word-wrapped lines are preserved
/// or not when is enabled.
/// If trailing spaces at the end of wrapped lines will be removed when
/// is formatted for display. The default is .
///
public virtual bool PreserveTrailingSpaces {
get => TextFormatter.PreserveTrailingSpaces;
set {
if (TextFormatter.PreserveTrailingSpaces != value) {
TextFormatter.PreserveTrailingSpaces = value;
TextFormatter.NeedsFormat = true;
}
}
}
///
/// Gets or sets how the View's is aligned horizontally when drawn. Changing this property will
/// redisplay the .
///
///
///
/// If is true, the will be adjusted to fit the text.
///
///
/// The text alignment.
public virtual TextAlignment TextAlignment {
get => TextFormatter.Alignment;
set {
TextFormatter.Alignment = value;
UpdateTextFormatterText ();
OnResizeNeeded ();
}
}
///
/// Gets or sets how the View's is aligned vertically when drawn. Changing this property will redisplay
/// the .
///
///
///
/// If is true, the will be adjusted to fit the text.
///
///
/// The text alignment.
public virtual VerticalTextAlignment VerticalTextAlignment {
get => TextFormatter.VerticalAlignment;
set {
TextFormatter.VerticalAlignment = value;
SetNeedsDisplay ();
}
}
///
/// Gets or sets the direction of the View's . Changing this property will redisplay the
/// .
///
///
///
/// If is true, the will be adjusted to fit the text.
///
///
/// The text alignment.
public virtual TextDirection TextDirection {
get => TextFormatter.Direction;
set {
UpdateTextDirection (value);
TextFormatter.Direction = value;
}
}
///
/// Can be overridden if the has
/// different format than the default.
///
protected virtual void UpdateTextFormatterText ()
{
if (TextFormatter != null) {
TextFormatter.Text = _text;
}
}
void UpdateTextDirection (TextDirection newDirection)
{
var directionChanged = TextFormatter.IsHorizontalDirection (TextFormatter.Direction) != TextFormatter.IsHorizontalDirection (newDirection);
TextFormatter.Direction = newDirection;
var isValidOldAutoSize = AutoSize && IsValidAutoSize (out var _);
UpdateTextFormatterText ();
if (!ValidatePosDim && directionChanged && AutoSize || ValidatePosDim && directionChanged && AutoSize && isValidOldAutoSize) {
OnResizeNeeded ();
} else if (directionChanged && IsAdded) {
ResizeBoundsToFit (Bounds.Size);
// BUGBUG: I think this call is redundant.
SetFrameToFitText ();
} else {
SetFrameToFitText ();
}
SetTextFormatterSize ();
SetNeedsDisplay ();
}
///
/// Sets the size of the View to the minimum width or height required to fit .
///
///
/// if the size was changed; if ==
/// or
/// will not fit.
///
///
/// Always returns if is or
/// if (Horizontal) or (Vertical) are not not set or zero.
/// Does not take into account word wrapping.
///
bool SetFrameToFitText ()
{
// BUGBUG: This API is broken - should not assume Frame.Height == Bounds.Height
//
// Gets the minimum dimensions required to fit the View's , factoring in .
//
// The minimum dimensions required.
// if the dimensions fit within the View's , otherwise.
//
// Always returns if is or
// if (Horizontal) or (Vertical) are not not set or zero.
// Does not take into account word wrapping.
//
bool GetMinimumSizeOfText (out Size sizeRequired)
{
if (!IsInitialized) {
sizeRequired = new Size (0, 0);
return false;
}
sizeRequired = Bounds.Size;
if (AutoSize || string.IsNullOrEmpty (TextFormatter.Text)) {
return false;
}
switch (TextFormatter.IsVerticalDirection (TextDirection)) {
case true:
var colWidth = TextFormatter.GetSumMaxCharWidth (new List { TextFormatter.Text }, 0, 1);
// TODO: v2 - This uses frame.Width; it should only use Bounds
if (_frame.Width < colWidth &&
(Width == null ||
Bounds.Width >= 0 &&
Width is Dim.DimAbsolute &&
Width.Anchor (0) >= 0 &&
Width.Anchor (0) < colWidth)) {
sizeRequired = new Size (colWidth, Bounds.Height);
return true;
}
break;
default:
if (_frame.Height < 1 &&
(Height == null ||
Height is Dim.DimAbsolute &&
Height.Anchor (0) == 0)) {
sizeRequired = new Size (Bounds.Width, 1);
return true;
}
break;
}
return false;
}
if (GetMinimumSizeOfText (out var size)) {
// TODO: This is a hack.
//_width = size.Width;
//_height = size.Height;
_frame = new Rect (_frame.Location, size);
//throw new InvalidOperationException ("This is a hack.");
return true;
}
return false;
}
///
/// Gets the width or height of the characters
/// in the property.
///
///
/// 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.
///
///
/// The number of characters required for the . If the text
/// direction specified
/// by does not match the parameter, 0 is returned.
///
public int GetHotKeySpecifierLength (bool isWidth = true)
{
if (isWidth) {
return TextFormatter.IsHorizontalDirection (TextDirection) &&
TextFormatter.Text?.Contains ((char)HotKeySpecifier.Value) == true
? Math.Max (HotKeySpecifier.GetColumns (), 0) : 0;
}
return TextFormatter.IsVerticalDirection (TextDirection) &&
TextFormatter.Text?.Contains ((char)HotKeySpecifier.Value) == true
? Math.Max (HotKeySpecifier.GetColumns (), 0) : 0;
}
///
/// Gets the dimensions required for ignoring a .
///
///
internal Size GetSizeNeededForTextWithoutHotKey () => new (TextFormatter.Size.Width - GetHotKeySpecifierLength (),
TextFormatter.Size.Height - GetHotKeySpecifierLength (false));
///
/// Sets .Size to the current size, adjusted for
/// .
///
///
/// Use this API to set when the view has changed such that the
/// size required to fit the text has changed.
/// changes.
///
///
internal void SetTextFormatterSize ()
{
if (!IsInitialized) {
TextFormatter.Size = Size.Empty;
return;
}
if (string.IsNullOrEmpty (TextFormatter.Text)) {
TextFormatter.Size = Bounds.Size;
return;
}
TextFormatter.Size = new Size (Bounds.Size.Width + GetHotKeySpecifierLength (),
Bounds.Size.Height + GetHotKeySpecifierLength (false));
}
///
/// Gets the Frame dimensions required to fit within using the text
/// specified by the
/// property and accounting for any characters.
///
/// The the needs to be set to fit the text.
public Size GetAutoSize ()
{
var x = 0;
var y = 0;
if (IsInitialized) {
x = Bounds.X;
y = Bounds.Y;
}
var rect = TextFormatter.CalcRect (x, y, TextFormatter.Text, TextFormatter.Direction);
int newWidth = rect.Size.Width - GetHotKeySpecifierLength () + (Margin == null ? 0 : Margin.Thickness.Horizontal + Border.Thickness.Horizontal + Padding.Thickness.Horizontal);
int newHeight = rect.Size.Height - GetHotKeySpecifierLength (false) + (Margin == null ? 0 : Margin.Thickness.Vertical + Border.Thickness.Vertical + Padding.Thickness.Vertical);
return new Size (newWidth, newHeight);
}
bool IsValidAutoSize (out Size autoSize)
{
var rect = TextFormatter.CalcRect (_frame.X, _frame.Y, TextFormatter.Text, TextDirection);
autoSize = new Size (rect.Size.Width - GetHotKeySpecifierLength (),
rect.Size.Height - GetHotKeySpecifierLength (false));
return !(ValidatePosDim && (!(Width is Dim.DimAbsolute) || !(Height is Dim.DimAbsolute)) ||
_frame.Size.Width != rect.Size.Width - GetHotKeySpecifierLength () ||
_frame.Size.Height != rect.Size.Height - GetHotKeySpecifierLength (false));
}
bool IsValidAutoSizeWidth (Dim width)
{
var rect = TextFormatter.CalcRect (_frame.X, _frame.Y, TextFormatter.Text, TextDirection);
var dimValue = width.Anchor (0);
return !(ValidatePosDim && !(width is Dim.DimAbsolute) || dimValue != rect.Size.Width - GetHotKeySpecifierLength ());
}
bool IsValidAutoSizeHeight (Dim height)
{
var rect = TextFormatter.CalcRect (_frame.X, _frame.Y, TextFormatter.Text, TextDirection);
var dimValue = height.Anchor (0);
return !(ValidatePosDim && !(height is Dim.DimAbsolute) || dimValue != rect.Size.Height - GetHotKeySpecifierLength (false));
}
}