View.Drawing.Clipping.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. 
  2. namespace Terminal.Gui.ViewBase;
  3. public partial class View
  4. {
  5. /// <summary>
  6. /// Gets the current Clip region.
  7. /// </summary>
  8. /// <remarks>
  9. /// <para>
  10. /// There is a single clip region for the entire application.
  11. /// </para>
  12. /// <para>
  13. /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, clone it first.
  14. /// </para>
  15. /// </remarks>
  16. /// <returns>The current Clip.</returns>
  17. public static Region? GetClip (IDriver? driver) => driver?.Clip;
  18. /// <summary>
  19. /// Sets the Clip to the specified region.
  20. /// </summary>
  21. /// <remarks>
  22. /// <para>
  23. /// There is a single clip region for the entire application. This method sets the clip region to the specified
  24. /// region.
  25. /// </para>
  26. /// </remarks>
  27. /// <param name="driver"></param>
  28. /// <param name="region"></param>
  29. public static void SetClip (IDriver? driver, Region? region)
  30. {
  31. // BUGBUG: If region is null we should set the clip to null.
  32. // BUGBUG: Fixing this probably breaks other things.
  33. if (driver is { } && region is { })
  34. {
  35. driver.Clip = region;
  36. }
  37. }
  38. /// <summary>
  39. /// Sets the Clip to be the rectangle of the screen.
  40. /// </summary>
  41. /// <param name="driver"></param>
  42. /// <remarks>
  43. /// <para>
  44. /// There is a single clip region for the entire application. This method sets the clip region to the screen.
  45. /// </para>
  46. /// <para>
  47. /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is
  48. /// recommended to clone it first.
  49. /// </para>
  50. /// </remarks>
  51. /// <returns>
  52. /// The current Clip, which can be then re-applied <see cref="View.SetClip"/>
  53. /// </returns>
  54. public static Region? SetClipToScreen (IDriver? driver)
  55. {
  56. Region? previous = GetClip (driver);
  57. if (Application.Driver is { })
  58. {
  59. Application.Driver.Clip = new (driver!.Screen);
  60. }
  61. return previous;
  62. }
  63. /// <summary>
  64. /// Removes the specified rectangle from the Clip.
  65. /// </summary>
  66. /// <remarks>
  67. /// <para>
  68. /// There is a single clip region for the entire application.
  69. /// </para>
  70. /// </remarks>
  71. /// <param name="rectangle"></param>
  72. public static void ExcludeFromClip (Rectangle rectangle) { Application.Driver?.Clip?.Exclude (rectangle); }
  73. /// <summary>
  74. /// Removes the specified rectangle from the Clip.
  75. /// </summary>
  76. /// <remarks>
  77. /// <para>
  78. /// There is a single clip region for the entire application.
  79. /// </para>
  80. /// </remarks>
  81. /// <param name="region"></param>
  82. public static void ExcludeFromClip (Region? region) { Application.Driver?.Clip?.Exclude (region); }
  83. /// <summary>
  84. /// Changes the Clip to the intersection of the current Clip and the <see cref="Frame"/> of this View.
  85. /// </summary>
  86. /// <remarks>
  87. /// <para>
  88. /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is
  89. /// recommended to clone it first.
  90. /// </para>
  91. /// </remarks>
  92. /// <returns>
  93. /// The current Clip, which can be then re-applied <see cref="View.SetClip"/>
  94. /// </returns>
  95. internal Region? AddFrameToClip ()
  96. {
  97. if (Driver is null)
  98. {
  99. return null;
  100. }
  101. Region previous = GetClip (Driver) ?? new (Application.Screen);
  102. Region frameRegion = previous.Clone ();
  103. // Translate viewportRegion to screen-relative coords
  104. Rectangle screenRect = FrameToScreen ();
  105. frameRegion.Intersect (screenRect);
  106. if (this is Adornment adornment && adornment.Thickness != Thickness.Empty)
  107. {
  108. // Ensure adornments can't draw outside their thickness
  109. frameRegion.Exclude (adornment.Thickness.GetInside (FrameToScreen()));
  110. }
  111. SetClip (Driver, frameRegion);
  112. return previous;
  113. }
  114. /// <summary>Changes the Clip to the intersection of the current Clip and the <see cref="Viewport"/> of this View.</summary>
  115. /// <remarks>
  116. /// <para>
  117. /// By default, sets the Clip to the intersection of the current clip region and the
  118. /// <see cref="Viewport"/>. This ensures that drawing is constrained to the viewport, but allows
  119. /// content to be drawn beyond the viewport.
  120. /// </para>
  121. /// <para>
  122. /// If <see cref="ViewportSettings"/> has <see cref="ViewBase.ViewportSettingsFlags.ClipContentOnly"/> set, clipping will be
  123. /// applied to just the visible content area.
  124. /// </para>
  125. /// <remarks>
  126. /// <para>
  127. /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it
  128. /// is recommended to clone it first.
  129. /// </para>
  130. /// </remarks>
  131. /// </remarks>
  132. /// <returns>
  133. /// The current Clip, which can be then re-applied <see cref="View.SetClip"/>
  134. /// </returns>
  135. public Region? AddViewportToClip ()
  136. {
  137. if (Driver is null)
  138. {
  139. return null;
  140. }
  141. Region previous = GetClip (Driver) ?? new (Application.Screen);
  142. Region viewportRegion = previous.Clone ();
  143. Rectangle viewport = ViewportToScreen (new Rectangle (Point.Empty, Viewport.Size));
  144. viewportRegion?.Intersect (viewport);
  145. if (ViewportSettings.HasFlag (ViewportSettingsFlags.ClipContentOnly))
  146. {
  147. // Clamp the Clip to the just content area that is within the viewport
  148. Rectangle visibleContent = ViewportToScreen (new Rectangle (new (-Viewport.X, -Viewport.Y), GetContentSize ()));
  149. viewportRegion?.Intersect (visibleContent);
  150. }
  151. if (this is Adornment adornment && adornment.Thickness != Thickness.Empty)
  152. {
  153. // Ensure adornments can't draw outside their thickness
  154. viewportRegion?.Exclude (adornment.Thickness.GetInside (viewport));
  155. }
  156. SetClip (Driver, viewportRegion);
  157. return previous;
  158. }
  159. }