Updated the Transparent scenario to better demonstrate transparency features, including dynamic resizing, custom drawing, and improved clarity in the TransparentView class. Added new methods to support non-rectangular drawn regions and transparency effects.

Enhanced the `DrawContext` and `View` classes with detailed documentation and examples for implementing transparency. Improved the `OnDrawingContent` method and `DrawingContent` event to support reporting drawn regions for transparency.

Performed general code cleanup, including removing unused code, simplifying `ViewportSettings` usage, and improving property initialization. Minor namespace cleanup was also included.
This commit is contained in:
Tig
2025-12-04 16:23:19 -07:00
parent e192b5622d
commit ca67dc0472
3 changed files with 215 additions and 33 deletions

View File

@@ -1,8 +1,9 @@
// ReSharper disable AccessToDisposedClosure
#nullable enable
namespace UICatalog.Scenarios;
[ScenarioMetadata ("Transparent", "Testing Transparency")]
[ScenarioMetadata ("Transparent", "Demonstrates View Transparency")]
public sealed class Transparent : Scenario
{
public override void Main ()
@@ -53,11 +54,19 @@ public sealed class Transparent : Scenario
var tv = new TransparentView ()
{
X = 3,
Y = 3,
Width = 50,
Height = 15
X = 2,
Y = 2,
Width = Dim.Fill (10),
Height = Dim.Fill (10)
};
appWindow.ViewportChanged += (sender, args) =>
{
// Little hack to convert the Dim.Fill to actual size
// So resizing works
tv.Width = appWindow!.Frame.Width - 10;
tv.Height = appWindow!.Frame.Height - 10;
};
appWindow.Add (tv);
// Run - Start the application.
@@ -72,34 +81,31 @@ public sealed class Transparent : Scenario
{
public TransparentView ()
{
Title = "Transparent View";
//base.Text = "View.Text.\nThis should be opaque.\nNote how clipping works?";
TextFormatter.Alignment = Alignment.Center;
TextFormatter.VerticalAlignment = Alignment.Center;
Title = "Transparent View - Move and Resize To See Transparency In Action";
base.Text = "View.Text.\nThis should be opaque. Note how clipping works?";
Arrangement = ViewArrangement.Overlapped | ViewArrangement.Resizable | ViewArrangement.Movable;
ViewportSettings |= Terminal.Gui.ViewBase.ViewportSettingsFlags.Transparent | Terminal.Gui.ViewBase.ViewportSettingsFlags.TransparentMouse;
ViewportSettings |= ViewportSettingsFlags.Transparent | ViewportSettingsFlags.TransparentMouse;
BorderStyle = LineStyle.RoundedDotted;
//SchemeName = "Base";
SchemeName = "Base";
var transparentSubView = new View ()
{
Text = "Sizable/Movable View with border. Should be opaque. No Shadow.",
Text = "Sizable/Movable SunView with border and shadow.",
Id = "transparentSubView",
X = 4,
Y = 8,
X = Pos.Center (),
Y = Pos.Center (),
Width = 20,
Height = 8,
BorderStyle = LineStyle.Dashed,
Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable,
// ShadowStyle = ShadowStyle.Transparent,
ShadowStyle = ShadowStyle.Transparent,
};
transparentSubView.Border!.Thickness = new (1, 1, 1, 1);
transparentSubView.SchemeName = "Dialog";
//transparentSubView.Visible = false;
Button button = new Button ()
{
Title = "_Opaque Shadows No Worky",
Title = "_Opaque Shadow",
X = Pos.Center (),
Y = 2,
SchemeName = "Dialog",
@@ -109,8 +115,6 @@ public sealed class Transparent : Scenario
MessageBox.Query (App, "Clicked!", "Button in Transparent View", "_Ok");
args.Handled = true;
};
//button.Visible = false;
var shortcut = new Shortcut ()
{
@@ -121,7 +125,6 @@ public sealed class Transparent : Scenario
HelpText = "Help!",
Key = Key.F11,
SchemeName = "Base"
};
button.ClearingViewport += (sender, args) =>
@@ -129,16 +132,91 @@ public sealed class Transparent : Scenario
args.Cancel = true;
};
// Subscribe to DrawingContent event to draw "TUI"
DrawingContent += TransparentView_DrawingContent;
base.Add (button);
base.Add (shortcut);
base.Add (transparentSubView);
//Padding.Thickness = new (1);
//Padding.SchemeName = "Error";
Padding!.Thickness = new (1);
Padding.Text = "This is the Padding";
}
Margin!.Thickness = new (1);
// Margin.ViewportSettings |= Terminal.Gui.ViewportSettingsFlags.Transparent;
private void TransparentView_DrawingContent (object? sender, DrawEventArgs e)
{
// Draw "TUI" text using rectangular regions, positioned after "Hi"
// Letter "T"
Rectangle tTop = new (20, 5, 7, 2); // Top horizontal bar
Rectangle tStem = new (23, 7, 2, 8); // Vertical stem
// Letter "U"
Rectangle uLeft = new (30, 5, 2, 8); // Left vertical bar
Rectangle uBottom = new (32, 13, 3, 2); // Bottom horizontal bar
Rectangle uRight = new (35, 5, 2, 8); // Right vertical bar
// Letter "I"
Rectangle iTop = new (39, 5, 4, 2); // Bar on top
Rectangle iStem = new (40, 7, 2, 6); // Vertical stem
Rectangle iBottom = new (39, 13, 4, 2); // Bar on Bottom
// Draw "TUI" using the HotActive attribute
SetAttributeForRole (VisualRole.HotActive);
FillRect (tTop, Glyphs.BlackCircle);
FillRect (tStem, Glyphs.BlackCircle);
FillRect (uLeft, Glyphs.BlackCircle);
FillRect (uBottom, Glyphs.BlackCircle);
FillRect (uRight, Glyphs.BlackCircle);
FillRect (iTop, Glyphs.BlackCircle);
FillRect (iStem, Glyphs.BlackCircle);
FillRect (iBottom, Glyphs.BlackCircle);
Region tuiRegion = new Region (ViewportToScreen (tTop));
tuiRegion.Union (ViewportToScreen (tStem));
tuiRegion.Union (ViewportToScreen (uLeft));
tuiRegion.Union (ViewportToScreen (uBottom));
tuiRegion.Union (ViewportToScreen (uRight));
tuiRegion.Union (ViewportToScreen (iTop));
tuiRegion.Union (ViewportToScreen (iStem));
tuiRegion.Union (ViewportToScreen (iBottom));
// Register the drawn region for "TUI" to enable transparency effects
e.DrawContext?.AddDrawnRegion (tuiRegion);
}
/// <inheritdoc />
protected override bool OnDrawingContent (DrawContext? context)
{
base.OnDrawingContent (context);
// Draw "Hi" text using rectangular regions
// Letter "H"
Rectangle hLeft = new (5, 5, 2, 10); // Left vertical bar
Rectangle hMiddle = new (7, 9, 3, 2); // Middle horizontal bar
Rectangle hRight = new (10, 5, 2, 10); // Right vertical bar
// Letter "i" (with some space between H and i)
Rectangle iDot = new (15, 5, 2, 2); // Dot on top
Rectangle iStem = new (15, 9, 2, 6); // Vertical stem
// Draw "Hi" using the Highlight attribute
SetAttributeForRole (VisualRole.Highlight);
FillRect (hLeft, Glyphs.BlackCircle);
FillRect (hMiddle, Glyphs.BlackCircle);
FillRect (hRight, Glyphs.BlackCircle);
FillRect (iDot, Glyphs.BlackCircle);
FillRect (iStem, Glyphs.BlackCircle);
// Register the drawn region for "Hi" to enable transparency effects
Region hiRegion = new Region (ViewportToScreen (hLeft));
hiRegion.Union (ViewportToScreen (hMiddle));
hiRegion.Union (ViewportToScreen (hRight));
hiRegion.Union (ViewportToScreen (iDot));
hiRegion.Union (ViewportToScreen (iStem));
context?.AddDrawnRegion (hiRegion);
// Return false to allow DrawingContent event to fire
return false;
}
/// <inheritdoc />

View File

@@ -1,10 +1,43 @@

namespace Terminal.Gui.ViewBase;
namespace Terminal.Gui.ViewBase;
/// <summary>
/// Tracks the region that has been drawn during <see cref="View.Draw(DrawContext?)"/>. This is primarily
/// in support of <see cref="ViewportSettingsFlags.Transparent"/>.
/// </summary>
/// <remarks>
/// <para>
/// When a <see cref="View"/> has <see cref="ViewportSettingsFlags.Transparent"/> set, the <see cref="DrawContext"/>
/// is used to track exactly which areas of the screen have been drawn to. After drawing is complete, these drawn
/// regions are excluded from the clip region, allowing views beneath the transparent view to show through in
/// the areas that were not drawn.
/// </para>
/// <para>
/// All coordinates tracked by <see cref="DrawContext"/> are in <b>screen-relative coordinates</b>. When reporting
/// drawn areas from within <see cref="View.OnDrawingContent(DrawContext?)"/>, use <see cref="View.ViewportToScreen(in Rectangle)"/>
/// or <see cref="View.ContentToScreen(in Point)"/> to convert viewport-relative or content-relative coordinates to
/// screen-relative coordinates before calling <see cref="AddDrawnRectangle"/> or <see cref="AddDrawnRegion"/>.
/// </para>
/// <para>
/// Example of reporting a non-rectangular drawn region for transparency:
/// </para>
/// <code>
/// protected override bool OnDrawingContent (DrawContext? context)
/// {
/// // Draw some content in viewport-relative coordinates
/// Rectangle rect1 = new Rectangle (5, 5, 10, 3);
/// Rectangle rect2 = new Rectangle (8, 8, 4, 7);
/// FillRect (rect1, Glyphs.BlackCircle);
/// FillRect (rect2, Glyphs.BlackCircle);
///
/// // Report the drawn region in screen-relative coordinates
/// Region drawnRegion = new Region (ViewportToScreen (rect1));
/// drawnRegion.Union (ViewportToScreen (rect2));
/// context?.AddDrawnRegion (drawnRegion);
///
/// return true;
/// }
/// </code>
/// </remarks>
public class DrawContext
{
private readonly Region _drawnRegion = new Region ();
@@ -12,12 +45,20 @@ public class DrawContext
/// <summary>
/// Gets a copy of the region drawn so far in this context.
/// </summary>
/// <remarks>
/// The returned region contains all areas that have been reported as drawn via <see cref="AddDrawnRectangle"/>
/// or <see cref="AddDrawnRegion"/>, in screen-relative coordinates.
/// </remarks>
public Region GetDrawnRegion () => _drawnRegion.Clone ();
/// <summary>
/// Reports that a rectangle has been drawn.
/// </summary>
/// <param name="rect">The rectangle that was drawn.</param>
/// <param name="rect">The rectangle that was drawn, in screen-relative coordinates.</param>
/// <remarks>
/// When called from within <see cref="View.OnDrawingContent(DrawContext?)"/>, ensure the rectangle is in
/// screen-relative coordinates by using <see cref="View.ViewportToScreen(in Rectangle)"/> or similar methods.
/// </remarks>
public void AddDrawnRectangle (Rectangle rect)
{
_drawnRegion.Combine (rect, RegionOp.Union);
@@ -26,7 +67,18 @@ public class DrawContext
/// <summary>
/// Reports that a region has been drawn.
/// </summary>
/// <param name="region">The region that was drawn.</param>
/// <param name="region">The region that was drawn, in screen-relative coordinates.</param>
/// <remarks>
/// <para>
/// This method is useful for reporting non-rectangular drawn areas, which is important for
/// proper transparency support with <see cref="ViewportSettingsFlags.Transparent"/>.
/// </para>
/// <para>
/// When called from within <see cref="View.OnDrawingContent(DrawContext?)"/>, ensure the region is in
/// screen-relative coordinates by using <see cref="View.ViewportToScreen(in Rectangle)"/> to convert each
/// rectangle in the region.
/// </para>
/// </remarks>
public void AddDrawnRegion (Region region)
{
_drawnRegion.Combine (region, RegionOp.Union);
@@ -36,7 +88,7 @@ public class DrawContext
/// Clips (intersects) the drawn region with the specified rectangle.
/// This modifies the internal drawn region directly.
/// </summary>
/// <param name="clipRect">The clipping rectangle.</param>
/// <param name="clipRect">The clipping rectangle, in screen-relative coordinates.</param>
public void ClipDrawnRegion (Rectangle clipRect)
{
_drawnRegion.Intersect (clipRect);
@@ -46,7 +98,7 @@ public class DrawContext
/// Clips (intersects) the drawn region with the specified region.
/// This modifies the internal drawn region directly.
/// </summary>
/// <param name="clipRegion">The clipping region.</param>
/// <param name="clipRegion">The clipping region, in screen-relative coordinates.</param>
public void ClipDrawnRegion (Region clipRegion)
{
_drawnRegion.Intersect (clipRegion);

View File

@@ -520,14 +520,66 @@ public partial class View // Drawing APIs
/// </summary>
/// <param name="context">The draw context to report drawn areas to.</param>
/// <returns><see langword="true"/> to stop further drawing content.</returns>
/// <remarks>
/// <para>
/// Override this method to draw custom content for your View.
/// </para>
/// <para>
/// <b>Transparency Support:</b> If your View has <see cref="ViewportSettings"/> with <see cref="ViewportSettingsFlags.Transparent"/>
/// set, you should report the exact regions you draw to via the <paramref name="context"/> parameter. This allows
/// the transparency system to exclude only the drawn areas from the clip region, letting views beneath show through
/// in the areas you didn't draw.
/// </para>
/// <para>
/// Use <see cref="DrawContext.AddDrawnRectangle"/> for simple rectangular areas, or <see cref="DrawContext.AddDrawnRegion"/>
/// for complex, non-rectangular shapes. All coordinates passed to these methods must be in <b>screen-relative coordinates</b>.
/// Use <see cref="View.ViewportToScreen(in Rectangle)"/> or <see cref="View.ContentToScreen(in Point)"/> to convert from
/// viewport-relative or content-relative coordinates.
/// </para>
/// <para>
/// Example of drawing custom content with transparency support:
/// </para>
/// <code>
/// protected override bool OnDrawingContent (DrawContext? context)
/// {
/// base.OnDrawingContent (context);
///
/// // Draw content in viewport-relative coordinates
/// Rectangle rect1 = new Rectangle (5, 5, 10, 3);
/// Rectangle rect2 = new Rectangle (8, 8, 4, 7);
/// FillRect (rect1, Glyphs.BlackCircle);
/// FillRect (rect2, Glyphs.BlackCircle);
///
/// // Report drawn region in screen-relative coordinates for transparency
/// if (ViewportSettings.HasFlag (ViewportSettingsFlags.Transparent))
/// {
/// Region drawnRegion = new Region (ViewportToScreen (rect1));
/// drawnRegion.Union (ViewportToScreen (rect2));
/// context?.AddDrawnRegion (drawnRegion);
/// }
///
/// return true;
/// }
/// </code>
/// </remarks>
protected virtual bool OnDrawingContent (DrawContext? context) { return false; }
/// <summary>Raised when the View's content is to be drawn.</summary>
/// <remarks>
/// <para>Will be invoked before any subviews added with <see cref="Add(View)"/> have been drawn.</para>
/// <para>
/// Rect provides the view-relative rectangle describing the currently visible viewport into the
/// <see cref="View"/> .
/// Subscribe to this event to draw custom content for the View. Use the drawing methods available on <see cref="View"/>
/// such as <see cref="View.AddRune(int, int, Rune)"/>, <see cref="View.AddStr(string)"/>, and <see cref="View.FillRect(Rectangle, Rune?)"/>.
/// </para>
/// <para>
/// The event is invoked after <see cref="ClearingViewport"/> and after any <see cref="SubViews"/> and <see cref="Text"/> have been drawn.
/// </para>
/// <para>
/// <b>Transparency Support:</b> If the View has <see cref="ViewportSettings"/> with <see cref="ViewportSettingsFlags.Transparent"/>
/// set, use the <see cref="DrawEventArgs.DrawContext"/> to report which areas were actually drawn. This enables proper transparency
/// by excluding only the drawn areas from the clip region. See <see cref="DrawContext"/> for details on reporting drawn regions.
/// </para>
/// <para>
/// The <see cref="DrawEventArgs.NewViewport"/> property provides the view-relative rectangle describing the currently visible viewport into the View.
/// </para>
/// </remarks>
public event EventHandler<DrawEventArgs>? DrawingContent;