View.Drawing.Clipping.cs 5.6 KB

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