Fixes #4216. Legacy drivers aren't refreshing the screen correctly on view drag

This commit is contained in:
BDisp
2025-07-28 03:27:16 +01:00
parent 847123aa00
commit 08dfea67cf
4 changed files with 74 additions and 26 deletions

View File

@@ -535,7 +535,7 @@ public static partial class Application // Run (Begin, Run, End, Stop)
return firstIteration;
}
LayoutAndDraw ();
LayoutAndDraw (TopLevels.Any (v => v.NeedsLayout || v.NeedsDraw));
if (PositionCursor ())
{

View File

@@ -1,5 +1,6 @@
#nullable enable
using System.ComponentModel;
using System.Diagnostics;
namespace Terminal.Gui.ViewBase;
@@ -111,6 +112,26 @@ public partial class View // Drawing APIs
Border?.AdvanceDrawIndicator ();
ClearNeedsDraw ();
if (this is not Adornment && SuperView is not Adornment)
{
// Parent
Debug.Assert (Margin!.Parent == this);
Debug.Assert (Border!.Parent == this);
Debug.Assert (Padding!.Parent == this);
// SubViewNeedsDraw is set to false by ClearNeedsDraw.
Debug.Assert (SubViewNeedsDraw == false);
Debug.Assert (Margin!.SubViewNeedsDraw == false);
Debug.Assert (Border!.SubViewNeedsDraw == false);
Debug.Assert (Padding!.SubViewNeedsDraw == false);
// NeedsDraw is set to false by ClearNeedsDraw.
Debug.Assert (NeedsDraw == false);
Debug.Assert (Margin!.NeedsDraw == false);
Debug.Assert (Border!.NeedsDraw == false);
Debug.Assert (Padding!.NeedsDraw == false);
}
}
// ------------------------------------
@@ -131,6 +152,11 @@ public partial class View // Drawing APIs
private void DoDrawAdornmentsSubViews ()
{
if (Border?.NeedsLayout == true)
{
Border.Layout ();
}
// NOTE: We do not support subviews of Margin?
if (Border?.SubViews is { } && Border.Thickness != Thickness.Empty)
@@ -151,6 +177,11 @@ public partial class View // Drawing APIs
SetClip (saved);
}
if (Padding?.NeedsLayout == true)
{
Padding.Layout ();
}
if (Padding?.SubViews is { } && Padding.Thickness != Thickness.Empty)
{
foreach (View subview in Padding.SubViews)
@@ -720,8 +751,7 @@ public partial class View // Drawing APIs
/// </remarks>
public bool NeedsDraw
{
// TODO: Figure out if we can decouple NeedsDraw from NeedsLayout.
get => Visible && (NeedsDrawRect != Rectangle.Empty || NeedsLayout);
get => Visible && (NeedsDrawRect != Rectangle.Empty || Margin?.NeedsDraw == true || Border?.NeedsDraw == true || Padding?.NeedsDraw == true);
set
{
if (value)
@@ -807,7 +837,7 @@ public partial class View // Drawing APIs
}
// There was multiple enumeration error here, so calling ToArray - probably a stop gap
foreach (View subview in InternalSubViews)
foreach (View subview in InternalSubViews.ToArray ())
{
if (subview.Frame.IntersectsWith (viewPortRelativeRegion))
{
@@ -846,17 +876,17 @@ public partial class View // Drawing APIs
NeedsDrawRect = Rectangle.Empty;
SubViewNeedsDraw = false;
if (Margin is { } && Margin.Thickness != Thickness.Empty)
if (Margin is { } && (Margin.Thickness != Thickness.Empty || Margin.SubViewNeedsDraw || Margin.NeedsDraw))
{
Margin?.ClearNeedsDraw ();
}
if (Border is { } && Border.Thickness != Thickness.Empty)
if (Border is { } && (Border.Thickness != Thickness.Empty || Border.SubViewNeedsDraw || Border.NeedsDraw))
{
Border?.ClearNeedsDraw ();
}
if (Padding is { } && Padding.Thickness != Thickness.Empty)
if (Padding is { } && (Padding.Thickness != Thickness.Empty || Padding.SubViewNeedsDraw || Padding.NeedsDraw))
{
Padding?.ClearNeedsDraw ();
}
@@ -876,7 +906,6 @@ public partial class View // Drawing APIs
{
LineCanvas.Clear ();
}
}
#endregion NeedsDraw

View File

@@ -419,7 +419,10 @@ public partial class View // Layout APIs
{
LayoutSubViews ();
// Debug.Assert(!NeedsLayout);
// A layout was performed so a draw is needed
// NeedsLayout may still be true if a dependent View still needs layout after SubViewsLaidOut event
SetNeedsDraw ();
return true;
}

View File

@@ -10,7 +10,7 @@ public class NeedsDrawTests
View view = new () { Width = 0, Height = 0 };
view.BeginInit ();
view.EndInit ();
Assert.True (view.NeedsDraw);
Assert.False (view.NeedsDraw);
//Assert.False (view.SubViewNeedsDraw);
}
@@ -70,14 +70,16 @@ public class NeedsDrawTests
view.NeedsDraw = false;
view.BeginInit ();
Assert.True (view.NeedsDraw); // Because layout is still needed
Assert.False (view.NeedsDraw); // Because layout is still needed
view.Layout ();
Assert.False (view.NeedsDraw);
// NeedsDraw is true after layout and NeedsLayout is false if SubViewsLaidOut doesn't call SetNeedsLayout
Assert.True (view.NeedsDraw);
Assert.False (view.NeedsLayout);
}
[Fact]
public void NeedsDraw_False_After_EndInit ()
public void NeedsDraw_True_After_EndInit_Where_Call_Layout ()
{
var view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
Assert.True (view.NeedsDraw);
@@ -96,7 +98,7 @@ public class NeedsDrawTests
}
[Fact]
public void NeedsDraw_After_SetLayoutNeeded ()
public void NeedsDraw_After_SetLayoutNeeded_And_Layout ()
{
var view = new View { Width = 2, Height = 2 };
Assert.True (view.NeedsDraw);
@@ -107,8 +109,12 @@ public class NeedsDrawTests
Assert.False (view.NeedsLayout);
view.SetNeedsLayout ();
Assert.True (view.NeedsDraw);
Assert.False (view.NeedsDraw);
Assert.True (view.NeedsLayout);
view.Layout ();
Assert.True (view.NeedsDraw);
Assert.False (view.NeedsLayout);
}
[Fact]
@@ -121,21 +127,27 @@ public class NeedsDrawTests
Assert.False (view.NeedsDraw);
Assert.False (view.NeedsLayout);
// SRL won't change anything since the view is Absolute
// SRL won't change anything since the view frame wasn't changed
view.SetRelativeLayout (Application.Screen.Size);
Assert.False (view.NeedsDraw);
view.SetNeedsLayout ();
// SRL won't change anything since the view is Absolute
// SRL won't change anything since the view frame wasn't changed
// SRL doesn't depend on NeedsLayout, but LayoutSubViews does
view.SetRelativeLayout (Application.Screen.Size);
Assert.False (view.NeedsDraw);
Assert.True (view.NeedsLayout);
view.Layout ();
Assert.True (view.NeedsDraw);
Assert.False (view.NeedsLayout);
view.NeedsDraw = false;
// SRL won't change anything since the view is Absolute. However, Layout has not been called
// SRL won't change anything since the view frame wasn't changed. However, Layout has not been called
view.SetRelativeLayout (new (10, 10));
Assert.True (view.NeedsDraw);
Assert.False (view.NeedsDraw);
}
[Fact]
@@ -149,17 +161,20 @@ public class NeedsDrawTests
Width = Dim.Fill (),
Height = Dim.Fill ()
};
Assert.True (superView.NeedsDraw);
// A layout wasn't called yet, so NeedsDraw is still empty
Assert.False (superView.NeedsDraw);
superView.Add (view);
Assert.True (view.NeedsDraw);
Assert.True (superView.NeedsDraw);
// A layout wasn't called yet, so NeedsDraw is still empty
Assert.False (view.NeedsDraw);
Assert.False (superView.NeedsDraw);
superView.BeginInit ();
Assert.True (view.NeedsDraw);
Assert.True (superView.NeedsDraw);
Assert.False (view.NeedsDraw);
Assert.False (superView.NeedsDraw);
superView.EndInit ();
superView.EndInit (); // Call Layout
Assert.True (view.NeedsDraw);
Assert.True (superView.NeedsDraw);
@@ -177,9 +192,10 @@ public class NeedsDrawTests
Width = Dim.Fill (),
Height = Dim.Fill ()
};
Assert.True (superView.NeedsDraw);
Assert.False (superView.NeedsDraw);
superView.Layout ();
Assert.True (superView.NeedsDraw);
superView.NeedsDraw = false;
superView.SetRelativeLayout (new (10, 10));