ShadowView.cs 6.1 KB

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