View.Drawing.Clipping.cs 6.0 KB

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