View.Drawing.Clipping.cs 5.6 KB

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