Skip to content

Commit

Permalink
Add DrawIncompleteFrame method and unit tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
BDisp committed Nov 18, 2023
1 parent 98f9e1e commit 680ba26
Show file tree
Hide file tree
Showing 2 changed files with 580 additions and 13 deletions.
277 changes: 266 additions & 11 deletions Terminal.Gui/View/ViewDrawing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,28 @@

namespace Terminal.Gui {
public partial class View {
/// <summary>
/// Specifies the side to start when draw frame with
/// <see cref="DrawIncompleteFrame(ValueTuple{int, Side}, ValueTuple{int, Side}, Rect, LineStyle, Attribute?, bool)"/> method.
/// </summary>
public enum Side {
/// <summary>
/// Start on left.
/// </summary>
Left,
/// <summary>
/// Start on top.
/// </summary>
Top,
/// <summary>
/// Start on right.
/// </summary>
Right,
/// <summary>
/// Start on bottom.
/// </summary>
Bottom
};

ColorScheme _colorScheme;

Expand Down Expand Up @@ -184,7 +206,7 @@ public void SetSubViewNeedsDisplay ()
/// This clears the Bounds used by this view.
/// </para>
/// </remarks>
public void Clear () => Clear (ViewToScreen(Bounds));
public void Clear () => Clear (ViewToScreen (Bounds));

// BUGBUG: This version of the Clear API should be removed. We should have a tenet that says
// "View APIs only deal with View-relative coords". This is only used by ComboBox which can
Expand Down Expand Up @@ -504,24 +526,257 @@ public virtual void OnDrawContentComplete (Rect contentArea)
}

/// <summary>
/// Draw a frame based on the passed bounds to the screen relative.
/// Draws a rectangular frame. The frame will be merged (auto-joined) with any other lines drawn by this View
/// if <paramref name="mergeWithLineCanvas"/> is true, otherwise will be rendered immediately.
/// </summary>
/// <param name="bounds">The bounds view relative.</param>
/// <param name="rect">The view relative location and size of the frame.</param>
/// <param name="lineStyle">The line style.</param>
/// <param name="attribute">The color to use.</param>
public void DrawFrame (Rect bounds, LineStyle lineStyle, Attribute? attribute = null)
/// <param name="attribute">The colors to be used.</param>
/// <param name="mergeWithLineCanvas">When drawing the frame, allow it to integrate (join) to other frames in other controls.
/// Or false to simply draw the rect exactly with no side effects.</param>
public void DrawFrame (Rect rect, LineStyle lineStyle, Attribute? attribute = null, bool mergeWithLineCanvas = true)
{
var vts = ViewToScreen (bounds);
LineCanvas.AddLine (new Point (vts.X, vts.Y), vts.Width,
LineCanvas lc;
if (mergeWithLineCanvas) {
lc = new LineCanvas ();
} else {
lc = LineCanvas;
}
var vts = ViewToScreen (rect);
lc.AddLine (new Point (vts.X, vts.Y), vts.Width,
Orientation.Horizontal, lineStyle, attribute);
LineCanvas.AddLine (new Point (vts.Right - 1, vts.Y), vts.Height,
lc.AddLine (new Point (vts.Right - 1, vts.Y), vts.Height,
Orientation.Vertical, lineStyle, attribute);
LineCanvas.AddLine (new Point (vts.X, vts.Bottom - 1), vts.Width,
lc.AddLine (new Point (vts.Right - 1, vts.Bottom - 1), -vts.Width,
Orientation.Horizontal, lineStyle, attribute);
LineCanvas.AddLine (new Point (vts.X, vts.Y), vts.Height,
lc.AddLine (new Point (vts.X, vts.Bottom - 1), -vts.Height,
Orientation.Vertical, lineStyle, attribute);

OnRenderLineCanvas ();
if (mergeWithLineCanvas) {
LineCanvas.Merge (lc);
} else {
OnRenderLineCanvas ();
}
}

/// <summary>
/// Draws an incomplete frame. The frame will be merged (auto-joined) with any other lines drawn by this View
/// if <paramref name="mergeWithLineCanvas"/> is true, otherwise will be rendered immediately.
/// The frame is always drawn clockwise. For <see cref="Side.Top"/> and <see cref="Side.Right"/> the end position must
/// be greater or equal to the start and for <see cref="Side.Left"/> and <see cref="Side.Bottom"/> the end position must
/// be less or equal to the start.
/// </summary>
/// <param name="startPos">The start and side position screen relative.</param>
/// <param name="endPos">The end and side position screen relative.</param>
/// <param name="rect">The view relative location and size of the frame.</param>
/// <param name="lineStyle">The line style.</param>
/// <param name="attribute">The colors to be used.</param>
/// <param name="mergeWithLineCanvas">When drawing the frame, allow it to integrate (join) to other frames in other controls.
/// Or false to simply draw the rect exactly with no side effects.</param>
public void DrawIncompleteFrame ((int start, Side side) startPos, (int end, Side side) endPos, Rect rect, LineStyle lineStyle, Attribute? attribute = null, bool mergeWithLineCanvas = true)
{
var vts = ViewToScreen (rect);
LineCanvas lc;
if (mergeWithLineCanvas) {
lc = new LineCanvas ();
} else {
lc = LineCanvas;
}
var start = startPos.start;
var end = endPos.end;
switch (startPos.side) {
case Side.Left:
if (start == vts.Y) {
lc.AddLine (new Point (vts.X, start), 1,
Orientation.Vertical, lineStyle, attribute);
} else {
if (end <= start && startPos.side == endPos.side) {
lc.AddLine (new Point (vts.X, start), end - start - 1,
Orientation.Vertical, lineStyle, attribute);
break;
} else {
lc.AddLine (new Point (vts.X, start), vts.Y - start - 1,
Orientation.Vertical, lineStyle, attribute);
}
}
switch (endPos.side) {
case Side.Left:
lc.AddLine (new Point (vts.X, vts.Y), vts.Width,
Orientation.Horizontal, lineStyle, attribute);
lc.AddLine (new Point (vts.Right - 1, vts.Y), vts.Height,
Orientation.Vertical, lineStyle, attribute);
lc.AddLine (new Point (vts.Right - 1, vts.Bottom - 1), -vts.Width,
Orientation.Horizontal, lineStyle, attribute);
if (end <= vts.Bottom - 1 && startPos.side == endPos.side) {
lc.AddLine (new Point (vts.X, vts.Bottom - 1), -(vts.Bottom - end),
Orientation.Vertical, lineStyle, attribute);
}
break;
case Side.Top:
lc.AddLine (new Point (vts.X, vts.Y), end,
Orientation.Horizontal, lineStyle, attribute);
break;
case Side.Right:
lc.AddLine (new Point (vts.X, vts.Y), vts.Width,
Orientation.Horizontal, lineStyle, attribute);
lc.AddLine (new Point (vts.Right - 1, vts.Y), end + 1,
Orientation.Vertical, lineStyle, attribute);
break;
case Side.Bottom:
lc.AddLine (new Point (vts.X, vts.Y), vts.Width,
Orientation.Horizontal, lineStyle, attribute);
lc.AddLine (new Point (vts.Right - 1, vts.Y), vts.Height,
Orientation.Vertical, lineStyle, attribute);
lc.AddLine (new Point (vts.Right - 1, vts.Bottom - 1), -end,
Orientation.Horizontal, lineStyle, attribute);
break;
}
break;

case Side.Top:
if (start == vts.Width - 1) {
lc.AddLine (new Point (vts.X + start, vts.Y), -1,
Orientation.Horizontal, lineStyle, attribute);
} else if (end >= start && startPos.side == endPos.side) {
lc.AddLine (new Point (vts.X + start, vts.Y), end - start + 1,
Orientation.Horizontal, lineStyle, attribute);
break;
} else if (vts.Width - start > 0) {
lc.AddLine (new Point (vts.X + start, vts.Y), Math.Max (vts.Width - start, 0),
Orientation.Horizontal, lineStyle, attribute);
}
switch (endPos.side) {
case Side.Left:
lc.AddLine (new Point (vts.Right - 1, vts.Y), vts.Height,
Orientation.Vertical, lineStyle, attribute);
lc.AddLine (new Point (vts.Right - 1, vts.Bottom - 1), -vts.Width,
Orientation.Horizontal, lineStyle, attribute);
lc.AddLine (new Point (vts.X, vts.Bottom - 1), -end,
Orientation.Vertical, lineStyle, attribute);
break;
case Side.Top:
lc.AddLine (new Point (vts.Right - 1, vts.Y), vts.Height,
Orientation.Vertical, lineStyle, attribute);
lc.AddLine (new Point (vts.Right - 1, vts.Bottom - 1), -vts.Width,
Orientation.Horizontal, lineStyle, attribute);
lc.AddLine (new Point (vts.X, vts.Bottom - 1), -vts.Height,
Orientation.Vertical, lineStyle, attribute);
if (end >= 0 && startPos.side == endPos.side) {
lc.AddLine (new Point (vts.X, vts.Y), end + 1,
Orientation.Horizontal, lineStyle, attribute);
}
break;
case Side.Right:
lc.AddLine (new Point (vts.Right - 1, vts.Y), end,
Orientation.Vertical, lineStyle, attribute);
break;
case Side.Bottom:
lc.AddLine (new Point (vts.Right - 1, vts.Y), vts.Height,
Orientation.Vertical, lineStyle, attribute);
lc.AddLine (new Point (vts.Right - 1, vts.Bottom - 1), -(vts.Width - end),
Orientation.Horizontal, lineStyle, attribute);
break;
}
break;
case Side.Right:
if (start == vts.Bottom - 1) {
lc.AddLine (new Point (vts.Width - 1, start), -1,
Orientation.Vertical, lineStyle, attribute);
} else {
if (end >= start && startPos.side == endPos.side) {
lc.AddLine (new Point (vts.Width - 1, start), end - start + 1,
Orientation.Vertical, lineStyle, attribute);
break;
} else {
lc.AddLine (new Point (vts.Width - 1, start), vts.Bottom - start,
Orientation.Vertical, lineStyle, attribute);
}
}
switch (endPos.side) {
case Side.Left:
lc.AddLine (new Point (vts.Width - 1, vts.Bottom - 1), -vts.Width,
Orientation.Horizontal, lineStyle, attribute);
lc.AddLine (new Point (vts.X, vts.Bottom - 1), -(vts.Bottom - end),
Orientation.Vertical, lineStyle, attribute);
break;
case Side.Top:
lc.AddLine (new Point (vts.Width - 1, vts.Bottom - 1), -vts.Width,
Orientation.Horizontal, lineStyle, attribute);
lc.AddLine (new Point (vts.X, vts.Bottom - 1), -vts.Height,
Orientation.Vertical, lineStyle, attribute);
lc.AddLine (new Point (vts.X, vts.Y), end,
Orientation.Horizontal, lineStyle, attribute);
break;
case Side.Right:
lc.AddLine (new Point (vts.Width - 1, vts.Bottom - 1), -vts.Width,
Orientation.Horizontal, lineStyle, attribute);
lc.AddLine (new Point (vts.X, vts.Bottom - 1), -vts.Height,
Orientation.Vertical, lineStyle, attribute);
lc.AddLine (new Point (vts.X, vts.Y), vts.Width,
Orientation.Horizontal, lineStyle, attribute);
if (end >= 0 && end < vts.Bottom - 1 && startPos.side == endPos.side) {
lc.AddLine (new Point (vts.Width - 1, vts.Y), end + 1,
Orientation.Vertical, lineStyle, attribute);
}
break;
case Side.Bottom:
lc.AddLine (new Point (vts.Width - 1, vts.Bottom - 1), -(vts.Width - end),
Orientation.Horizontal, lineStyle, attribute);
break;
}
break;
case Side.Bottom:
if (start == vts.X) {
lc.AddLine (new Point (vts.X, vts.Bottom - 1), 1,
Orientation.Horizontal, lineStyle, attribute);
} else if (end <= start && startPos.side == endPos.side) {
lc.AddLine (new Point (vts.X + start, vts.Bottom - 1), -(start - end + 1),
Orientation.Horizontal, lineStyle, attribute);
break;
} else {
lc.AddLine (new Point (vts.X + start, vts.Bottom - 1), -(start + 1),
Orientation.Horizontal, lineStyle, attribute);
}
switch (endPos.side) {
case Side.Left:
lc.AddLine (new Point (vts.X, vts.Bottom - 1), -(vts.Bottom - end),
Orientation.Vertical, lineStyle, attribute);
break;
case Side.Top:
lc.AddLine (new Point (vts.X, vts.Bottom - 1), -vts.Height,
Orientation.Vertical, lineStyle, attribute);
lc.AddLine (new Point (vts.X, vts.Y), end + 1,
Orientation.Horizontal, lineStyle, attribute);
break;
case Side.Right:
lc.AddLine (new Point (vts.X, vts.Bottom - 1), -vts.Height,
Orientation.Vertical, lineStyle, attribute);
lc.AddLine (new Point (vts.X, vts.Y), vts.Width,
Orientation.Horizontal, lineStyle, attribute);
lc.AddLine (new Point (vts.Width - 1, vts.Y), end,
Orientation.Vertical, lineStyle, attribute);
break;
case Side.Bottom:
lc.AddLine (new Point (vts.X, vts.Bottom - 1), -vts.Height,
Orientation.Vertical, lineStyle, attribute);
lc.AddLine (new Point (vts.X, vts.Y), vts.Width,
Orientation.Horizontal, lineStyle, attribute);
lc.AddLine (new Point (vts.Width - 1, vts.Y), vts.Height,
Orientation.Vertical, lineStyle, attribute);
if (vts.Width - end > 0 && startPos.side == endPos.side) {
lc.AddLine (new Point (vts.Width - 1, vts.Bottom - 1), -(vts.Width - end),
Orientation.Horizontal, lineStyle, attribute);
}
break;
}
break;
}

if (mergeWithLineCanvas) {
LineCanvas.Merge (lc);
} else {
OnRenderLineCanvas ();
}
}
}
}
Loading

0 comments on commit 680ba26

Please sign in to comment.