Skip to content

Commit d946731

Browse files
committed
Fix TableControl OOM in ScrollablePanel and add ClearContents
Cap unbounded width in TableControl.GetLogicalContentSize to prevent OutOfMemoryException when hosted in ScrollablePanelControl. Add safety cap in AnsiConsoleHelper.AnsiEmptySpace as defense in depth. Add ClearContents method to ScrollablePanelControl. Add Table builder.
1 parent 41e9d9b commit d946731

5 files changed

Lines changed: 61 additions & 1 deletion

File tree

SharpConsoleUI/Builders/Controls.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,12 @@ public static FigleControlBuilder Figlet(string? text = null)
246246
/// <returns>A new progress bar builder</returns>
247247
public static ProgressBarBuilder ProgressBar() => new ProgressBarBuilder();
248248

249+
/// <summary>
250+
/// Creates a new table control builder
251+
/// </summary>
252+
/// <returns>A new table control builder</returns>
253+
public static TableControlBuilder Table() => new TableControlBuilder();
254+
249255
/// <summary>
250256
/// Creates a new panel builder for bordered content panels
251257
/// </summary>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// -----------------------------------------------------------------------
2+
// ConsoleEx - A simple console window system for .NET Core
3+
//
4+
// Author: Nikolaos Protopapas
5+
// Email: nikolaos.protopapas@gmail.com
6+
// License: MIT
7+
// -----------------------------------------------------------------------
8+
9+
namespace SharpConsoleUI.Configuration
10+
{
11+
/// <summary>
12+
/// Centralized default values for layout measurement and rendering width limits.
13+
/// </summary>
14+
public static class LayoutDefaults
15+
{
16+
/// <summary>
17+
/// Width used by GetLogicalContentSize when no explicit width is set.
18+
/// Prevents unbounded allocation when measuring Spectre renderables.
19+
/// </summary>
20+
public const int DefaultUnboundedMeasureWidth = 1000;
21+
22+
/// <summary>
23+
/// Hard safety cap for AnsiEmptySpace to prevent OOM from any caller
24+
/// passing an excessively large width.
25+
/// </summary>
26+
public const int MaxSafeRenderWidth = 10000;
27+
}
28+
}

SharpConsoleUI/Controls/ScrollablePanelControl.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,29 @@ public void RemoveControl(IWindowControl control)
996996
}
997997
}
998998

999+
/// <summary>
1000+
/// Removes all child controls from the panel.
1001+
/// </summary>
1002+
public void ClearContents()
1003+
{
1004+
if (_focusedChild is IFocusableControl fc)
1005+
fc.SetFocus(false, FocusReason.Programmatic);
1006+
_focusedChild = null;
1007+
_lastInternalFocusedChild = null;
1008+
1009+
foreach (var child in _children)
1010+
{
1011+
child.Container = null;
1012+
child.Dispose();
1013+
}
1014+
_children.Clear();
1015+
1016+
if (_hasFocus && !CanReceiveFocus)
1017+
SetFocus(false, FocusReason.Programmatic);
1018+
1019+
Invalidate(true);
1020+
}
1021+
9991022
/// <summary>
10001023
/// Gets the collection of child controls.
10011024
/// </summary>

SharpConsoleUI/Controls/TableControl.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,8 @@ public bool ProcessMouseEvent(MouseEventArgs args)
743743
/// <inheritdoc/>
744744
public System.Drawing.Size GetLogicalContentSize()
745745
{
746-
var constraints = new LayoutConstraints(0, int.MaxValue, 0, int.MaxValue);
746+
int maxWidth = _width ?? LayoutDefaults.DefaultUnboundedMeasureWidth;
747+
var constraints = new LayoutConstraints(0, maxWidth, 0, int.MaxValue);
747748
var size = MeasureDOM(constraints);
748749
return new System.Drawing.Size(size.Width, size.Height);
749750
}

SharpConsoleUI/Helpers/AnsiConsoleHelper.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
// License: MIT
77
// -----------------------------------------------------------------------
88

9+
using SharpConsoleUI.Configuration;
910
using Spectre.Console;
1011
using Spectre.Console.Rendering;
1112
using System;
@@ -66,6 +67,7 @@ public static string AnsiEmptySpace(int width, Color backgroundColor)
6667
{
6768
if (width <= 0)
6869
return string.Empty;
70+
width = Math.Min(width, LayoutDefaults.MaxSafeRenderWidth);
6971
return ConvertSpectreMarkupToAnsi($"{new string(' ', width)}", width, 1, false, backgroundColor, null)[0];
7072
}
7173

0 commit comments

Comments
 (0)