Scroll.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. #nullable enable
  2. using System.ComponentModel;
  3. namespace Terminal.Gui;
  4. /// <summary>
  5. /// Indicates the position and size of scrollable content. The indicator can be dragged with the mouse. Can be
  6. /// oriented either vertically or horizontally. Used within a <see cref="ScrollBar"/>.
  7. /// </summary>
  8. /// <remarks>
  9. /// <para>
  10. /// By default, this view cannot be focused and does not support keyboard.
  11. /// </para>
  12. /// </remarks>
  13. public class Scroll : View
  14. {
  15. /// <inheritdoc/>
  16. public Scroll ()
  17. {
  18. _slider = new ();
  19. Add (_slider);
  20. WantContinuousButtonPressed = true;
  21. CanFocus = false;
  22. Orientation = Orientation.Vertical;
  23. Width = Dim.Auto (DimAutoStyle.Content, 1);
  24. Height = Dim.Auto (DimAutoStyle.Content, 1);
  25. }
  26. internal readonly ScrollSlider _slider;
  27. private Orientation _orientation;
  28. private int _position;
  29. private int _size;
  30. /// <inheritdoc/>
  31. public override void EndInit ()
  32. {
  33. base.EndInit ();
  34. AdjustScroll ();
  35. }
  36. /// <summary>
  37. /// Gets or sets if the Scroll is oriented vertically or horizontally.
  38. /// </summary>
  39. public Orientation Orientation
  40. {
  41. get => _orientation;
  42. set
  43. {
  44. _orientation = value;
  45. AdjustScroll ();
  46. }
  47. }
  48. /// <summary>
  49. /// Gets or sets the position of the start of the Scroll slider, relative to <see cref="Size"/>.
  50. /// </summary>
  51. public int Position
  52. {
  53. get => _position;
  54. set
  55. {
  56. if (value == _position || value < 0)
  57. {
  58. return;
  59. }
  60. if (SuperViewAsScrollBar is { IsInitialized: false })
  61. {
  62. // Ensures a more exactly calculation
  63. SetRelativeLayout (SuperViewAsScrollBar.Frame.Size);
  64. }
  65. int barSize = BarSize;
  66. if (value + barSize > Size)
  67. {
  68. return;
  69. }
  70. CancelEventArgs<int> args = OnPositionChanging (_position, value);
  71. if (args.Cancel)
  72. {
  73. return;
  74. }
  75. _position = value;
  76. AdjustScroll ();
  77. OnPositionChanged (_position);
  78. }
  79. }
  80. /// <summary>Raised when the <see cref="Position"/> has changed.</summary>
  81. public event EventHandler<EventArgs<int>>? PositionChanged;
  82. /// <summary>
  83. /// Raised when the <see cref="Position"/> is changing. Set <see cref="CancelEventArgs.Cancel"/> to
  84. /// <see langword="true"/> to prevent the position from being changed.
  85. /// </summary>
  86. public event EventHandler<CancelEventArgs<int>>? PositionChanging;
  87. /// <summary>
  88. /// Gets or sets the size of the Scroll. This is the total size of the content that can be scrolled through.
  89. /// </summary>
  90. public int Size
  91. {
  92. get => _size;
  93. set
  94. {
  95. _size = value;
  96. OnSizeChanged (_size);
  97. AdjustScroll ();
  98. }
  99. }
  100. /// <summary>Raised when <see cref="Size"/> has changed.</summary>
  101. public event EventHandler<EventArgs<int>>? SizeChanged;
  102. /// <inheritdoc/>
  103. protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
  104. {
  105. int location = Orientation == Orientation.Vertical ? mouseEvent.Position.Y : mouseEvent.Position.X;
  106. int barSize = BarSize;
  107. (int start, int end) sliderPos = _orientation == Orientation.Vertical
  108. ? new (_slider.Frame.Y, _slider.Frame.Bottom - 1)
  109. : new (_slider.Frame.X, _slider.Frame.Right - 1);
  110. if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed) && location < sliderPos.start)
  111. {
  112. Position = Math.Max (Position - barSize, 0);
  113. }
  114. else if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed) && location > sliderPos.end)
  115. {
  116. Position = Math.Min (Position + barSize, Size - barSize);
  117. }
  118. else if ((mouseEvent.Flags == MouseFlags.WheeledDown && Orientation == Orientation.Vertical)
  119. || (mouseEvent.Flags == MouseFlags.WheeledRight && Orientation == Orientation.Horizontal))
  120. {
  121. Position = Math.Min (Position + 1, Size - barSize);
  122. }
  123. else if ((mouseEvent.Flags == MouseFlags.WheeledUp && Orientation == Orientation.Vertical)
  124. || (mouseEvent.Flags == MouseFlags.WheeledLeft && Orientation == Orientation.Horizontal))
  125. {
  126. Position = Math.Max (Position - 1, 0);
  127. }
  128. else if (mouseEvent.Flags == MouseFlags.Button1Clicked)
  129. {
  130. if (_slider.Frame.Contains (mouseEvent.Position))
  131. {
  132. return _slider.OnMouseEvent (mouseEvent);
  133. }
  134. }
  135. return base.OnMouseEvent (mouseEvent);
  136. }
  137. /// <summary>Virtual method called when <see cref="Position"/> has changed. Raises <see cref="PositionChanged"/>.</summary>
  138. protected virtual void OnPositionChanged (int position) { PositionChanged?.Invoke (this, new (in position)); }
  139. /// <summary>
  140. /// Virtual method called when <see cref="Position"/> is changing. Raises <see cref="PositionChanging"/>, which is
  141. /// cancelable.
  142. /// </summary>
  143. protected virtual CancelEventArgs<int> OnPositionChanging (int currentPos, int newPos)
  144. {
  145. CancelEventArgs<int> args = new (ref currentPos, ref newPos);
  146. PositionChanging?.Invoke (this, args);
  147. return args;
  148. }
  149. /// <summary>Called when <see cref="Size"/> has changed. Raises <see cref="SizeChanged"/>.</summary>
  150. protected void OnSizeChanged (int size) { SizeChanged?.Invoke (this, new (in size)); }
  151. internal void AdjustScroll ()
  152. {
  153. if (SuperViewAsScrollBar is { })
  154. {
  155. X = Orientation == Orientation.Vertical ? 0 : 1;
  156. Y = Orientation == Orientation.Vertical ? 1 : 0;
  157. Width = Orientation == Orientation.Vertical ? Dim.Fill () : Dim.Fill (1);
  158. Height = Orientation == Orientation.Vertical ? Dim.Fill (1) : Dim.Fill ();
  159. }
  160. _slider.AdjustSlider ();
  161. SetScrollText ();
  162. }
  163. /// <inheritdoc/>
  164. internal override void OnLayoutComplete (LayoutEventArgs args)
  165. {
  166. base.OnLayoutComplete (args);
  167. AdjustScroll ();
  168. }
  169. internal ScrollBar? SuperViewAsScrollBar => SuperView as ScrollBar;
  170. private int BarSize => Orientation == Orientation.Vertical ? Viewport.Height : Viewport.Width;
  171. private void SetScrollText ()
  172. {
  173. TextDirection = Orientation == Orientation.Vertical ? TextDirection.TopBottom_LeftRight : TextDirection.LeftRight_TopBottom;
  174. // QUESTION: Should these Glyphs be configurable via CM?
  175. Text = string.Concat (
  176. Enumerable.Repeat (
  177. Glyphs.Stipple.ToString (),
  178. GetContentSize ().Width * GetContentSize ().Height));
  179. }
  180. }