Fixes bugs where unnecessary Draw operations were happening in LayoutAndDraw. In some cases causing everything to always be drawn.

Refactored `NeedsDraw` logic into a modular implementation in `View.NeedsDraw.cs`, introducing methods to manage drawing states more effectively. Updated `Menus.cs` event handlers to include null checks for robustness. Improved margin drawing logic in `Margin.cs` with better performance and debugging assertions.

Added comprehensive unit tests in `NeedsDrawTests.cs` and `StaticDrawTests.cs` to validate the new drawing logic, including edge cases and static `View.Draw` behavior. Removed redundant tests from `ViewDrawingFlowTests.cs`.

Refactored diagnostic flags handling in `UICatalogRunnable.cs` for clarity. Performed general code cleanup, leveraging modern C# features and improving maintainability.
This commit is contained in:
Tig
2025-12-04 12:20:44 -07:00
parent 6a334bbf2e
commit 193a5873d1
8 changed files with 704 additions and 495 deletions

View File

@@ -6,222 +6,7 @@ namespace ViewBaseTests.Drawing;
public class ViewDrawingFlowTests () : FakeDriverBase
{
#region NeedsDraw Tests
[Fact]
public void NeedsDraw_InitiallyFalse_WhenNotVisible ()
{
var view = new View { Visible = false };
view.BeginInit ();
view.EndInit ();
Assert.False (view.NeedsDraw);
}
[Fact]
public void NeedsDraw_TrueAfterSetNeedsDraw ()
{
var view = new View { X = 0, Y = 0, Width = 10, Height = 10 };
view.BeginInit ();
view.EndInit ();
view.LayoutSubViews ();
view.SetNeedsDraw ();
Assert.True (view.NeedsDraw);
}
[Fact]
public void NeedsDraw_ClearedAfterDraw ()
{
IDriver driver = CreateFakeDriver (80, 25);
driver.Clip = new Region (driver.Screen);
var view = new View
{
X = 0,
Y = 0,
Width = 10,
Height = 10,
Driver = driver
};
view.BeginInit ();
view.EndInit ();
view.LayoutSubViews ();
view.SetNeedsDraw ();
Assert.True (view.NeedsDraw);
view.Draw ();
Assert.False (view.NeedsDraw);
}
[Fact]
public void SetNeedsDraw_WithRectangle_UpdatesNeedsDrawRect ()
{
var view = new View { Driver = CreateFakeDriver (), X = 0, Y = 0, Width = 20, Height = 20 };
view.BeginInit ();
view.EndInit ();
view.LayoutSubViews ();
// After layout, view will have NeedsDrawRect set to the viewport
// We need to clear it first
view.Draw ();
Assert.False (view.NeedsDraw);
Assert.Equal (Rectangle.Empty, view.NeedsDrawRect);
var rect = new Rectangle (5, 5, 10, 10);
view.SetNeedsDraw (rect);
Assert.True (view.NeedsDraw);
Assert.Equal (rect, view.NeedsDrawRect);
}
[Fact]
public void SetNeedsDraw_MultipleRectangles_Expands ()
{
IDriver driver = CreateFakeDriver (80, 25);
driver.Clip = new Region (driver.Screen);
var view = new View { X = 0, Y = 0, Width = 30, Height = 30, Driver = driver };
view.BeginInit ();
view.EndInit ();
view.LayoutSubViews ();
// After layout, clear NeedsDraw
view.Draw ();
Assert.False (view.NeedsDraw);
view.SetNeedsDraw (new Rectangle (5, 5, 10, 10));
view.SetNeedsDraw (new Rectangle (15, 15, 10, 10));
// Should expand to cover the entire viewport when we have overlapping regions
// The current implementation expands to viewport size
Rectangle expected = new Rectangle (0, 0, 30, 30);
Assert.Equal (expected, view.NeedsDrawRect);
}
[Fact]
public void SetNeedsDraw_NotVisible_DoesNotSet ()
{
var view = new View
{
X = 0,
Y = 0,
Width = 10,
Height = 10,
Visible = false
};
view.BeginInit ();
view.EndInit ();
view.SetNeedsDraw ();
Assert.False (view.NeedsDraw);
}
[Fact]
public void SetNeedsDraw_PropagatesToSuperView ()
{
var parent = new View { X = 0, Y = 0, Width = 50, Height = 50 };
var child = new View { X = 10, Y = 10, Width = 20, Height = 20 };
parent.Add (child);
parent.BeginInit ();
parent.EndInit ();
parent.LayoutSubViews ();
child.SetNeedsDraw ();
Assert.True (child.NeedsDraw);
Assert.True (parent.SubViewNeedsDraw);
}
[Fact]
public void SetNeedsDraw_SetsAdornmentsNeedsDraw ()
{
var view = new View { X = 0, Y = 0, Width = 20, Height = 20 };
view.Border!.Thickness = new Thickness (1);
view.Padding!.Thickness = new Thickness (1);
view.BeginInit ();
view.EndInit ();
view.LayoutSubViews ();
view.SetNeedsDraw ();
Assert.True (view.Border!.NeedsDraw);
Assert.True (view.Padding!.NeedsDraw);
}
#endregion
#region SubViewNeedsDraw Tests
[Fact]
public void SubViewNeedsDraw_InitiallyFalse ()
{
IDriver driver = CreateFakeDriver (80, 25);
driver.Clip = new Region (driver.Screen);
var view = new View { Width = 10, Height = 10, Driver = driver };
view.BeginInit ();
view.EndInit ();
view.Draw (); // Draw once to clear initial NeedsDraw
Assert.False (view.SubViewNeedsDraw);
}
[Fact]
public void SetSubViewNeedsDraw_PropagatesUp ()
{
var grandparent = new View { X = 0, Y = 0, Width = 100, Height = 100 };
var parent = new View { X = 10, Y = 10, Width = 50, Height = 50 };
var child = new View { X = 5, Y = 5, Width = 20, Height = 20 };
grandparent.Add (parent);
parent.Add (child);
grandparent.BeginInit ();
grandparent.EndInit ();
grandparent.LayoutSubViews ();
child.SetSubViewNeedsDraw ();
Assert.True (child.SubViewNeedsDraw);
Assert.True (parent.SubViewNeedsDraw);
Assert.True (grandparent.SubViewNeedsDraw);
}
[Fact]
public void SubViewNeedsDraw_ClearedAfterDraw ()
{
IDriver driver = CreateFakeDriver (80, 25);
driver.Clip = new Region (driver.Screen);
var parent = new View
{
X = 0,
Y = 0,
Width = 50,
Height = 50,
Driver = driver
};
var child = new View { X = 10, Y = 10, Width = 20, Height = 20 };
parent.Add (child);
parent.BeginInit ();
parent.EndInit ();
parent.LayoutSubViews ();
child.SetNeedsDraw ();
Assert.True (parent.SubViewNeedsDraw);
parent.Draw ();
Assert.False (parent.SubViewNeedsDraw);
Assert.False (child.SubViewNeedsDraw);
}
#endregion
#region Draw Visibility Tests
[Fact]