diff --git a/Terminal.Gui/Drawing/Region.cs b/Terminal.Gui/Drawing/Region.cs
index c3ef1d61b2..abd06dcbae 100644
--- a/Terminal.Gui/Drawing/Region.cs
+++ b/Terminal.Gui/Drawing/Region.cs
@@ -1,21 +1,25 @@
-///
+#nullable enable
+
+namespace Terminal.Gui;
+
+///
/// Represents a region composed of one or more rectangles, providing methods for union, intersection, exclusion, and
/// complement operations.
///
public class Region : IDisposable
{
- private List _rectangles;
+ private List? _rectangles;
///
/// Initializes a new instance of the class.
///
- public Region () { _rectangles = new (); }
+ public Region () { _rectangles = []; }
///
/// Initializes a new instance of the class with the specified rectangle.
///
/// The initial rectangle for the region.
- public Region (Rectangle rectangle) { _rectangles = new () { rectangle }; }
+ public Region (Rectangle rectangle) { _rectangles = [rectangle]; }
///
/// Adds the specified rectangle to the region.
@@ -23,7 +27,7 @@ public class Region : IDisposable
/// The rectangle to add to the region.
public void Union (Rectangle rectangle)
{
- _rectangles.Add (rectangle);
+ _rectangles!.Add (rectangle);
_rectangles = MergeRectangles (_rectangles);
}
@@ -31,10 +35,13 @@ public void Union (Rectangle rectangle)
/// Adds the specified region to this region.
///
/// The region to add to this region.
- public void Union (Region region)
+ public void Union (Region? region)
{
- _rectangles.AddRange (region._rectangles);
- _rectangles = MergeRectangles (_rectangles);
+ if (region is { })
+ {
+ _rectangles!.AddRange (region._rectangles!);
+ _rectangles = MergeRectangles (_rectangles);
+ }
}
///
@@ -43,20 +50,23 @@ public void Union (Region region)
/// The rectangle to intersect with the region.
public void Intersect (Rectangle rectangle)
{
- _rectangles = _rectangles.Select (r => Rectangle.Intersect (r, rectangle)).Where (r => !r.IsEmpty).ToList ();
+ _rectangles = _rectangles!.Select (r => Rectangle.Intersect (r, rectangle)).Where (r => !r.IsEmpty).ToList ();
}
///
/// Updates the region to be the intersection of itself with the specified region.
///
/// The region to intersect with this region.
- public void Intersect (Region region)
+ public void Intersect (Region? region)
{
- List intersections = new List ();
+ List intersections = [];
+
+ // Null is same as empty region
+ region ??= new ();
- foreach (Rectangle rect1 in _rectangles)
+ foreach (Rectangle rect1 in _rectangles!)
{
- foreach (Rectangle rect2 in region._rectangles)
+ foreach (Rectangle rect2 in region!._rectangles!)
{
Rectangle intersected = Rectangle.Intersect (rect1, rect2);
@@ -74,17 +84,20 @@ public void Intersect (Region region)
/// Removes the specified rectangle from the region.
///
/// The rectangle to exclude from the region.
- public void Exclude (Rectangle rectangle) { _rectangles = _rectangles.SelectMany (r => SubtractRectangle (r, rectangle)).ToList (); }
+ public void Exclude (Rectangle rectangle) { _rectangles = _rectangles!.SelectMany (r => SubtractRectangle (r, rectangle)).ToList (); }
///
/// Removes the portion of the specified region from this region.
///
/// The region to exclude from this region.
- public void Exclude (Region region)
+ public void Exclude (Region? region)
{
- foreach (Rectangle rect in region._rectangles)
+ // Null is same as empty region
+ region ??= new ();
+
+ foreach (Rectangle rect in region._rectangles!)
{
- _rectangles = _rectangles.SelectMany (r => SubtractRectangle (r, rect)).ToList ();
+ _rectangles = _rectangles!.SelectMany (r => SubtractRectangle (r, rect)).ToList ();
}
}
@@ -94,14 +107,14 @@ public void Exclude (Region region)
/// The bounding rectangle to use for complementing the region.
public void Complement (Rectangle bounds)
{
- if (bounds.IsEmpty || _rectangles.Count == 0)
+ if (bounds.IsEmpty || _rectangles!.Count == 0)
{
- _rectangles.Clear ();
+ _rectangles!.Clear ();
return;
}
- List complementRectangles = new List { bounds };
+ List complementRectangles = [bounds];
foreach (Rectangle rect in _rectangles)
{
@@ -118,7 +131,7 @@ public void Complement (Rectangle bounds)
public Region Clone ()
{
var clone = new Region ();
- clone._rectangles = new (_rectangles);
+ clone._rectangles = [.. _rectangles!];
return clone;
}
@@ -129,7 +142,7 @@ public Region Clone ()
/// A that bounds the region.
public Rectangle GetBounds ()
{
- if (_rectangles.Count == 0)
+ if (_rectangles!.Count == 0)
{
return Rectangle.Empty;
}
@@ -146,7 +159,7 @@ public Rectangle GetBounds ()
/// Determines whether the region is empty.
///
/// true if the region is empty; otherwise, false.
- public bool IsEmpty () { return !_rectangles.Any (); }
+ public bool IsEmpty () { return !_rectangles!.Any (); }
///
/// Determines whether the specified point is contained within the region.
@@ -154,20 +167,20 @@ public Rectangle GetBounds ()
/// The x-coordinate of the point.
/// The y-coordinate of the point.
/// true if the point is contained within the region; otherwise, false.
- public bool Contains (int x, int y) { return _rectangles.Any (r => r.Contains (x, y)); }
+ public bool Contains (int x, int y) { return _rectangles!.Any (r => r.Contains (x, y)); }
///
/// Determines whether the specified rectangle is contained within the region.
///
/// The rectangle to check for containment.
/// true if the rectangle is contained within the region; otherwise, false.
- public bool Contains (Rectangle rectangle) { return _rectangles.Any (r => r.Contains (rectangle)); }
+ public bool Contains (Rectangle rectangle) { return _rectangles!.Any (r => r.Contains (rectangle)); }
///
/// Returns an array of rectangles that represent the region.
///
/// An array of objects that make up the region.
- public Rectangle [] GetRegionScans () { return _rectangles.ToArray (); }
+ public Rectangle [] GetRegionScans () { return _rectangles!.ToArray (); }
///
/// Offsets all rectangles in the region by the specified amounts.
@@ -176,10 +189,10 @@ public Rectangle GetBounds ()
/// The amount to offset along the y-axis.
public void Offset (int offsetX, int offsetY)
{
- for (int i = 0; i < _rectangles.Count; i++)
+ for (var i = 0; i < _rectangles!.Count; i++)
{
- var rect = _rectangles [i];
- _rectangles [i] = new Rectangle (rect.Left + offsetX, rect.Top + offsetY, rect.Width, rect.Height);
+ Rectangle rect = _rectangles [i];
+ _rectangles [i] = new (rect.Left + offsetX, rect.Top + offsetY, rect.Width, rect.Height);
}
}
@@ -188,11 +201,11 @@ public void Offset (int offsetX, int offsetY)
///
/// The list of rectangles to merge.
/// A list of merged rectangles.
- private List MergeRectangles (List rectangles)
+ private static List MergeRectangles (List rectangles)
{
// Simplified merging logic: this does not handle all edge cases for merging overlapping rectangles.
// For a full implementation, a plane sweep algorithm or similar would be needed.
- List merged = new List (rectangles);
+ List merged = [.. rectangles];
bool mergedAny;
do
@@ -230,7 +243,7 @@ private List MergeRectangles (List rectangles)
/// The original rectangle.
/// The rectangle to subtract from the original.
/// An enumerable collection of resulting rectangles after subtraction.
- private IEnumerable SubtractRectangle (Rectangle original, Rectangle subtract)
+ private static IEnumerable SubtractRectangle (Rectangle original, Rectangle subtract)
{
if (!original.IntersectsWith (subtract))
{
@@ -279,5 +292,5 @@ private IEnumerable SubtractRectangle (Rectangle original, Rectangle
///
/// Releases all resources used by the .
///
- public void Dispose () { _rectangles.Clear (); }
+ public void Dispose () { _rectangles!.Clear (); }
}
diff --git a/Terminal.Gui/Drawing/Thickness.cs b/Terminal.Gui/Drawing/Thickness.cs
index 6aa2088e5a..ed73b3913c 100644
--- a/Terminal.Gui/Drawing/Thickness.cs
+++ b/Terminal.Gui/Drawing/Thickness.cs
@@ -235,6 +235,19 @@ public Rectangle GetInside (Rectangle rect)
return new (x, y, width, height);
}
+ ///
+ /// Returns a region describing the thickness.
+ ///
+ /// The source rectangle
+ ///
+ public Region AsRegion (Rectangle rect)
+ {
+ Region region = new Region (rect);
+ region.Exclude (GetInside (rect));
+
+ return region;
+ }
+
///
/// Gets the total width of the left and right sides of the rectangle. Sets the width of the left and right sides
/// of the rectangle to half the specified value.
diff --git a/Terminal.Gui/View/View.Drawing.Clipping.cs b/Terminal.Gui/View/View.Drawing.Clipping.cs
index 30f8b703c6..509854f99a 100644
--- a/Terminal.Gui/View/View.Drawing.Clipping.cs
+++ b/Terminal.Gui/View/View.Drawing.Clipping.cs
@@ -86,7 +86,7 @@ public static void SetClip (Region? region)
///
/// The current Clip, which can be then re-applied
///
- internal Region? ClipFrame ()
+ internal Region? AddFrameToClip ()
{
if (Driver is null)
{
@@ -133,7 +133,7 @@ public static void SetClip (Region? region)
///
/// The current Clip, which can be then re-applied
///
- public Region? ClipViewport ()
+ public Region? AddViewportToClip ()
{
if (Driver is null)
{
diff --git a/Terminal.Gui/View/View.Drawing.Primitives.cs b/Terminal.Gui/View/View.Drawing.Primitives.cs
index a7f1c8522d..153f12e644 100644
--- a/Terminal.Gui/View/View.Drawing.Primitives.cs
+++ b/Terminal.Gui/View/View.Drawing.Primitives.cs
@@ -137,7 +137,7 @@ public void FillRect (Rectangle rect, Color? color = null)
return;
}
- Region prevClip = ClipViewport ();
+ Region prevClip = AddViewportToClip ();
Rectangle toClear = ViewportToScreen (rect);
Attribute prev = SetAttribute (new (color ?? GetNormalColor ().Background));
Driver.FillRect (toClear);
@@ -155,7 +155,7 @@ public void FillRect (Rectangle rect, Rune rune)
return;
}
- Region prevClip = ClipViewport ();
+ Region prevClip = AddViewportToClip ();
Rectangle toClear = ViewportToScreen (rect);
Driver.FillRect (toClear, rune);
SetClip (prevClip);
diff --git a/Terminal.Gui/View/View.Drawing.cs b/Terminal.Gui/View/View.Drawing.cs
index adea35c36a..b5e2486a69 100644
--- a/Terminal.Gui/View/View.Drawing.cs
+++ b/Terminal.Gui/View/View.Drawing.cs
@@ -1,5 +1,6 @@
#nullable enable
using System.ComponentModel;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Terminal.Gui;
@@ -47,27 +48,38 @@ public void Draw ()
return;
}
- Region? saved = GetClip ();
+ Region? originalClip = GetClip ();
+
+ Region? contentClip = null;
// TODO: This can be further optimized by checking NeedsDraw below and only clearing, drawing text, drawing content, etc. if it is true.
- if (NeedsDraw || SubViewNeedsDraw)
+ if (NeedsDraw || SubViewNeedsDraw || ViewportSettings.HasFlag(ViewportSettings.Transparent))
{
// Draw the Border and Padding.
- // We clip to the frame to prevent drawing outside the frame.
- saved = ClipFrame ();
-
+ if (this is Adornment)
+ {
+ AddFrameToClip ();
+ }
+ else
+ {
+ // Set the clip to be just the thicknesses of the adornments
+ // TODO: Put this union logic in a method on View?
+ Region? clipAdornments = Margin!.Thickness.AsRegion (Margin!.FrameToScreen ());
+ clipAdornments?.Union (Border!.Thickness.AsRegion (Border!.FrameToScreen ()));
+ clipAdornments?.Union (Padding!.Thickness.AsRegion (Padding!.FrameToScreen ()));
+ clipAdornments?.Intersect (originalClip);
+ SetClip (clipAdornments);
+ }
DoDrawBorderAndPadding ();
- SetClip (saved);
+ SetClip (originalClip);
- // Draw the content within the Viewport
+ // Clear the Viewport
// By default, we clip to the viewport preventing drawing outside the viewport
// We also clip to the content, but if a developer wants to draw outside the viewport, they can do
// so via settings. SetClip honors the ViewportSettings.DisableVisibleContentClipping flag.
// Get our Viewport in screen coordinates
+ originalClip = AddViewportToClip ();
- saved = ClipViewport ();
-
- // Clear the viewport
// TODO: Simplify/optimize SetAttribute system.
DoSetAttribute ();
DoClearViewport ();
@@ -87,11 +99,28 @@ public void Draw ()
DoSetAttribute ();
DoDrawContent ();
+ // TODO: This flag may not be needed. Just returning true from OnClearViewport may be sufficient.
+ if (ViewportSettings.HasFlag (ViewportSettings.Transparent) && _subviews is { Count: > 0 })
+ {
+ contentClip = new Region ();
+
+ contentClip.Union(ViewportToScreen (new Rectangle(Viewport.Location, TextFormatter.FormatAndGetSize())));
+ // TODO: Move this into DrawSubviews somehow
+ foreach (View view in _subviews?.Where (view => view.Visible).Reverse ())
+ {
+ contentClip.Union (view.FrameToScreen ());
+ }
+ }
+ else
+ {
+ contentClip = new (ViewportToScreen (new Rectangle (Point.Empty, Viewport.Size)));
+ }
+
// Restore the clip before rendering the line canvas and adornment subviews
// because they may draw outside the viewport.
- SetClip (saved);
+ SetClip (originalClip);
- saved = ClipFrame ();
+ originalClip = AddFrameToClip ();
// Draw the line canvas
DoRenderLineCanvas ();
@@ -113,9 +142,6 @@ public void Draw ()
// We're done drawing
DoDrawComplete ();
- // QUESTION: Should this go before DoDrawComplete? What is more correct?
- SetClip (saved);
-
// Exclude this view (not including Margin) from the Clip
if (this is not Adornment)
{
@@ -126,7 +152,24 @@ public void Draw ()
borderFrame = Border.FrameToScreen ();
}
- ExcludeFromClip (borderFrame);
+ if (ViewportSettings.HasFlag (ViewportSettings.Transparent) && contentClip is { })
+ {
+ Region? saved = originalClip!.Clone ();
+
+ saved.Exclude (Border!.Thickness.AsRegion (Border!.FrameToScreen ()));
+ saved.Exclude (Padding!.Thickness.AsRegion (Padding!.FrameToScreen ()));
+ saved.Exclude (contentClip);
+ SetClip (saved);
+ }
+ else
+ {
+ SetClip (originalClip);
+ ExcludeFromClip (borderFrame);
+ }
+ }
+ else
+ {
+ SetClip (originalClip);
}
}
@@ -144,10 +187,10 @@ private void DoDrawBorderAndPaddingSubViews ()
subview.SetNeedsDraw ();
}
- LineCanvas.Exclude (new (subview.FrameToScreen()));
+ LineCanvas.Exclude (new (subview.FrameToScreen ()));
}
- Region? saved = Border?.ClipFrame ();
+ Region? saved = Border?.AddFrameToClip ();
Border?.DoDrawSubviews ();
SetClip (saved);
}
@@ -159,7 +202,7 @@ private void DoDrawBorderAndPaddingSubViews ()
subview.SetNeedsDraw ();
}
- Region? saved = Padding?.ClipFrame ();
+ Region? saved = Padding?.AddFrameToClip ();
Padding?.DoDrawSubviews ();
SetClip (saved);
}
@@ -170,6 +213,7 @@ private void DoDrawBorderAndPadding ()
if (Margin?.NeedsLayout == true)
{
Margin.NeedsLayout = false;
+ // BUGBUG: This should not use ClearFrame as that clears the insides too
Margin?.ClearFrame ();
Margin?.Parent?.SetSubViewNeedsDraw ();
}
@@ -293,6 +337,11 @@ public void SetNormalAttribute ()
private void DoClearViewport ()
{
+ if (ViewportSettings.HasFlag (ViewportSettings.Transparent))
+ {
+ return;
+ }
+
if (OnClearingViewport ())
{
return;
diff --git a/Terminal.Gui/View/ViewportSettings.cs b/Terminal.Gui/View/ViewportSettings.cs
index db2c23f4fe..c687da7513 100644
--- a/Terminal.Gui/View/ViewportSettings.cs
+++ b/Terminal.Gui/View/ViewportSettings.cs
@@ -13,7 +13,7 @@ public enum ViewportSettings
///
/// No settings.
///
- None = 0,
+ None = 0b_0000,
///
/// If set, .X can be set to negative values enabling scrolling beyond the left of
@@ -23,7 +23,7 @@ public enum ViewportSettings
/// When not set, .X is constrained to positive values.
///
///
- AllowNegativeX = 1,
+ AllowNegativeX = 0b_0001,
///
/// If set, .Y can be set to negative values enabling scrolling beyond the top of the
@@ -32,7 +32,7 @@ public enum ViewportSettings
/// When not set, .Y is constrained to positive values.
///
///
- AllowNegativeY = 2,
+ AllowNegativeY = 0b_0010,
///
/// If set, .Size can be set to negative coordinates enabling scrolling beyond the
@@ -58,7 +58,7 @@ public enum ViewportSettings
/// The practical effect of this is that the last column of the content will always be visible.
///
///
- AllowXGreaterThanContentWidth = 4,
+ AllowXGreaterThanContentWidth = 0b_0100,
///
/// If set, .Y can be set values greater than
@@ -74,7 +74,7 @@ public enum ViewportSettings
/// The practical effect of this is that the last row of the content will always be visible.
///
///
- AllowYGreaterThanContentHeight = 8,
+ AllowYGreaterThanContentHeight = 0b_1000,
///
/// If set, .Location can be set values greater than
@@ -102,7 +102,7 @@ public enum ViewportSettings
/// This can be useful in infinite scrolling scenarios.
///
///
- AllowNegativeXWhenWidthGreaterThanContentWidth = 16,
+ AllowNegativeXWhenWidthGreaterThanContentWidth = 0b_0001_0000,
///
/// If set and .Height is greater than
@@ -117,7 +117,7 @@ public enum ViewportSettings
/// This can be useful in infinite scrolling scenarios.
///
///
- AllowNegativeYWhenHeightGreaterThanContentHeight = 32,
+ AllowNegativeYWhenHeightGreaterThanContentHeight = 0b_0010_0000,
///
/// The combination of and
@@ -129,7 +129,7 @@ public enum ViewportSettings
/// By default, clipping is applied to the . Setting this flag will cause clipping to be
/// applied to the visible content area.
///
- ClipContentOnly = 64,
+ ClipContentOnly = 0b_0100_0000,
///
/// If set will clear only the portion of the content
@@ -138,5 +138,11 @@ public enum ViewportSettings
/// must be set for this setting to work (clipping beyond the visible area must be
/// disabled).
///
- ClearContentOnly = 128
+ ClearContentOnly = 0b_1000_0000,
+
+ ///
+ /// If set, any will not be cleared when the View is drawn and the clip region
+ /// will be set to clip the View's and .
+ ///
+ Transparent = 0b_0001_0000_0000,
}
diff --git a/Terminal.Gui/Views/CharMap/CharMap.cs b/Terminal.Gui/Views/CharMap/CharMap.cs
index b49c8c4e6f..a42d39c69e 100644
--- a/Terminal.Gui/Views/CharMap/CharMap.cs
+++ b/Terminal.Gui/Views/CharMap/CharMap.cs
@@ -97,8 +97,8 @@ public CharMap ()
};
// Set up the vertical scrollbar. Turn off AutoShow since it's always visible.
- VerticalScrollBar.AutoShow = false;
- VerticalScrollBar.Visible = true; // Force always visible
+ VerticalScrollBar.AutoShow = true;
+ VerticalScrollBar.Visible = false; // Force always visible
VerticalScrollBar.X = Pos.AnchorEnd ();
VerticalScrollBar.Y = HEADER_HEIGHT; // Header
}
diff --git a/UICatalog/Scenarios/AllViewsTester.cs b/UICatalog/Scenarios/AllViewsTester.cs
index d982b2b899..c6f9136796 100644
--- a/UICatalog/Scenarios/AllViewsTester.cs
+++ b/UICatalog/Scenarios/AllViewsTester.cs
@@ -18,10 +18,9 @@ public class AllViewsTester : Scenario
private Dictionary? _viewClasses;
private ListView? _classListView;
private AdornmentsEditor? _adornmentsEditor;
-
private ArrangementEditor? _arrangementEditor;
-
private LayoutEditor? _layoutEditor;
+ private ViewportSettingsEditor? _viewportSettingsEditor;
private FrameView? _settingsPane;
private RadioGroup? _orientation;
private string _demoText = "This, that, and the other thing.";
@@ -133,13 +132,27 @@ public override void Main ()
AutoSelectAdornments = false,
SuperViewRendersLineCanvas = true
};
- _layoutEditor.Border!.Thickness = new (1);
+ _layoutEditor.Border!.Thickness = new (1, 1, 1, 0);
+
+ _viewportSettingsEditor = new ()
+ {
+ Title = "ViewportSettings [_5]",
+ X = Pos.Right (_arrangementEditor) - 1,
+ Y = Pos.Bottom (_layoutEditor) - Pos.Func (() => _layoutEditor.Frame.Height == 1 ? 0 : 1),
+ Width = Dim.Width (_layoutEditor),
+ Height = Dim.Auto (),
+ CanFocus = true,
+ AutoSelectViewToEdit = false,
+ AutoSelectAdornments = false,
+ SuperViewRendersLineCanvas = true
+ };
+ _viewportSettingsEditor.Border!.Thickness = new (1, 1, 1, 1);
_settingsPane = new ()
{
- Title = "Settings [_5]",
+ Title = "Misc Settings [_6]",
X = Pos.Right (_adornmentsEditor) - 1,
- Y = Pos.Bottom (_layoutEditor) - Pos.Func (() => _layoutEditor.Frame.Height == 1 ? 0 : 1),
+ Y = Pos.Bottom (_viewportSettingsEditor) - Pos.Func (() => _viewportSettingsEditor.Frame.Height == 1 ? 0 : 1),
Width = Dim.Width (_layoutEditor),
Height = Dim.Auto (),
CanFocus = true,
@@ -237,7 +250,7 @@ public override void Main ()
_hostPane.Padding.Diagnostics = ViewDiagnosticFlags.Ruler;
_hostPane.Padding.ColorScheme = app.ColorScheme;
- app.Add (_classListView, _adornmentsEditor, _arrangementEditor, _layoutEditor, _settingsPane, _eventLog, _hostPane);
+ app.Add (_classListView, _adornmentsEditor, _arrangementEditor, _layoutEditor, _viewportSettingsEditor, _settingsPane, _eventLog, _hostPane);
app.Initialized += App_Initialized;
@@ -306,6 +319,7 @@ private void CreateCurrentView (Type type)
_hostPane!.Add (_curView);
_layoutEditor!.ViewToEdit = _curView;
+ _viewportSettingsEditor!.ViewToEdit = _curView;
_arrangementEditor!.ViewToEdit = _curView;
_curView.SetNeedsLayout ();
}
@@ -318,6 +332,7 @@ private void DisposeCurrentView ()
_curView.SubviewsLaidOut -= CurrentView_LayoutComplete;
_hostPane!.Remove (_curView);
_layoutEditor!.ViewToEdit = null;
+ _viewportSettingsEditor!.ViewToEdit = null;
_arrangementEditor!.ViewToEdit = null;
_curView.Dispose ();
diff --git a/UICatalog/Scenarios/Arrangement.cs b/UICatalog/Scenarios/Arrangement.cs
index 33ac9ee84c..b8d06b1827 100644
--- a/UICatalog/Scenarios/Arrangement.cs
+++ b/UICatalog/Scenarios/Arrangement.cs
@@ -185,6 +185,29 @@ public override void Main ()
};
testFrame.Add (datePicker);
+ TransparentView transparentView = new ()
+ {
+ Id = "transparentView",
+ X = 5,
+ Y = 11,
+ Width = 35,
+ Height = 15
+ };
+
+ transparentView.Add (
+ new TransparentView ()
+ {
+ Title = "Transparent SubView",
+ Text = "Transparent SubView",
+ Id = "transparentSubView",
+ X = 10,
+ Y = 10,
+ Width = 10,
+ Height = 5
+
+ });
+ testFrame.Add (transparentView);
+
adornmentsEditor.AutoSelectSuperView = testFrame;
arrangementEditor.AutoSelectSuperView = testFrame;
@@ -299,3 +322,26 @@ public override List GetDemoKeyStrokes ()
return keys;
}
}
+
+public class TransparentView : FrameView
+{
+ public TransparentView ()
+ {
+ Title = "Transparent";
+ base.Text = "Text";
+ Arrangement = ViewArrangement.Overlapped | ViewArrangement.Resizable | ViewArrangement.Movable;
+ ViewportSettings |= Terminal.Gui.ViewportSettings.Transparent;
+ base.Add (
+ new Button ()
+ {
+ Title = "_Hi",
+ X = Pos.Center (),
+ Y = Pos.Center (),
+ ShadowStyle = ShadowStyle.None,
+ ColorScheme = Colors.ColorSchemes ["Toplevel"],
+ });
+ }
+
+ ///
+ protected override bool OnMouseEvent (MouseEventArgs mouseEvent) { return false; }
+}
diff --git a/UICatalog/Scenarios/Editors/ViewportSettingsEditor.cs b/UICatalog/Scenarios/Editors/ViewportSettingsEditor.cs
new file mode 100644
index 0000000000..011ca212fc
--- /dev/null
+++ b/UICatalog/Scenarios/Editors/ViewportSettingsEditor.cs
@@ -0,0 +1,371 @@
+#nullable enable
+using System;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios;
+
+///
+/// Provides an editor UI for View.ViewportSettings.
+///
+public sealed class ViewportSettingsEditor : EditorBase
+{
+ public ViewportSettingsEditor ()
+ {
+ Title = "ViewportSettingsEditor";
+ TabStop = TabBehavior.TabGroup;
+
+ Initialized += ViewportSettingsEditor_Initialized;
+ }
+
+ protected override void OnUpdateSettings ()
+ {
+ foreach (View subview in Subviews)
+ {
+ subview.Enabled = ViewToEdit is not Adornment;
+ }
+
+ if (ViewToEdit is null)
+ { }
+ }
+
+ protected override void OnViewToEditChanged ()
+ {
+ if (ViewToEdit is { } and not Adornment)
+ {
+ //ViewToEdit.VerticalScrollBar.AutoShow = true;
+ //ViewToEdit.HorizontalScrollBar.AutoShow = true;
+
+ _contentSizeWidth!.Value = ViewToEdit.GetContentSize ().Width;
+ _contentSizeHeight!.Value = ViewToEdit.GetContentSize ().Height;
+
+ _cbAllowNegativeX!.CheckedState = ViewToEdit.ViewportSettings.HasFlag (Terminal.Gui.ViewportSettings.AllowNegativeX)
+ ? CheckState.Checked
+ : CheckState.UnChecked;
+
+ _cbAllowNegativeY!.CheckedState = ViewToEdit.ViewportSettings.HasFlag (Terminal.Gui.ViewportSettings.AllowNegativeY)
+ ? CheckState.Checked
+ : CheckState.UnChecked;
+
+ _cbAllowXGreaterThanContentWidth!.CheckedState = ViewToEdit.ViewportSettings.HasFlag (Terminal.Gui.ViewportSettings.AllowXGreaterThanContentWidth)
+ ? CheckState.Checked
+ : CheckState.UnChecked;
+
+ _cbAllowYGreaterThanContentHeight!.CheckedState = ViewToEdit.ViewportSettings.HasFlag (Terminal.Gui.ViewportSettings.AllowYGreaterThanContentHeight)
+ ? CheckState.Checked
+ : CheckState.UnChecked;
+
+ _cbClearContentOnly!.CheckedState = ViewToEdit.ViewportSettings.HasFlag (Terminal.Gui.ViewportSettings.ClearContentOnly)
+ ? CheckState.Checked
+ : CheckState.UnChecked;
+
+ _cbClipContentOnly!.CheckedState = ViewToEdit.ViewportSettings.HasFlag (Terminal.Gui.ViewportSettings.ClipContentOnly)
+ ? CheckState.Checked
+ : CheckState.UnChecked;
+
+ _cbTransparent!.CheckedState = ViewToEdit.ViewportSettings.HasFlag(Terminal.Gui.ViewportSettings.Transparent)
+ ? CheckState.Checked
+ : CheckState.UnChecked;
+
+ _cbVerticalScrollBar!.CheckedState = ViewToEdit.VerticalScrollBar.Visible ? CheckState.Checked : CheckState.UnChecked;
+ _cbAutoShowVerticalScrollBar!.CheckedState = ViewToEdit.VerticalScrollBar.AutoShow ? CheckState.Checked : CheckState.UnChecked;
+ _cbHorizontalScrollBar!.CheckedState = ViewToEdit.HorizontalScrollBar.Visible ? CheckState.Checked : CheckState.UnChecked;
+ _cbAutoShowHorizontalScrollBar!.CheckedState = ViewToEdit.HorizontalScrollBar.AutoShow ? CheckState.Checked : CheckState.UnChecked;
+ }
+ }
+
+ private CheckBox? _cbAllowNegativeX;
+ private CheckBox? _cbAllowNegativeY;
+ private CheckBox? _cbAllowXGreaterThanContentWidth;
+ private CheckBox? _cbAllowYGreaterThanContentHeight;
+ private NumericUpDown? _contentSizeWidth;
+ private NumericUpDown? _contentSizeHeight;
+ private CheckBox? _cbClearContentOnly;
+ private CheckBox? _cbClipContentOnly;
+ private CheckBox? _cbTransparent;
+ private CheckBox? _cbVerticalScrollBar;
+ private CheckBox? _cbAutoShowVerticalScrollBar;
+ private CheckBox? _cbHorizontalScrollBar;
+ private CheckBox? _cbAutoShowHorizontalScrollBar;
+
+ private void ViewportSettingsEditor_Initialized (object? s, EventArgs e)
+ {
+ _cbAllowNegativeX = new()
+ {
+ Title = "Allow X < 0",
+ CanFocus = true
+ };
+
+ Add (_cbAllowNegativeX);
+
+ _cbAllowNegativeY = new()
+ {
+ Title = "Allow Y < 0",
+ CanFocus = true
+ };
+
+ Add (_cbAllowNegativeY);
+
+ _cbAllowXGreaterThanContentWidth = new()
+ {
+ Title = "Allow X > Content Width",
+ Y = Pos.Bottom (_cbAllowNegativeX),
+ CanFocus = true
+ };
+
+ _cbAllowNegativeX.CheckedStateChanging += AllowNegativeXToggle;
+ _cbAllowXGreaterThanContentWidth.CheckedStateChanging += AllowXGreaterThanContentWidthToggle;
+
+ Add (_cbAllowXGreaterThanContentWidth);
+
+ void AllowNegativeXToggle (object? sender, CancelEventArgs e)
+ {
+ if (e.NewValue == CheckState.Checked)
+ {
+ ViewToEdit!.ViewportSettings |= Terminal.Gui.ViewportSettings.AllowNegativeX;
+ }
+ else
+ {
+ ViewToEdit!.ViewportSettings &= ~Terminal.Gui.ViewportSettings.AllowNegativeX;
+ }
+ }
+
+ void AllowXGreaterThanContentWidthToggle (object? sender, CancelEventArgs e)
+ {
+ if (e.NewValue == CheckState.Checked)
+ {
+ ViewToEdit!.ViewportSettings |= Terminal.Gui.ViewportSettings.AllowXGreaterThanContentWidth;
+ }
+ else
+ {
+ ViewToEdit!.ViewportSettings &= ~Terminal.Gui.ViewportSettings.AllowXGreaterThanContentWidth;
+ }
+ }
+
+ _cbAllowYGreaterThanContentHeight = new()
+ {
+ Title = "Allow Y > Content Height",
+ X = Pos.Right (_cbAllowXGreaterThanContentWidth) + 1,
+ Y = Pos.Bottom (_cbAllowNegativeX),
+ CanFocus = true
+ };
+
+ _cbAllowNegativeY.CheckedStateChanging += AllowNegativeYToggle;
+
+ _cbAllowYGreaterThanContentHeight.CheckedStateChanging += AllowYGreaterThanContentHeightToggle;
+
+ Add (_cbAllowYGreaterThanContentHeight);
+
+ void AllowNegativeYToggle (object? sender, CancelEventArgs e)
+ {
+ if (e.NewValue == CheckState.Checked)
+ {
+ ViewToEdit!.ViewportSettings |= Terminal.Gui.ViewportSettings.AllowNegativeY;
+ }
+ else
+ {
+ ViewToEdit!.ViewportSettings &= ~Terminal.Gui.ViewportSettings.AllowNegativeY;
+ }
+ }
+
+ void AllowYGreaterThanContentHeightToggle (object? sender, CancelEventArgs e)
+ {
+ if (e.NewValue == CheckState.Checked)
+ {
+ ViewToEdit!.ViewportSettings |= Terminal.Gui.ViewportSettings.AllowYGreaterThanContentHeight;
+ }
+ else
+ {
+ ViewToEdit!.ViewportSettings &= ~Terminal.Gui.ViewportSettings.AllowYGreaterThanContentHeight;
+ }
+ }
+
+ _cbAllowNegativeY.X = Pos.Left (_cbAllowYGreaterThanContentHeight);
+
+ var labelContentSize = new Label
+ {
+ Title = "ContentSize:",
+ Y = Pos.Bottom (_cbAllowYGreaterThanContentHeight)
+ };
+
+ _contentSizeWidth = new()
+ {
+ X = Pos.Right (labelContentSize) + 1,
+ Y = Pos.Top (labelContentSize),
+ CanFocus = true
+ };
+ _contentSizeWidth.ValueChanging += ContentSizeWidthValueChanged;
+
+ void ContentSizeWidthValueChanged (object? sender, CancelEventArgs e)
+ {
+ if (e.NewValue < 0)
+ {
+ e.Cancel = true;
+
+ return;
+ }
+
+ // BUGBUG: set_ContentSize is supposed to be `protected`.
+ ViewToEdit!.SetContentSize (ViewToEdit.GetContentSize () with { Width = e.NewValue });
+ }
+
+ var labelComma = new Label
+ {
+ Title = ",",
+ X = Pos.Right (_contentSizeWidth),
+ Y = Pos.Top (labelContentSize)
+ };
+
+ _contentSizeHeight = new()
+ {
+ X = Pos.Right (labelComma) + 1,
+ Y = Pos.Top (labelContentSize),
+ CanFocus = true
+ };
+ _contentSizeHeight.ValueChanging += ContentSizeHeightValueChanged;
+
+ void ContentSizeHeightValueChanged (object? sender, CancelEventArgs e)
+ {
+ if (e.NewValue < 0)
+ {
+ e.Cancel = true;
+
+ return;
+ }
+
+ // BUGBUG: set_ContentSize is supposed to be `protected`.
+ ViewToEdit?.SetContentSize (ViewToEdit.GetContentSize () with { Height = e.NewValue });
+ }
+
+ _cbClearContentOnly = new()
+ {
+ Title = "ClearContentOnly",
+ X = 0,
+ Y = Pos.Bottom (labelContentSize),
+ CanFocus = true
+ };
+ _cbClearContentOnly.CheckedStateChanging += ClearContentOnlyToggle;
+
+ void ClearContentOnlyToggle (object? sender, CancelEventArgs e)
+ {
+ if (e.NewValue == CheckState.Checked)
+ {
+ ViewToEdit!.ViewportSettings |= Terminal.Gui.ViewportSettings.ClearContentOnly;
+ }
+ else
+ {
+ ViewToEdit!.ViewportSettings &= ~Terminal.Gui.ViewportSettings.ClearContentOnly;
+ }
+ }
+
+ _cbClipContentOnly = new()
+ {
+ Title = "ClipContentOnly",
+ X = Pos.Right (_cbClearContentOnly) + 1,
+ Y = Pos.Bottom (labelContentSize),
+ CanFocus = true
+ };
+ _cbClipContentOnly.CheckedStateChanging += ClipContentOnlyToggle;
+
+ void ClipContentOnlyToggle (object? sender, CancelEventArgs e)
+ {
+ if (e.NewValue == CheckState.Checked)
+ {
+ ViewToEdit!.ViewportSettings |= Terminal.Gui.ViewportSettings.ClipContentOnly;
+ }
+ else
+ {
+ ViewToEdit!.ViewportSettings &= ~Terminal.Gui.ViewportSettings.ClipContentOnly;
+ }
+ }
+
+ _cbTransparent = new ()
+ {
+ Title = "Transparent",
+ X = Pos.Right (_cbClipContentOnly) + 1,
+ Y = Pos.Bottom (labelContentSize),
+ CanFocus = true
+ };
+ _cbTransparent.CheckedStateChanging += TransparentToggle;
+
+ void TransparentToggle (object? sender, CancelEventArgs e)
+ {
+ if (e.NewValue == CheckState.Checked)
+ {
+ ViewToEdit!.ViewportSettings |= Terminal.Gui.ViewportSettings.Transparent;
+ }
+ else
+ {
+ ViewToEdit!.ViewportSettings &= ~Terminal.Gui.ViewportSettings.Transparent;
+ }
+ }
+
+ _cbVerticalScrollBar = new()
+ {
+ Title = "VerticalScrollBar",
+ X = 0,
+ Y = Pos.Bottom (_cbClearContentOnly),
+ CanFocus = false
+ };
+ _cbVerticalScrollBar.CheckedStateChanging += VerticalScrollBarToggle;
+
+ void VerticalScrollBarToggle (object? sender, CancelEventArgs e)
+ {
+ ViewToEdit!.VerticalScrollBar.Visible = e.NewValue == CheckState.Checked;
+ }
+
+ _cbAutoShowVerticalScrollBar = new()
+ {
+ Title = "AutoShow",
+ X = Pos.Right (_cbVerticalScrollBar) + 1,
+ Y = Pos.Top (_cbVerticalScrollBar),
+ CanFocus = false
+ };
+ _cbAutoShowVerticalScrollBar.CheckedStateChanging += AutoShowVerticalScrollBarToggle;
+
+ void AutoShowVerticalScrollBarToggle (object? sender, CancelEventArgs e)
+ {
+ ViewToEdit!.VerticalScrollBar.AutoShow = e.NewValue == CheckState.Checked;
+ }
+
+ _cbHorizontalScrollBar = new()
+ {
+ Title = "HorizontalScrollBar",
+ X = 0,
+ Y = Pos.Bottom (_cbVerticalScrollBar),
+ CanFocus = false
+ };
+ _cbHorizontalScrollBar.CheckedStateChanging += HorizontalScrollBarToggle;
+
+ void HorizontalScrollBarToggle (object? sender, CancelEventArgs e)
+ {
+ ViewToEdit!.HorizontalScrollBar.Visible = e.NewValue == CheckState.Checked;
+ }
+
+ _cbAutoShowHorizontalScrollBar = new()
+ {
+ Title = "AutoShow ",
+ X = Pos.Right (_cbHorizontalScrollBar) + 1,
+ Y = Pos.Top (_cbHorizontalScrollBar),
+ CanFocus = false
+ };
+ _cbAutoShowHorizontalScrollBar.CheckedStateChanging += AutoShowHorizontalScrollBarToggle;
+
+ void AutoShowHorizontalScrollBarToggle (object? sender, CancelEventArgs e)
+ {
+ ViewToEdit!.HorizontalScrollBar.AutoShow = e.NewValue == CheckState.Checked;
+ }
+
+ Add (
+ labelContentSize,
+ _contentSizeWidth,
+ labelComma,
+ _contentSizeHeight,
+ _cbClearContentOnly,
+ _cbClipContentOnly,
+ _cbTransparent,
+ _cbVerticalScrollBar,
+ _cbHorizontalScrollBar,
+ _cbAutoShowVerticalScrollBar,
+ _cbAutoShowHorizontalScrollBar);
+ }
+}
diff --git a/UnitTests/View/Draw/DrawTests.cs b/UnitTests/View/Draw/DrawTests.cs
index 90d2ecb520..666e5b8adc 100644
--- a/UnitTests/View/Draw/DrawTests.cs
+++ b/UnitTests/View/Draw/DrawTests.cs
@@ -968,7 +968,7 @@ public void SetClip_ClipVisibleContentOnly_VisibleContentIsClipped ()
Assert.Equal (view.Frame, View.GetClip ()!.GetBounds ());
// Act
- view.ClipViewport ();
+ view.AddViewportToClip ();
// Assert
Assert.Equal (expectedClip, View.GetClip ()!.GetBounds ());
@@ -1002,7 +1002,7 @@ public void SetClip_Default_ClipsToViewport ()
view.Viewport = view.Viewport with { X = 1, Y = 1 };
// Act
- view.ClipViewport ();
+ view.AddViewportToClip ();
// Assert
Assert.Equal (expectedClip, View.GetClip ()!.GetBounds ());
diff --git a/UnitTests/View/ViewTests.cs b/UnitTests/View/ViewTests.cs
index c169013e99..9a76f8e83e 100644
--- a/UnitTests/View/ViewTests.cs
+++ b/UnitTests/View/ViewTests.cs
@@ -163,7 +163,7 @@ public void Clear_Viewport_Can_Use_Driver_AddRune_Or_AddStr_Methods ()
view.DrawingContent += (s, e) =>
{
- Region savedClip = view.ClipViewport ();
+ Region savedClip = view.AddViewportToClip ();
for (var row = 0; row < view.Viewport.Height; row++)
{
@@ -226,7 +226,7 @@ public void Clear_Can_Use_Driver_AddRune_Or_AddStr_Methods ()
view.DrawingContent += (s, e) =>
{
- Region savedClip = view.ClipViewport ();
+ Region savedClip = view.AddViewportToClip ();
for (var row = 0; row < view.Viewport.Height; row++)
{