瀏覽代碼

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.
Tig 1 周之前
父節點
當前提交
ca67dc0472
共有 3 個文件被更改,包括 215 次插入33 次删除
  1. 102 24
      Examples/UICatalog/Scenarios/Transparent.cs
  2. 58 6
      Terminal.Gui/ViewBase/DrawContext.cs
  3. 55 3
      Terminal.Gui/ViewBase/View.Drawing.cs

+ 102 - 24
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";
+        }
+
+        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);
+        }
 
-            Margin!.Thickness = new (1);
-           // Margin.ViewportSettings |= Terminal.Gui.ViewportSettingsFlags.Transparent;
+        /// <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 />

+ 58 - 6
Terminal.Gui/ViewBase/DrawContext.cs

@@ -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);

+ 55 - 3
Terminal.Gui/ViewBase/View.Drawing.cs

@@ -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;