From ca67dc04725e9df52826757b36ecbb5a00781125 Mon Sep 17 00:00:00 2001 From: Tig Date: Thu, 4 Dec 2025 16:23:19 -0700 Subject: [PATCH] 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. --- Examples/UICatalog/Scenarios/Transparent.cs | 126 ++++++++++++++++---- Terminal.Gui/ViewBase/DrawContext.cs | 64 +++++++++- Terminal.Gui/ViewBase/View.Drawing.cs | 58 ++++++++- 3 files changed, 215 insertions(+), 33 deletions(-) diff --git a/Examples/UICatalog/Scenarios/Transparent.cs b/Examples/UICatalog/Scenarios/Transparent.cs index 801372d2c..833bcc558 100644 --- a/Examples/UICatalog/Scenarios/Transparent.cs +++ b/Examples/UICatalog/Scenarios/Transparent.cs @@ -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); + } + + /// + 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; } /// diff --git a/Terminal.Gui/ViewBase/DrawContext.cs b/Terminal.Gui/ViewBase/DrawContext.cs index e6df3033b..0a683a636 100644 --- a/Terminal.Gui/ViewBase/DrawContext.cs +++ b/Terminal.Gui/ViewBase/DrawContext.cs @@ -1,10 +1,43 @@ - -namespace Terminal.Gui.ViewBase; +namespace Terminal.Gui.ViewBase; /// /// Tracks the region that has been drawn during . This is primarily /// in support of . /// +/// +/// +/// When a has set, the +/// 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. +/// +/// +/// All coordinates tracked by are in screen-relative coordinates. When reporting +/// drawn areas from within , use +/// or to convert viewport-relative or content-relative coordinates to +/// screen-relative coordinates before calling or . +/// +/// +/// Example of reporting a non-rectangular drawn region for transparency: +/// +/// +/// 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; +/// } +/// +/// public class DrawContext { private readonly Region _drawnRegion = new Region (); @@ -12,12 +45,20 @@ public class DrawContext /// /// Gets a copy of the region drawn so far in this context. /// + /// + /// The returned region contains all areas that have been reported as drawn via + /// or , in screen-relative coordinates. + /// public Region GetDrawnRegion () => _drawnRegion.Clone (); /// /// Reports that a rectangle has been drawn. /// - /// The rectangle that was drawn. + /// The rectangle that was drawn, in screen-relative coordinates. + /// + /// When called from within , ensure the rectangle is in + /// screen-relative coordinates by using or similar methods. + /// public void AddDrawnRectangle (Rectangle rect) { _drawnRegion.Combine (rect, RegionOp.Union); @@ -26,7 +67,18 @@ public class DrawContext /// /// Reports that a region has been drawn. /// - /// The region that was drawn. + /// The region that was drawn, in screen-relative coordinates. + /// + /// + /// This method is useful for reporting non-rectangular drawn areas, which is important for + /// proper transparency support with . + /// + /// + /// When called from within , ensure the region is in + /// screen-relative coordinates by using to convert each + /// rectangle in the region. + /// + /// 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. /// - /// The clipping rectangle. + /// The clipping rectangle, in screen-relative coordinates. 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. /// - /// The clipping region. + /// The clipping region, in screen-relative coordinates. public void ClipDrawnRegion (Region clipRegion) { _drawnRegion.Intersect (clipRegion); diff --git a/Terminal.Gui/ViewBase/View.Drawing.cs b/Terminal.Gui/ViewBase/View.Drawing.cs index 3a523de7d..5c213f55e 100644 --- a/Terminal.Gui/ViewBase/View.Drawing.cs +++ b/Terminal.Gui/ViewBase/View.Drawing.cs @@ -520,14 +520,66 @@ public partial class View // Drawing APIs /// /// The draw context to report drawn areas to. /// to stop further drawing content. + /// + /// + /// Override this method to draw custom content for your View. + /// + /// + /// Transparency Support: If your View has with + /// set, you should report the exact regions you draw to via the 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. + /// + /// + /// Use for simple rectangular areas, or + /// for complex, non-rectangular shapes. All coordinates passed to these methods must be in screen-relative coordinates. + /// Use or to convert from + /// viewport-relative or content-relative coordinates. + /// + /// + /// Example of drawing custom content with transparency support: + /// + /// + /// 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; + /// } + /// + /// protected virtual bool OnDrawingContent (DrawContext? context) { return false; } /// Raised when the View's content is to be drawn. /// - /// Will be invoked before any subviews added with have been drawn. /// - /// Rect provides the view-relative rectangle describing the currently visible viewport into the - /// . + /// Subscribe to this event to draw custom content for the View. Use the drawing methods available on + /// such as , , and . + /// + /// + /// The event is invoked after and after any and have been drawn. + /// + /// + /// Transparency Support: If the View has with + /// set, use the to report which areas were actually drawn. This enables proper transparency + /// by excluding only the drawn areas from the clip region. See for details on reporting drawn regions. + /// + /// + /// The property provides the view-relative rectangle describing the currently visible viewport into the View. /// /// public event EventHandler? DrawingContent;