mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-27 00:07:58 +01:00
Refactored the `View` class to include a `NeedsDraw` check in multiple drawing methods, improving rendering efficiency. Adjusted `OnDrewText` and `DrewText` event handling for consistency. Removed unused code and redundant tests. Rewrote and reorganized `ClearViewportTests` for clarity and compatibility with the new `NeedsDraw` logic. Added new tests to validate `ClearViewport` behavior under various conditions, including transparent viewports, event cancellations, and content-only clearing. Updated namespaces for better alignment, disabled a noisy `ComboBoxTests` test, and improved code formatting and maintainability across files.
468 lines
12 KiB
C#
468 lines
12 KiB
C#
using System.Text;
|
|
using UnitTests;
|
|
using Xunit.Abstractions;
|
|
|
|
namespace ViewBaseTests.Drawing;
|
|
|
|
public class ViewDrawTextAndLineCanvasTests () : FakeDriverBase
|
|
{
|
|
#region DrawText Tests
|
|
|
|
[Fact]
|
|
public void DrawText_EmptyText_DoesNotThrow ()
|
|
{
|
|
IDriver driver = CreateFakeDriver (80, 25);
|
|
driver.Clip = new Region (driver.Screen);
|
|
|
|
var view = new View
|
|
{
|
|
X = 10,
|
|
Y = 10,
|
|
Width = 20,
|
|
Height = 20,
|
|
Driver = driver,
|
|
Text = ""
|
|
};
|
|
view.BeginInit ();
|
|
view.EndInit ();
|
|
view.LayoutSubViews ();
|
|
|
|
var exception = Record.Exception (() => view.Draw ());
|
|
|
|
Assert.Null (exception);
|
|
}
|
|
|
|
[Fact]
|
|
public void DrawText_NullText_DoesNotThrow ()
|
|
{
|
|
IDriver driver = CreateFakeDriver (80, 25);
|
|
driver.Clip = new Region (driver.Screen);
|
|
|
|
var view = new View
|
|
{
|
|
X = 10,
|
|
Y = 10,
|
|
Width = 20,
|
|
Height = 20,
|
|
Driver = driver,
|
|
Text = null!
|
|
};
|
|
view.BeginInit ();
|
|
view.EndInit ();
|
|
view.LayoutSubViews ();
|
|
|
|
var exception = Record.Exception (() => view.Draw ());
|
|
|
|
Assert.Null (exception);
|
|
}
|
|
|
|
[Fact]
|
|
public void DrawText_DrawsTextToDriver ()
|
|
{
|
|
IDriver driver = CreateFakeDriver (80, 25);
|
|
driver.Clip = new Region (driver.Screen);
|
|
|
|
var view = new View
|
|
{
|
|
X = 1,
|
|
Y = 1,
|
|
Width = 20,
|
|
Height = 20,
|
|
Driver = driver,
|
|
Text = "Test"
|
|
};
|
|
view.BeginInit ();
|
|
view.EndInit ();
|
|
view.LayoutSubViews ();
|
|
|
|
view.Draw ();
|
|
|
|
// Text should appear at the content location
|
|
Point screenPos = view.ContentToScreen (Point.Empty);
|
|
|
|
Assert.Equal ("T", driver.Contents! [screenPos.Y, screenPos.X].Grapheme);
|
|
Assert.Equal ("e", driver.Contents [screenPos.Y, screenPos.X + 1].Grapheme);
|
|
Assert.Equal ("s", driver.Contents [screenPos.Y, screenPos.X + 2].Grapheme);
|
|
Assert.Equal ("t", driver.Contents [screenPos.Y, screenPos.X + 3].Grapheme);
|
|
}
|
|
|
|
[Fact]
|
|
public void DrawText_WithFocus_UsesFocusAttribute ()
|
|
{
|
|
IDriver driver = CreateFakeDriver (80, 25);
|
|
driver.Clip = new Region (driver.Screen);
|
|
|
|
var view = new View
|
|
{
|
|
X = 10,
|
|
Y = 10,
|
|
Width = 20,
|
|
Height = 20,
|
|
Driver = driver,
|
|
Text = "Test",
|
|
CanFocus = true
|
|
};
|
|
view.BeginInit ();
|
|
view.EndInit ();
|
|
view.LayoutSubViews ();
|
|
view.SetFocus ();
|
|
|
|
view.Draw ();
|
|
|
|
// Text should use focus attribute
|
|
Point screenPos = view.ContentToScreen (Point.Empty);
|
|
Attribute expectedAttr = view.GetAttributeForRole (VisualRole.Focus);
|
|
|
|
Assert.Equal (expectedAttr, driver.Contents! [screenPos.Y, screenPos.X].Attribute);
|
|
}
|
|
|
|
[Fact]
|
|
public void DrawText_WithoutFocus_UsesNormalAttribute ()
|
|
{
|
|
IDriver driver = CreateFakeDriver (80, 25);
|
|
driver.Clip = new Region (driver.Screen);
|
|
|
|
var view = new View
|
|
{
|
|
X = 10,
|
|
Y = 10,
|
|
Width = 20,
|
|
Height = 20,
|
|
Driver = driver,
|
|
Text = "Test",
|
|
CanFocus = true
|
|
};
|
|
view.BeginInit ();
|
|
view.EndInit ();
|
|
view.LayoutSubViews ();
|
|
|
|
view.Draw ();
|
|
|
|
// Text should use normal attribute
|
|
Point screenPos = view.ContentToScreen (Point.Empty);
|
|
Attribute expectedAttr = view.GetAttributeForRole (VisualRole.Normal);
|
|
|
|
Assert.Equal (expectedAttr, driver.Contents! [screenPos.Y, screenPos.X].Attribute);
|
|
}
|
|
|
|
[Fact]
|
|
public void DrawText_SetsSubViewNeedsDraw ()
|
|
{
|
|
IDriver driver = CreateFakeDriver (80, 25);
|
|
driver.Clip = new Region (driver.Screen);
|
|
|
|
var view = new View
|
|
{
|
|
X = 10,
|
|
Y = 10,
|
|
Width = 20,
|
|
Height = 20,
|
|
Driver = driver,
|
|
Text = "Test"
|
|
};
|
|
var child = new View { X = 0, Y = 0, Width = 10, Height = 10 };
|
|
view.Add (child);
|
|
view.BeginInit ();
|
|
view.EndInit ();
|
|
view.LayoutSubViews ();
|
|
|
|
// Clear SubViewNeedsDraw
|
|
view.Draw ();
|
|
Assert.False (view.SubViewNeedsDraw);
|
|
|
|
// Call DrawText directly which should set SubViewNeedsDraw
|
|
view.DrawText ();
|
|
|
|
// SubViews need to be redrawn since text was drawn over them
|
|
Assert.True (view.SubViewNeedsDraw);
|
|
}
|
|
|
|
[Fact]
|
|
public void DrawingText_Event_Raised ()
|
|
{
|
|
IDriver driver = CreateFakeDriver (80, 25);
|
|
driver.Clip = new Region (driver.Screen);
|
|
|
|
bool eventRaised = false;
|
|
|
|
var view = new View
|
|
{
|
|
X = 10,
|
|
Y = 10,
|
|
Width = 20,
|
|
Height = 20,
|
|
Driver = driver,
|
|
Text = "Test"
|
|
};
|
|
view.BeginInit ();
|
|
view.EndInit ();
|
|
view.LayoutSubViews ();
|
|
|
|
view.DrawingText += (s, e) => eventRaised = true;
|
|
|
|
view.Draw ();
|
|
|
|
Assert.True (eventRaised);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region LineCanvas Tests
|
|
|
|
[Fact]
|
|
public void LineCanvas_InitiallyEmpty ()
|
|
{
|
|
var view = new View ();
|
|
|
|
Assert.NotNull (view.LineCanvas);
|
|
Assert.Equal (Rectangle.Empty, view.LineCanvas.Bounds);
|
|
}
|
|
|
|
[Fact]
|
|
public void RenderLineCanvas_DrawsLines ()
|
|
{
|
|
IDriver driver = CreateFakeDriver (80, 25);
|
|
driver.Clip = new Region (driver.Screen);
|
|
|
|
var view = new View
|
|
{
|
|
X = 10,
|
|
Y = 10,
|
|
Width = 20,
|
|
Height = 20,
|
|
Driver = driver
|
|
};
|
|
view.BeginInit ();
|
|
view.EndInit ();
|
|
view.LayoutSubViews ();
|
|
|
|
// Add a line to the canvas
|
|
Point screenPos = new Point (15, 15);
|
|
view.LineCanvas.AddLine (screenPos, 5, Orientation.Horizontal, LineStyle.Single);
|
|
|
|
view.RenderLineCanvas ();
|
|
|
|
// Verify the line was drawn (check for horizontal line character)
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
Assert.NotEqual (" ", driver.Contents! [screenPos.Y, screenPos.X + i].Grapheme);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void RenderLineCanvas_ClearsAfterRendering ()
|
|
{
|
|
IDriver driver = CreateFakeDriver (80, 25);
|
|
driver.Clip = new Region (driver.Screen);
|
|
|
|
var view = new View
|
|
{
|
|
X = 10,
|
|
Y = 10,
|
|
Width = 20,
|
|
Height = 20,
|
|
Driver = driver
|
|
};
|
|
view.BeginInit ();
|
|
view.EndInit ();
|
|
view.LayoutSubViews ();
|
|
|
|
// Add a line to the canvas
|
|
view.LineCanvas.AddLine (new Point (15, 15), 5, Orientation.Horizontal, LineStyle.Single);
|
|
|
|
Assert.NotEqual (Rectangle.Empty, view.LineCanvas.Bounds);
|
|
|
|
view.RenderLineCanvas ();
|
|
|
|
// LineCanvas should be cleared after rendering
|
|
Assert.Equal (Rectangle.Empty, view.LineCanvas.Bounds);
|
|
}
|
|
|
|
[Fact]
|
|
public void RenderLineCanvas_WithSuperViewRendersLineCanvas_DoesNotClear ()
|
|
{
|
|
IDriver driver = CreateFakeDriver (80, 25);
|
|
driver.Clip = new Region (driver.Screen);
|
|
|
|
var view = new View
|
|
{
|
|
X = 10,
|
|
Y = 10,
|
|
Width = 20,
|
|
Height = 20,
|
|
Driver = driver,
|
|
SuperViewRendersLineCanvas = true
|
|
};
|
|
view.BeginInit ();
|
|
view.EndInit ();
|
|
view.LayoutSubViews ();
|
|
|
|
// Add a line to the canvas
|
|
view.LineCanvas.AddLine (new Point (15, 15), 5, Orientation.Horizontal, LineStyle.Single);
|
|
|
|
Rectangle boundsBefore = view.LineCanvas.Bounds;
|
|
|
|
view.RenderLineCanvas ();
|
|
|
|
// LineCanvas should NOT be cleared when SuperViewRendersLineCanvas is true
|
|
Assert.Equal (boundsBefore, view.LineCanvas.Bounds);
|
|
}
|
|
|
|
[Fact]
|
|
public void SuperViewRendersLineCanvas_MergesWithParentCanvas ()
|
|
{
|
|
IDriver driver = CreateFakeDriver (80, 25);
|
|
driver.Clip = new Region (driver.Screen);
|
|
|
|
var parent = new View
|
|
{
|
|
X = 10,
|
|
Y = 10,
|
|
Width = 50,
|
|
Height = 50,
|
|
Driver = driver
|
|
};
|
|
var child = new View
|
|
{
|
|
X = 5,
|
|
Y = 5,
|
|
Width = 30,
|
|
Height = 30,
|
|
SuperViewRendersLineCanvas = true
|
|
};
|
|
parent.Add (child);
|
|
parent.BeginInit ();
|
|
parent.EndInit ();
|
|
parent.LayoutSubViews ();
|
|
|
|
// Add a line to child's canvas
|
|
child.LineCanvas.AddLine (new Point (20, 20), 5, Orientation.Horizontal, LineStyle.Single);
|
|
|
|
Assert.NotEqual (Rectangle.Empty, child.LineCanvas.Bounds);
|
|
Assert.Equal (Rectangle.Empty, parent.LineCanvas.Bounds);
|
|
|
|
parent.Draw ();
|
|
|
|
// Child's canvas should have been merged into parent's
|
|
// and child's canvas should be cleared
|
|
Assert.Equal (Rectangle.Empty, child.LineCanvas.Bounds);
|
|
}
|
|
|
|
[Fact]
|
|
public void OnRenderingLineCanvas_CanPreventRendering ()
|
|
{
|
|
IDriver driver = CreateFakeDriver (80, 25);
|
|
driver.Clip = new Region (driver.Screen);
|
|
|
|
var view = new TestView
|
|
{
|
|
X = 10,
|
|
Y = 10,
|
|
Width = 20,
|
|
Height = 20,
|
|
Driver = driver,
|
|
PreventRenderLineCanvas = true
|
|
};
|
|
view.BeginInit ();
|
|
view.EndInit ();
|
|
view.LayoutSubViews ();
|
|
|
|
// Add a line to the canvas
|
|
Point screenPos = new Point (15, 15);
|
|
view.LineCanvas.AddLine (screenPos, 5, Orientation.Horizontal, LineStyle.Single);
|
|
|
|
view.Draw ();
|
|
|
|
// When OnRenderingLineCanvas returns true, RenderLineCanvas is not called
|
|
// So the LineCanvas should still have lines (not cleared)
|
|
// BUT because SuperViewRendersLineCanvas is false (default), the LineCanvas
|
|
// gets cleared during the draw cycle anyway. We need to check that the
|
|
// line was NOT actually rendered to the driver.
|
|
bool lineRendered = true;
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
if (driver.Contents! [screenPos.Y, screenPos.X + i].Grapheme == " ")
|
|
{
|
|
lineRendered = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Assert.False (lineRendered);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SuperViewRendersLineCanvas Tests
|
|
|
|
[Fact]
|
|
public void SuperViewRendersLineCanvas_DefaultFalse ()
|
|
{
|
|
var view = new View ();
|
|
|
|
Assert.False (view.SuperViewRendersLineCanvas);
|
|
}
|
|
|
|
[Fact]
|
|
public void SuperViewRendersLineCanvas_CanBeSet ()
|
|
{
|
|
var view = new View { SuperViewRendersLineCanvas = true };
|
|
|
|
Assert.True (view.SuperViewRendersLineCanvas);
|
|
}
|
|
|
|
[Fact]
|
|
public void Draw_WithSuperViewRendersLineCanvas_SetsNeedsDraw ()
|
|
{
|
|
IDriver driver = CreateFakeDriver (80, 25);
|
|
driver.Clip = new Region (driver.Screen);
|
|
|
|
var parent = new View
|
|
{
|
|
X = 10,
|
|
Y = 10,
|
|
Width = 50,
|
|
Height = 50,
|
|
Driver = driver
|
|
};
|
|
var child = new View
|
|
{
|
|
X = 5,
|
|
Y = 5,
|
|
Width = 30,
|
|
Height = 30,
|
|
SuperViewRendersLineCanvas = true
|
|
};
|
|
parent.Add (child);
|
|
parent.BeginInit ();
|
|
parent.EndInit ();
|
|
parent.LayoutSubViews ();
|
|
|
|
// Draw once to clear NeedsDraw
|
|
parent.Draw ();
|
|
Assert.False (child.NeedsDraw);
|
|
|
|
// Draw again - child with SuperViewRendersLineCanvas should be redrawn
|
|
parent.Draw ();
|
|
|
|
// The child should have been set to NeedsDraw during DrawSubViews
|
|
// This is verified by the fact that it was drawn (we can't check NeedsDraw after Draw)
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Helper Test View
|
|
|
|
private class TestView : View
|
|
{
|
|
public bool PreventRenderLineCanvas { get; set; }
|
|
|
|
protected override bool OnRenderingLineCanvas ()
|
|
{
|
|
return PreventRenderLineCanvas || base.OnRenderingLineCanvas ();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|