ShadowView.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. #nullable enable
  2. namespace Terminal.Gui.ViewBase;
  3. /// <summary>
  4. /// Draws a shadow on the right or bottom of the view. Used internally by <see cref="Margin"/>.
  5. /// </summary>
  6. internal class ShadowView : View
  7. {
  8. private ShadowStyle _shadowStyle;
  9. /// <inheritdoc/>
  10. protected override bool OnDrawingText () { return true; }
  11. /// <inheritdoc/>
  12. protected override bool OnClearingViewport ()
  13. {
  14. // Prevent clearing (so we can have transparency)
  15. return true;
  16. }
  17. /// <inheritdoc/>
  18. protected override bool OnDrawingContent ()
  19. {
  20. switch (ShadowStyle)
  21. {
  22. case ShadowStyle.Opaque:
  23. if (Orientation == Orientation.Vertical)
  24. {
  25. DrawVerticalShadowOpaque (Viewport);
  26. }
  27. else
  28. {
  29. DrawHorizontalShadowOpaque (Viewport);
  30. }
  31. break;
  32. case ShadowStyle.Transparent:
  33. if (Orientation == Orientation.Vertical)
  34. {
  35. DrawVerticalShadowTransparent (Viewport);
  36. }
  37. else
  38. {
  39. DrawHorizontalShadowTransparent (Viewport);
  40. }
  41. break;
  42. }
  43. return true;
  44. }
  45. /// <summary>
  46. /// Gets or sets the orientation of the shadow.
  47. /// </summary>
  48. public Orientation Orientation { get; set; }
  49. public override ShadowStyle ShadowStyle
  50. {
  51. get => _shadowStyle;
  52. set
  53. {
  54. Visible = value != ShadowStyle.None;
  55. _shadowStyle = value;
  56. ViewportSettings |= ViewportSettingsFlags.TransparentMouse;
  57. }
  58. }
  59. private void DrawHorizontalShadowOpaque (Rectangle rectangle)
  60. {
  61. // Draw the start glyph
  62. SetAttribute (GetAttributeUnderLocation (ViewportToScreen (new Point (0, 0))));
  63. AddRune (0, 0, Glyphs.ShadowHorizontalStart);
  64. // Fill the rest of the rectangle with the glyph - note we skip the last since vertical will draw it
  65. for (var i = 1; i < rectangle.Width - 1; i++)
  66. {
  67. SetAttribute (GetAttributeUnderLocation (ViewportToScreen (new Point (i, 0))));
  68. AddRune (i, 0, Glyphs.ShadowHorizontal);
  69. }
  70. // Last is special
  71. SetAttribute (GetAttributeUnderLocation (ViewportToScreen (new Point (rectangle.Width - 1, 0))));
  72. AddRune (rectangle.Width - 1, 0, Glyphs.ShadowHorizontalEnd);
  73. }
  74. private void DrawHorizontalShadowTransparent (Rectangle viewport)
  75. {
  76. Rectangle screen = ViewportToScreen (Viewport);
  77. for (int r = Math.Max (0, screen.Y); r < screen.Y + screen.Height; r++)
  78. {
  79. for (int c = Math.Max (0, screen.X + 1); c < screen.X + screen.Width; c++)
  80. {
  81. Driver.Move (c, r);
  82. SetAttribute (GetAttributeUnderLocation (new (c, r)));
  83. if (c < ScreenContents?.GetLength (1) && r < ScreenContents?.GetLength (0))
  84. {
  85. AddRune (ScreenContents [r, c].Rune);
  86. }
  87. }
  88. }
  89. }
  90. private void DrawVerticalShadowOpaque (Rectangle viewport)
  91. {
  92. // Draw the start glyph
  93. SetAttribute (GetAttributeUnderLocation (ViewportToScreen (new Point (0, 0))));
  94. AddRune (0, 0, Glyphs.ShadowVerticalStart);
  95. // Fill the rest of the rectangle with the glyph
  96. for (var i = 1; i < viewport.Height - 1; i++)
  97. {
  98. SetAttribute (GetAttributeUnderLocation (ViewportToScreen (new Point (0, i))));
  99. AddRune (0, i, Glyphs.ShadowVertical);
  100. }
  101. }
  102. private void DrawVerticalShadowTransparent (Rectangle viewport)
  103. {
  104. Rectangle screen = ViewportToScreen (Viewport);
  105. // Fill in the rest of the rectangle
  106. for (int c = Math.Max (0, screen.X); c < screen.X + screen.Width; c++)
  107. {
  108. for (int r = Math.Max (0, screen.Y); r < screen.Y + viewport.Height; r++)
  109. {
  110. Driver.Move (c, r);
  111. SetAttribute (GetAttributeUnderLocation (new (c, r)));
  112. if (ScreenContents is { } && screen.X < ScreenContents.GetLength (1) && r < ScreenContents.GetLength (0))
  113. {
  114. AddRune (ScreenContents [r, c].Rune);
  115. }
  116. }
  117. }
  118. }
  119. private Attribute GetAttributeUnderLocation (Point location)
  120. {
  121. if (SuperView is not Adornment adornment
  122. || location.X < 0
  123. || location.X >= Application.Screen.Width
  124. || location.Y < 0
  125. || location.Y >= Application.Screen.Height)
  126. {
  127. return Attribute.Default;
  128. }
  129. if (ScreenContents == null ||
  130. location.Y < 0 || location.Y >= ScreenContents.GetLength (0) ||
  131. location.X < 0 || location.X >= ScreenContents.GetLength (1))
  132. {
  133. return Attribute.Default;
  134. }
  135. Attribute attr = ScreenContents [location.Y, location.X].Attribute!.Value;
  136. var newAttribute =
  137. new Attribute (
  138. ShadowStyle == ShadowStyle.Opaque ? Color.Black : attr.Foreground.GetDimColor (),
  139. ShadowStyle == ShadowStyle.Opaque ? attr.Background : attr.Background.GetDimColor (0.05),
  140. attr.Style);
  141. // If the BG is DarkGray, GetDimColor gave up. Instead of using the attribute in the Driver under the shadow,
  142. // use the Normal attribute from the View under the shadow.
  143. if (newAttribute.Background == Color.DarkGray)
  144. {
  145. List<View?> currentViewsUnderMouse = View.GetViewsUnderLocation (location, ViewportSettingsFlags.Transparent);
  146. View? underView = currentViewsUnderMouse!.LastOrDefault ();
  147. attr = underView?.GetAttributeForRole (VisualRole.Normal) ?? Attribute.Default;
  148. newAttribute = new (
  149. ShadowStyle == ShadowStyle.Opaque ? Color.Black : attr.Background.GetDimColor (),
  150. ShadowStyle == ShadowStyle.Opaque ? attr.Background : attr.Foreground.GetDimColor (0.25),
  151. attr.Style);
  152. }
  153. return newAttribute;
  154. }
  155. }