| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- namespace Terminal.Gui.Views;
- /// <summary>
- /// Draws a single line using the <see cref="LineStyle"/> specified by <see cref="Line.Style"/>.
- /// </summary>
- /// <remarks>
- /// <para>
- /// <see cref="Line"/> is a <see cref="View"/> that renders a single horizontal or vertical line
- /// using the <see cref="LineCanvas"/> system. <see cref="Line"/> integrates with the LineCanvas
- /// to enable proper box-drawing character selection and line intersection handling.
- /// </para>
- /// <para>
- /// The line's appearance is controlled by the <see cref="Style"/> property, which supports
- /// various line styles including Single, Double, Heavy, Rounded, Dashed, and Dotted.
- /// </para>
- /// <para>
- /// Use the <see cref="Length"/> property to control the extent of the line regardless of its
- /// <see cref="Orientation"/>. For horizontal lines, Length controls Width; for vertical lines,
- /// it controls Height. The perpendicular dimension is always 1.
- /// </para>
- /// <para>
- /// When multiple <see cref="Line"/> instances or other LineCanvas-aware views (like <see cref="Border"/>)
- /// intersect, the LineCanvas automatically selects the appropriate box-drawing characters for corners,
- /// T-junctions, and crosses.
- /// </para>
- /// <para>
- /// <see cref="Line"/> sets <see cref="View.SuperViewRendersLineCanvas"/> to <see langword="true"/>,
- /// meaning its parent view is responsible for rendering the line. This allows for proper intersection
- /// handling when multiple views contribute lines to the same canvas.
- /// </para>
- /// </remarks>
- /// <example>
- /// <code>
- /// // Create a horizontal line
- /// var hLine = new Line { Y = 5 };
- ///
- /// // Create a vertical line with specific length
- /// var vLine = new Line { X = 10, Orientation = Orientation.Vertical, Length = 15 };
- ///
- /// // Create a double-line style horizontal line
- /// var doubleLine = new Line { Y = 10, Style = LineStyle.Double };
- /// </code>
- /// </example>
- public class Line : View, IOrientation
- {
- private readonly OrientationHelper _orientationHelper;
- private LineStyle _style = LineStyle.Single;
- private Dim _length = Dim.Fill ();
- /// <summary>
- /// Constructs a new instance of the <see cref="Line"/> class with horizontal orientation.
- /// </summary>
- /// <remarks>
- /// By default, a horizontal line fills the available width and has a height of 1.
- /// The line style defaults to <see cref="LineStyle.Single"/>.
- /// </remarks>
- public Line ()
- {
- CanFocus = false;
- base.SuperViewRendersLineCanvas = true;
- _orientationHelper = new (this);
- _orientationHelper.Orientation = Orientation.Horizontal;
- // Set default dimensions for horizontal orientation
- // Set Height first (this will update _length, but we'll override it next)
- Height = 1;
- // Now set Width and _length to Fill
- _length = Dim.Fill ();
- Width = _length;
- }
- /// <summary>
- /// Gets or sets the length of the line along its orientation.
- /// </summary>
- /// <remarks>
- /// <para>
- /// This is the "source of truth" for the line's primary dimension.
- /// For a horizontal line, Length controls Width.
- /// For a vertical line, Length controls Height.
- /// </para>
- /// <para>
- /// When Width or Height is set directly, Length is updated to match the primary dimension.
- /// When Orientation changes, the appropriate dimension is set to Length and the perpendicular
- /// dimension is set to 1.
- /// </para>
- /// <para>
- /// This property provides a cleaner API for controlling the line's extent
- /// without needing to know whether to use Width or Height.
- /// </para>
- /// </remarks>
- public Dim Length
- {
- get => Orientation == Orientation.Horizontal ? Width : Height;
- set
- {
- _length = value;
- // Update the appropriate dimension based on current orientation
- if (Orientation == Orientation.Horizontal)
- {
- Width = _length;
- }
- else
- {
- Height = _length;
- }
- }
- }
- /// <summary>
- /// Gets or sets the style of the line. This controls the visual appearance of the line.
- /// </summary>
- /// <remarks>
- /// Supports various line styles including Single, Double, Heavy, Rounded, Dashed, and Dotted.
- /// Note: This is separate from <see cref="View.BorderStyle"/> to avoid conflicts with the View's Border.
- /// </remarks>
- public LineStyle Style
- {
- get => _style;
- set
- {
- if (_style != value)
- {
- _style = value;
- SetNeedsDraw ();
- }
- }
- }
- #region IOrientation members
- /// <summary>
- /// The direction of the line.
- /// </summary>
- /// <remarks>
- /// <para>
- /// When orientation changes, the appropriate dimension is set to <see cref="Length"/>
- /// and the perpendicular dimension is set to 1.
- /// </para>
- /// <para>
- /// For object initializers where dimensions are set before orientation:
- /// <code>new Line { Height = 9, Orientation = Orientation.Vertical }</code>
- /// Setting Height=9 updates Length to 9 (since default orientation is Horizontal and Height is perpendicular).
- /// Then when Orientation is set to Vertical, Height is set to Length (9) and Width is set to 1,
- /// resulting in the expected Width=1, Height=9.
- /// </para>
- /// </remarks>
- public Orientation Orientation
- {
- get => _orientationHelper.Orientation;
- set => _orientationHelper.Orientation = value;
- }
- #pragma warning disable CS0067 // The event is never used
- /// <inheritdoc/>
- public event EventHandler<CancelEventArgs<Orientation>>? OrientationChanging;
- /// <inheritdoc/>
- public event EventHandler<EventArgs<Orientation>>? OrientationChanged;
- #pragma warning restore CS0067 // The event is never used
- /// <inheritdoc/>
- public void OnOrientationChanged (Orientation newOrientation)
- {
- // Set dimensions based on new orientation:
- // - Primary dimension (along orientation) = Length
- // - Perpendicular dimension = 1
- if (newOrientation == Orientation.Horizontal)
- {
- Width = _length;
- Height = 1;
- }
- else
- {
- Height = _length;
- Width = 1;
- }
- }
- /// <inheritdoc/>
- protected override bool OnWidthChanging (ValueChangingEventArgs<Dim> e)
- {
- // If horizontal, allow width changes and update _length
- _length = e.NewValue;
- if (Orientation == Orientation.Horizontal)
- {
- return base.OnWidthChanging (e);
- }
- // If vertical, keep width at 1 (don't allow changes to perpendicular dimension)
- e.NewValue = 1;
- return base.OnWidthChanging (e);
- }
- /// <inheritdoc/>
- protected override bool OnHeightChanging (ValueChangingEventArgs<Dim> e)
- {
- // If vertical, allow height changes and update _length
- _length = e.NewValue;
- if (Orientation == Orientation.Vertical)
- {
- return base.OnHeightChanging (e);
- }
- e.NewValue = 1;
- return base.OnHeightChanging (e);
- }
- #endregion
- /// <inheritdoc/>
- /// <remarks>
- /// This method adds the line to the LineCanvas for rendering.
- /// The actual rendering is performed by the parent view through <see cref="View.RenderLineCanvas"/>.
- /// </remarks>
- protected override bool OnDrawingContent ()
- {
- Point pos = ViewportToScreen (Viewport).Location;
- int length = Orientation == Orientation.Horizontal ? Frame.Width : Frame.Height;
- LineCanvas.AddLine (
- pos,
- length,
- Orientation,
- Style,
- GetAttributeForRole(VisualRole.Normal)
- );
- return true;
- }
- }
|