diff --git a/Terminal.Gui/View/Layout/ViewLayout.cs b/Terminal.Gui/View/Layout/ViewLayout.cs
index d373ddc79..111441b84 100644
--- a/Terminal.Gui/View/Layout/ViewLayout.cs
+++ b/Terminal.Gui/View/Layout/ViewLayout.cs
@@ -66,7 +66,7 @@ public partial class View {
_y = _frame.Y;
_width = _frame.Width;
_height = _frame.Height;
-
+
// TODO: Figure out if the below can be optimized.
if (IsInitialized /*|| LayoutStyle == LayoutStyle.Absolute*/) {
LayoutFrames ();
@@ -615,10 +615,14 @@ public partial class View {
// First try SuperView.Bounds, then Application.Top, then Driver
// Finally, if none of those are valid, use int.MaxValue (for Unit tests).
- SetRelativeLayout (SuperView?.Bounds ?? Application.Top?.Bounds ?? Application.Driver?.Bounds ?? new Rect (0, 0, int.MaxValue, int.MaxValue));
+ var relativeBounds = SuperView is { IsInitialized: true } ? SuperView.Bounds :
+ ((Application.Top != null && Application.Top.IsInitialized) ? Application.Top.Bounds :
+ Application.Driver?.Bounds ??
+ new Rect (0, 0, int.MaxValue, int.MaxValue));
+ SetRelativeLayout (relativeBounds);
// TODO: Determine what, if any of the below is actually needed here.
- if (IsInitialized/* || LayoutStyle == LayoutStyle.Absolute*/) {
+ if (IsInitialized) {
SetFrameToFitText ();
LayoutFrames ();
TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
@@ -874,16 +878,16 @@ public partial class View {
// Set the frame. Do NOT use `Frame` as it overwrites X, Y, Width, and Height, making
// the view LayoutStyle.Absolute.
_frame = r;
- if (X is Pos.PosAbsolute) {
+ if (_x is Pos.PosAbsolute) {
_x = Frame.X;
}
- if (Y is Pos.PosAbsolute) {
+ if (_y is Pos.PosAbsolute) {
_y = Frame.Y;
}
- if (Width is Dim.DimAbsolute) {
+ if (_width is Dim.DimAbsolute) {
_width = Frame.Width;
}
- if (Height is Dim.DimAbsolute) {
+ if (_height is Dim.DimAbsolute) {
_height = Frame.Height;
}
@@ -894,7 +898,7 @@ public partial class View {
SetNeedsLayout ();
//SetNeedsDisplay ();
}
-
+
// BUGBUG: Why is this AFTER setting Frame? Seems duplicative.
if (!SetFrameToFitText ()) {
TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
@@ -1148,7 +1152,7 @@ public partial class View {
void LayoutSubview (View v, Rect contentArea)
{
//if (v.LayoutStyle == LayoutStyle.Computed) {
- v.SetRelativeLayout (contentArea);
+ v.SetRelativeLayout (contentArea);
//}
v.LayoutSubviews ();
diff --git a/Terminal.Gui/Views/ScrollBarView.cs b/Terminal.Gui/Views/ScrollBarView.cs
index 8932bd198..c6171f8d5 100644
--- a/Terminal.Gui/Views/ScrollBarView.cs
+++ b/Terminal.Gui/Views/ScrollBarView.cs
@@ -8,816 +8,824 @@
using System;
using System.Text;
-namespace Terminal.Gui {
+namespace Terminal.Gui;
+
+///
+/// ScrollBarViews are views that display a 1-character scrollbar, either horizontal or vertical
+///
+///
+///
+/// The scrollbar is drawn to be a representation of the Size, assuming that the
+/// scroll position is set at Position.
+///
+///
+/// If the region to display the scrollbar is larger than three characters,
+/// arrow indicators are drawn.
+///
+///
+public class ScrollBarView : View {
+ bool _autoHideScrollBars = true;
+ View _contentBottomRightCorner;
+ bool _hosted;
+ bool _keepContentAlwaysInViewport = true;
+
+ int _lastLocation = -1;
+ ScrollBarView _otherScrollBarView;
+ int _posBarOffset;
+ int _posBottomTee;
+ int _posLeftTee;
+ int _posRightTee;
+
+ int _posTopTee;
+ bool _showScrollIndicator;
+ int _size, _position;
+ bool _vertical;
+
///
- /// ScrollBarViews are views that display a 1-character scrollbar, either horizontal or vertical
+ /// Initializes a new instance of the class using
+ /// layout.
///
- ///
- ///
- /// The scrollbar is drawn to be a representation of the Size, assuming that the
- /// scroll position is set at Position.
- ///
- ///
- /// If the region to display the scrollbar is larger than three characters,
- /// arrow indicators are drawn.
- ///
- ///
- public class ScrollBarView : View {
- bool _vertical;
- int _size, _position;
- bool _showScrollIndicator;
- bool _keepContentAlwaysInViewport = true;
- bool _autoHideScrollBars = true;
- bool _hosted;
- ScrollBarView _otherScrollBarView;
- View _contentBottomRightCorner;
+ /// Frame for the scrollbar.
+ public ScrollBarView (Rect rect) : this (rect, 0, 0, false) { }
- bool _showBothScrollIndicator => OtherScrollBarView?._showScrollIndicator == true && _showScrollIndicator;
+ ///
+ /// Initializes a new instance of the class using
+ /// layout.
+ ///
+ /// Frame for the scrollbar.
+ /// The size that this scrollbar represents. Sets the property.
+ /// The position within this scrollbar. Sets the property.
+ ///
+ /// If set to true this is a vertical scrollbar, otherwise, the scrollbar is horizontal. Sets the
+ /// property.
+ ///
+ public ScrollBarView (Rect rect, int size, int position, bool isVertical) : base (rect) => SetInitialProperties (size, position, isVertical);
- ///
- /// Initializes a new instance of the class using layout.
- ///
- /// Frame for the scrollbar.
- public ScrollBarView (Rect rect) : this (rect, 0, 0, false) { }
+ ///
+ /// Initializes a new instance of the class using
+ /// layout.
+ ///
+ public ScrollBarView () : this (0, 0, false) { }
- ///
- /// Initializes a new instance of the class using layout.
- ///
- /// Frame for the scrollbar.
- /// The size that this scrollbar represents. Sets the property.
- /// The position within this scrollbar. Sets the property.
- /// If set to true this is a vertical scrollbar, otherwise, the scrollbar is horizontal. Sets the property.
- public ScrollBarView (Rect rect, int size, int position, bool isVertical) : base (rect)
- {
- SetInitialProperties (size, position, isVertical);
+ ///
+ /// Initializes a new instance of the class using
+ /// layout.
+ ///
+ /// The size that this scrollbar represents.
+ /// The position within this scrollbar.
+ /// If set to true this is a vertical scrollbar, otherwise, the scrollbar is horizontal.
+ public ScrollBarView (int size, int position, bool isVertical) => SetInitialProperties (size, position, isVertical);
+
+ ///
+ /// Initializes a new instance of the class using
+ /// layout.
+ ///
+ /// The view that will host this scrollbar.
+ /// If set to true this is a vertical scrollbar, otherwise, the scrollbar is horizontal.
+ ///
+ /// If set to true (default) will have the other scrollbar, otherwise will
+ /// have only one.
+ ///
+ public ScrollBarView (View host, bool isVertical, bool showBothScrollIndicator = true) : this (0, 0, isVertical)
+ {
+ if (host == null) {
+ throw new ArgumentNullException ("The host parameter can't be null.");
}
-
- ///
- /// Initializes a new instance of the class using layout.
- ///
- public ScrollBarView () : this (0, 0, false) { }
-
- ///
- /// Initializes a new instance of the class using layout.
- ///
- /// The size that this scrollbar represents.
- /// The position within this scrollbar.
- /// If set to true this is a vertical scrollbar, otherwise, the scrollbar is horizontal.
- public ScrollBarView (int size, int position, bool isVertical) : base ()
- {
- SetInitialProperties (size, position, isVertical);
+ if (host.SuperView == null) {
+ throw new ArgumentNullException ("The host SuperView parameter can't be null.");
}
-
- ///
- /// Initializes a new instance of the class using layout.
- ///
- /// The view that will host this scrollbar.
- /// If set to true this is a vertical scrollbar, otherwise, the scrollbar is horizontal.
- /// If set to true (default) will have the other scrollbar, otherwise will have only one.
- public ScrollBarView (View host, bool isVertical, bool showBothScrollIndicator = true) : this (0, 0, isVertical)
- {
- if (host == null) {
- throw new ArgumentNullException ("The host parameter can't be null.");
- } else if (host.SuperView == null) {
- throw new ArgumentNullException ("The host SuperView parameter can't be null.");
- }
- _hosted = true;
- ColorScheme = host.ColorScheme;
- X = isVertical ? Pos.Right (host) - 1 : Pos.Left (host);
- Y = isVertical ? Pos.Top (host) : Pos.Bottom (host) - 1;
- Host = host;
- CanFocus = false;
- Enabled = host.Enabled;
- Visible = host.Visible;
- //Host.CanFocusChanged += Host_CanFocusChanged;
- Host.EnabledChanged += Host_EnabledChanged;
- Host.VisibleChanged += Host_VisibleChanged;
- Host.SuperView.Add (this);
- AutoHideScrollBars = true;
- if (showBothScrollIndicator) {
- OtherScrollBarView = new ScrollBarView (0, 0, !isVertical) {
- Id = "OtherScrollBarView",
- ColorScheme = host.ColorScheme,
- Host = host,
- CanFocus = false,
- Enabled = host.Enabled,
- Visible = host.Visible,
- OtherScrollBarView = this
- };
- OtherScrollBarView._hosted = true;
- OtherScrollBarView.X = OtherScrollBarView.IsVertical ? Pos.Right (host) - 1 : Pos.Left (host);
- OtherScrollBarView.Y = OtherScrollBarView.IsVertical ? Pos.Top (host) : Pos.Bottom (host) - 1;
- OtherScrollBarView.Host.SuperView.Add (OtherScrollBarView);
- OtherScrollBarView.ShowScrollIndicator = true;
- }
- ShowScrollIndicator = true;
- CreateBottomRightCorner ();
- ClearOnVisibleFalse = false;
- }
-
- private void CreateBottomRightCorner ()
- {
- if (Host != null && (_contentBottomRightCorner == null && OtherScrollBarView == null
- || (_contentBottomRightCorner == null && OtherScrollBarView != null && OtherScrollBarView._contentBottomRightCorner == null))) {
-
- _contentBottomRightCorner = new View () {
- Id = "contentBottomRightCorner",
- Visible = Host.Visible,
- ClearOnVisibleFalse = false,
- ColorScheme = ColorScheme
- };
- if (_hosted) {
- Host.SuperView.Add (_contentBottomRightCorner);
- } else {
- Host.Add (_contentBottomRightCorner);
- }
- _contentBottomRightCorner.X = Pos.Right (Host) - 1;
- _contentBottomRightCorner.Y = Pos.Bottom (Host) - 1;
- _contentBottomRightCorner.Width = 1;
- _contentBottomRightCorner.Height = 1;
- _contentBottomRightCorner.MouseClick += ContentBottomRightCorner_MouseClick;
- _contentBottomRightCorner.DrawContent += _contentBottomRightCorner_DrawContent;
- }
- }
-
- private void _contentBottomRightCorner_DrawContent (object sender, DrawEventArgs e)
- {
- Driver.SetAttribute (Host.HasFocus ? ColorScheme.Focus : GetNormalColor ());
- }
-
- private void Host_VisibleChanged (object sender, EventArgs e)
- {
- if (!Host.Visible) {
- Visible = Host.Visible;
- if (_otherScrollBarView != null) {
- _otherScrollBarView.Visible = Visible;
- }
- _contentBottomRightCorner.Visible = Visible;
- } else {
- ShowHideScrollBars ();
- }
- }
-
- private void Host_EnabledChanged (object sender, EventArgs e)
- {
- Enabled = Host.Enabled;
- if (_otherScrollBarView != null) {
- _otherScrollBarView.Enabled = Enabled;
- }
- _contentBottomRightCorner.Enabled = Enabled;
- }
-
- //private void Host_CanFocusChanged ()
- //{
- // CanFocus = Host.CanFocus;
- // if (otherScrollBarView != null) {
- // otherScrollBarView.CanFocus = CanFocus;
- // }
- //}
-
- void ContentBottomRightCorner_MouseClick (object sender, MouseEventEventArgs me)
- {
- if (me.MouseEvent.Flags == MouseFlags.WheeledDown || me.MouseEvent.Flags == MouseFlags.WheeledUp
- || me.MouseEvent.Flags == MouseFlags.WheeledRight || me.MouseEvent.Flags == MouseFlags.WheeledLeft) {
-
- MouseEvent (me.MouseEvent);
- } else if (me.MouseEvent.Flags == MouseFlags.Button1Clicked) {
- Host.SetFocus ();
- }
-
- me.Handled = true;
- }
-
- void SetInitialProperties (int size, int position, bool isVertical)
- {
- Id = "ScrollBarView";
- _vertical = isVertical;
- this._position = position;
- this._size = size;
- WantContinuousButtonPressed = true;
- ClearOnVisibleFalse = false;
-
- Added += (s, e) => CreateBottomRightCorner ();
-
- Initialized += (s, e) => {
- SetWidthHeight ();
- SetRelativeLayout (SuperView?.Frame ?? Host?.Frame ?? Frame);
- if (Id == "OtherScrollBarView" || OtherScrollBarView == null) {
- // Only do this once if both scrollbars are enabled
- ShowHideScrollBars ();
- }
- SetPosition (position);
+ _hosted = true;
+ ColorScheme = host.ColorScheme;
+ X = isVertical ? Pos.Right (host) - 1 : Pos.Left (host);
+ Y = isVertical ? Pos.Top (host) : Pos.Bottom (host) - 1;
+ Host = host;
+ CanFocus = false;
+ Enabled = host.Enabled;
+ Visible = host.Visible;
+ //Host.CanFocusChanged += Host_CanFocusChanged;
+ Host.EnabledChanged += Host_EnabledChanged;
+ Host.VisibleChanged += Host_VisibleChanged;
+ Host.SuperView.Add (this);
+ AutoHideScrollBars = true;
+ if (showBothScrollIndicator) {
+ OtherScrollBarView = new ScrollBarView (0, 0, !isVertical) {
+ Id = "OtherScrollBarView",
+ ColorScheme = host.ColorScheme,
+ Host = host,
+ CanFocus = false,
+ Enabled = host.Enabled,
+ Visible = host.Visible,
+ OtherScrollBarView = this
};
+ OtherScrollBarView._hosted = true;
+ OtherScrollBarView.X = OtherScrollBarView.IsVertical ? Pos.Right (host) - 1 : Pos.Left (host);
+ OtherScrollBarView.Y = OtherScrollBarView.IsVertical ? Pos.Top (host) : Pos.Bottom (host) - 1;
+ OtherScrollBarView.Host.SuperView.Add (OtherScrollBarView);
+ OtherScrollBarView.ShowScrollIndicator = true;
}
+ ShowScrollIndicator = true;
+ CreateBottomRightCorner ();
+ ClearOnVisibleFalse = false;
+ }
- ///
- /// If set to true this is a vertical scrollbar, otherwise, the scrollbar is horizontal.
- ///
- public bool IsVertical {
- get => _vertical;
- set {
- _vertical = value;
- if (IsInitialized) {
- SetWidthHeight ();
- }
+ bool _showBothScrollIndicator => OtherScrollBarView?._showScrollIndicator == true && _showScrollIndicator;
+
+ ///
+ /// If set to true this is a vertical scrollbar, otherwise, the scrollbar is horizontal.
+ ///
+ public bool IsVertical {
+ get => _vertical;
+ set {
+ _vertical = value;
+ if (IsInitialized) {
+ SetWidthHeight ();
}
}
+ }
- ///
- /// The size of content the scrollbar represents.
- ///
- /// The size.
- /// The is typically the size of the virtual content. E.g. when a Scrollbar is
- /// part of a the Size is set to the appropriate dimension of .
- public int Size {
- get => _size;
- set {
- _size = value;
- if (IsInitialized) {
- SetRelativeLayout (SuperView?.Frame ?? Host.Frame);
- ShowHideScrollBars (false);
- SetNeedsDisplay ();
- }
+ ///
+ /// The size of content the scrollbar represents.
+ ///
+ /// The size.
+ ///
+ /// The is typically the size of the virtual content. E.g. when a Scrollbar is
+ /// part of a the Size is set to the appropriate dimension of .
+ ///
+ public int Size {
+ get => _size;
+ set {
+ _size = value;
+ if (IsInitialized) {
+ SetRelativeLayout (SuperView?.Frame ?? Host.Frame);
+ ShowHideScrollBars (false);
+ SetNeedsDisplay ();
}
}
+ }
- ///
- /// This event is raised when the position on the scrollbar has changed.
- ///
- public event EventHandler ChangedPosition;
-
- ///
- /// The position, relative to , to set the scrollbar at.
- ///
- /// The position.
- public int Position {
- get => _position;
- set {
- _position = value;
- if (IsInitialized) {
- // We're not initialized so we can't do anything fancy. Just cache value.
- SetPosition (value);
- }
+ ///
+ /// The position, relative to , to set the scrollbar at.
+ ///
+ /// The position.
+ public int Position {
+ get => _position;
+ set {
+ _position = value;
+ if (IsInitialized) {
+ // We're not initialized so we can't do anything fancy. Just cache value.
+ SetPosition (value);
}
}
+ }
- // Helper to assist Initialized event handler
- private void SetPosition (int newPosition)
- {
- if (CanScroll (newPosition - _position, out int max, _vertical)) {
- if (max == newPosition - _position) {
- _position = newPosition;
+ // BUGBUG: v2 - for consistency this should be named "Parent" not "Host"
+ ///
+ /// Get or sets the view that host this
+ ///
+ public View Host { get; internal set; }
+
+ ///
+ /// Represent a vertical or horizontal ScrollBarView other than this.
+ ///
+ public ScrollBarView OtherScrollBarView {
+ get => _otherScrollBarView;
+ set {
+ if (value != null && (value.IsVertical && _vertical || !value.IsVertical && !_vertical)) {
+ throw new ArgumentException ($"There is already a {(_vertical ? "vertical" : "horizontal")} ScrollBarView.");
+ }
+ _otherScrollBarView = value;
+ }
+ }
+
+ // BUGBUG: v2 - Why can't we get rid of this and just use Visible?
+ ///
+ /// Gets or sets the visibility for the vertical or horizontal scroll indicator.
+ ///
+ /// true if show vertical or horizontal scroll indicator; otherwise, false.
+ public bool ShowScrollIndicator {
+ get => _showScrollIndicator;
+ set {
+ //if (value == showScrollIndicator) {
+ // return;
+ //}
+
+ _showScrollIndicator = value;
+ if (IsInitialized) {
+ SetNeedsLayout ();
+ if (value) {
+ Visible = true;
} else {
- _position = Math.Max (_position + max, 0);
+ Visible = false;
+ Position = 0;
}
- } else if (max < 0) {
- _position = Math.Max (_position + max, 0);
+ SetWidthHeight ();
+ }
+ }
+ }
+
+ ///
+ /// Get or sets if the view-port is kept always visible in the area of this
+ ///
+ public bool KeepContentAlwaysInViewport {
+ get => _keepContentAlwaysInViewport;
+ set {
+ if (_keepContentAlwaysInViewport != value) {
+ _keepContentAlwaysInViewport = value;
+ var pos = 0;
+ if (value && !_vertical && _position + Host.Bounds.Width > _size) {
+ pos = _size - Host.Bounds.Width + (_showBothScrollIndicator ? 1 : 0);
+ }
+ if (value && _vertical && _position + Host.Bounds.Height > _size) {
+ pos = _size - Host.Bounds.Height + (_showBothScrollIndicator ? 1 : 0);
+ }
+ if (pos != 0) {
+ Position = pos;
+ }
+ if (OtherScrollBarView != null && OtherScrollBarView._keepContentAlwaysInViewport != value) {
+ OtherScrollBarView.KeepContentAlwaysInViewport = value;
+ }
+ if (pos == 0) {
+ Refresh ();
+ }
+ }
+ }
+ }
+
+ ///
+ /// If true the vertical/horizontal scroll bars won't be showed if it's not needed.
+ ///
+ public bool AutoHideScrollBars {
+ get => _autoHideScrollBars;
+ set {
+ if (_autoHideScrollBars != value) {
+ _autoHideScrollBars = value;
+ SetNeedsDisplay ();
+ }
+ }
+ }
+
+ void CreateBottomRightCorner ()
+ {
+ if (Host != null &&
+ (_contentBottomRightCorner == null && OtherScrollBarView == null ||
+ _contentBottomRightCorner == null && OtherScrollBarView != null && OtherScrollBarView._contentBottomRightCorner == null)) {
+
+ _contentBottomRightCorner = new View {
+ Id = "contentBottomRightCorner",
+ Visible = Host.Visible,
+ ClearOnVisibleFalse = false,
+ ColorScheme = ColorScheme
+ };
+ if (_hosted) {
+ Host.SuperView.Add (_contentBottomRightCorner);
} else {
- _position = Math.Max (newPosition, 0);
+ Host.Add (_contentBottomRightCorner);
}
- OnChangedPosition ();
- SetNeedsDisplay ();
+ _contentBottomRightCorner.X = Pos.Right (Host) - 1;
+ _contentBottomRightCorner.Y = Pos.Bottom (Host) - 1;
+ _contentBottomRightCorner.Width = 1;
+ _contentBottomRightCorner.Height = 1;
+ _contentBottomRightCorner.MouseClick += ContentBottomRightCorner_MouseClick;
+ _contentBottomRightCorner.DrawContent += _contentBottomRightCorner_DrawContent;
}
+ }
- // BUGBUG: v2 - for consistency this should be named "Parent" not "Host"
- ///
- /// Get or sets the view that host this
- ///
- public View Host { get; internal set; }
+ void _contentBottomRightCorner_DrawContent (object sender, DrawEventArgs e) => Driver.SetAttribute (Host.HasFocus ? ColorScheme.Focus : GetNormalColor ());
- ///
- /// Represent a vertical or horizontal ScrollBarView other than this.
- ///
- public ScrollBarView OtherScrollBarView {
- get => _otherScrollBarView;
- set {
- if (value != null && (value.IsVertical && _vertical || !value.IsVertical && !_vertical)) {
- throw new ArgumentException ($"There is already a {(_vertical ? "vertical" : "horizontal")} ScrollBarView.");
- }
- _otherScrollBarView = value;
+ void Host_VisibleChanged (object sender, EventArgs e)
+ {
+ if (!Host.Visible) {
+ Visible = Host.Visible;
+ if (_otherScrollBarView != null) {
+ _otherScrollBarView.Visible = Visible;
}
- }
-
- // BUGBUG: v2 - Why can't we get rid of this and just use Visible?
- ///
- /// Gets or sets the visibility for the vertical or horizontal scroll indicator.
- ///
- /// true if show vertical or horizontal scroll indicator; otherwise, false.
- public bool ShowScrollIndicator {
- get => _showScrollIndicator;
- set {
- //if (value == showScrollIndicator) {
- // return;
- //}
-
- _showScrollIndicator = value;
- if (IsInitialized) {
- SetNeedsLayout ();
- if (value) {
- Visible = true;
- } else {
- Visible = false;
- Position = 0;
- }
- SetWidthHeight ();
- }
- }
- }
-
- ///
- /// Get or sets if the view-port is kept always visible in the area of this
- ///
- public bool KeepContentAlwaysInViewport {
- get { return _keepContentAlwaysInViewport; }
- set {
- if (_keepContentAlwaysInViewport != value) {
- _keepContentAlwaysInViewport = value;
- int pos = 0;
- if (value && !_vertical && _position + Host.Bounds.Width > _size) {
- pos = _size - Host.Bounds.Width + (_showBothScrollIndicator ? 1 : 0);
- }
- if (value && _vertical && _position + Host.Bounds.Height > _size) {
- pos = _size - Host.Bounds.Height + (_showBothScrollIndicator ? 1 : 0);
- }
- if (pos != 0) {
- Position = pos;
- }
- if (OtherScrollBarView != null && OtherScrollBarView._keepContentAlwaysInViewport != value) {
- OtherScrollBarView.KeepContentAlwaysInViewport = value;
- }
- if (pos == 0) {
- Refresh ();
- }
- }
- }
- }
-
- ///
- /// If true the vertical/horizontal scroll bars won't be showed if it's not needed.
- ///
- public bool AutoHideScrollBars {
- get => _autoHideScrollBars;
- set {
- if (_autoHideScrollBars != value) {
- _autoHideScrollBars = value;
- SetNeedsDisplay ();
- }
- }
- }
-
- ///
- /// Virtual method to invoke the action event.
- ///
- public virtual void OnChangedPosition ()
- {
- ChangedPosition?.Invoke (this, EventArgs.Empty);
- }
-
- ///
- /// Only used for a hosted view that will update and redraw the scrollbars.
- ///
- public virtual void Refresh ()
- {
+ _contentBottomRightCorner.Visible = Visible;
+ } else {
ShowHideScrollBars ();
}
+ }
- void ShowHideScrollBars (bool redraw = true)
- {
- if (!_hosted || (_hosted && !_autoHideScrollBars)) {
- if (_contentBottomRightCorner != null && _contentBottomRightCorner.Visible) {
- _contentBottomRightCorner.Visible = false;
- } else if (_otherScrollBarView != null && _otherScrollBarView._contentBottomRightCorner != null && _otherScrollBarView._contentBottomRightCorner.Visible) {
- _otherScrollBarView._contentBottomRightCorner.Visible = false;
- }
- return;
- }
+ void Host_EnabledChanged (object sender, EventArgs e)
+ {
+ Enabled = Host.Enabled;
+ if (_otherScrollBarView != null) {
+ _otherScrollBarView.Enabled = Enabled;
+ }
+ _contentBottomRightCorner.Enabled = Enabled;
+ }
- var pending = CheckBothScrollBars (this);
- if (_otherScrollBarView != null) {
- CheckBothScrollBars (_otherScrollBarView, pending);
- }
+ //private void Host_CanFocusChanged ()
+ //{
+ // CanFocus = Host.CanFocus;
+ // if (otherScrollBarView != null) {
+ // otherScrollBarView.CanFocus = CanFocus;
+ // }
+ //}
+ void ContentBottomRightCorner_MouseClick (object sender, MouseEventEventArgs me)
+ {
+ if (me.MouseEvent.Flags == MouseFlags.WheeledDown ||
+ me.MouseEvent.Flags == MouseFlags.WheeledUp ||
+ me.MouseEvent.Flags == MouseFlags.WheeledRight ||
+ me.MouseEvent.Flags == MouseFlags.WheeledLeft) {
+
+ MouseEvent (me.MouseEvent);
+ } else if (me.MouseEvent.Flags == MouseFlags.Button1Clicked) {
+ Host.SetFocus ();
+ }
+
+ me.Handled = true;
+ }
+
+ void SetInitialProperties (int size, int position, bool isVertical)
+ {
+ Id = "ScrollBarView";
+ _vertical = isVertical;
+ _position = position;
+ _size = size;
+ WantContinuousButtonPressed = true;
+ ClearOnVisibleFalse = false;
+
+ Added += (s, e) => CreateBottomRightCorner ();
+
+ Initialized += (s, e) => {
SetWidthHeight ();
- SetRelativeLayout (SuperView?.Frame ?? Host.Frame);
- if (_otherScrollBarView != null) {
- OtherScrollBarView.SetRelativeLayout (SuperView?.Frame ?? Host.Frame);
+ SetRelativeLayout (SuperView?.Frame ?? Host?.Frame ?? Frame);
+ // BUGBUG: We're not supposed to use Id internally!
+ if (Id == "OtherScrollBarView" || OtherScrollBarView == null) {
+ // Only do this once if both scrollbars are enabled
+ ShowHideScrollBars ();
}
+ SetPosition (position);
+ };
+ }
- if (_showBothScrollIndicator) {
- if (_contentBottomRightCorner != null) {
- _contentBottomRightCorner.Visible = true;
- } else if (_otherScrollBarView != null && _otherScrollBarView._contentBottomRightCorner != null) {
- _otherScrollBarView._contentBottomRightCorner.Visible = true;
- }
- } else if (!_showScrollIndicator) {
- if (_contentBottomRightCorner != null) {
- _contentBottomRightCorner.Visible = false;
- } else if (_otherScrollBarView != null && _otherScrollBarView._contentBottomRightCorner != null) {
- _otherScrollBarView._contentBottomRightCorner.Visible = false;
- }
- if (Application.MouseGrabView != null && Application.MouseGrabView == this) {
- Application.UngrabMouse ();
- }
- } else if (_contentBottomRightCorner != null) {
+ ///
+ /// This event is raised when the position on the scrollbar has changed.
+ ///
+ public event EventHandler ChangedPosition;
+
+ // Helper to assist Initialized event handler
+ void SetPosition (int newPosition)
+ {
+ if (CanScroll (newPosition - _position, out var max, _vertical)) {
+ if (max == newPosition - _position) {
+ _position = newPosition;
+ } else {
+ _position = Math.Max (_position + max, 0);
+ }
+ } else if (max < 0) {
+ _position = Math.Max (_position + max, 0);
+ } else {
+ _position = Math.Max (newPosition, 0);
+ }
+ OnChangedPosition ();
+ SetNeedsDisplay ();
+ }
+
+ ///
+ /// Virtual method to invoke the action event.
+ ///
+ public virtual void OnChangedPosition () => ChangedPosition?.Invoke (this, EventArgs.Empty);
+
+ ///
+ /// Only used for a hosted view that will update and redraw the scrollbars.
+ ///
+ public virtual void Refresh () => ShowHideScrollBars ();
+
+ void ShowHideScrollBars (bool redraw = true)
+ {
+ if (!_hosted || _hosted && !_autoHideScrollBars) {
+ if (_contentBottomRightCorner != null && _contentBottomRightCorner.Visible) {
+ _contentBottomRightCorner.Visible = false;
+ } else if (_otherScrollBarView != null && _otherScrollBarView._contentBottomRightCorner != null && _otherScrollBarView._contentBottomRightCorner.Visible) {
+ _otherScrollBarView._contentBottomRightCorner.Visible = false;
+ }
+ return;
+ }
+
+ var pending = CheckBothScrollBars (this);
+ if (_otherScrollBarView != null) {
+ CheckBothScrollBars (_otherScrollBarView, pending);
+ }
+
+ SetWidthHeight ();
+ SetRelativeLayout (SuperView?.Frame ?? Host.Frame);
+ if (_otherScrollBarView != null) {
+ OtherScrollBarView.SetRelativeLayout (SuperView?.Frame ?? Host.Frame);
+ }
+
+ if (_showBothScrollIndicator) {
+ if (_contentBottomRightCorner != null) {
+ _contentBottomRightCorner.Visible = true;
+ } else if (_otherScrollBarView != null && _otherScrollBarView._contentBottomRightCorner != null) {
+ _otherScrollBarView._contentBottomRightCorner.Visible = true;
+ }
+ } else if (!_showScrollIndicator) {
+ if (_contentBottomRightCorner != null) {
_contentBottomRightCorner.Visible = false;
} else if (_otherScrollBarView != null && _otherScrollBarView._contentBottomRightCorner != null) {
_otherScrollBarView._contentBottomRightCorner.Visible = false;
}
- if (Host?.Visible == true && _showScrollIndicator && !Visible) {
- Visible = true;
- }
- if (Host?.Visible == true && _otherScrollBarView?._showScrollIndicator == true && !_otherScrollBarView.Visible) {
- _otherScrollBarView.Visible = true;
- }
-
- if (!redraw) {
- return;
- }
-
- if (_showScrollIndicator) {
- Draw ();
- }
- if (_otherScrollBarView != null && _otherScrollBarView._showScrollIndicator) {
- _otherScrollBarView.Draw ();
- }
- if (_contentBottomRightCorner != null && _contentBottomRightCorner.Visible) {
- _contentBottomRightCorner.Draw ();
- } else if (_otherScrollBarView != null && _otherScrollBarView._contentBottomRightCorner != null && _otherScrollBarView._contentBottomRightCorner.Visible) {
- _otherScrollBarView._contentBottomRightCorner.Draw ();
- }
- }
-
- bool CheckBothScrollBars (ScrollBarView scrollBarView, bool pending = false)
- {
- int barsize = scrollBarView._vertical ? scrollBarView.Bounds.Height : scrollBarView.Bounds.Width;
-
- if (barsize == 0 || barsize >= scrollBarView._size) {
- if (scrollBarView._showScrollIndicator) {
- scrollBarView.ShowScrollIndicator = false;
- }
- if (scrollBarView.Visible) {
- scrollBarView.Visible = false;
- }
- } else if (barsize > 0 && barsize == scrollBarView._size && scrollBarView.OtherScrollBarView != null && pending) {
- if (scrollBarView._showScrollIndicator) {
- scrollBarView.ShowScrollIndicator = false;
- }
- if (scrollBarView.Visible) {
- scrollBarView.Visible = false;
- }
- if (scrollBarView.OtherScrollBarView != null && scrollBarView._showBothScrollIndicator) {
- scrollBarView.OtherScrollBarView.ShowScrollIndicator = false;
- }
- if (scrollBarView.OtherScrollBarView.Visible) {
- scrollBarView.OtherScrollBarView.Visible = false;
- }
- } else if (barsize > 0 && barsize == _size && scrollBarView.OtherScrollBarView != null && !pending) {
- pending = true;
- } else {
- if (scrollBarView.OtherScrollBarView != null && pending) {
- if (!scrollBarView._showBothScrollIndicator) {
- scrollBarView.OtherScrollBarView.ShowScrollIndicator = true;
- }
- if (!scrollBarView.OtherScrollBarView.Visible) {
- scrollBarView.OtherScrollBarView.Visible = true;
- }
- }
- if (!scrollBarView._showScrollIndicator) {
- scrollBarView.ShowScrollIndicator = true;
- }
- if (!scrollBarView.Visible) {
- scrollBarView.Visible = true;
- }
- }
-
- return pending;
- }
-
- // BUGBUG: v2 - rationalize this with View.SetMinWidthHeight
- void SetWidthHeight ()
- {
- // BUGBUG: v2 - If Host is also the ScrollBarView's superview, this is all bogus because it's not
- // supported that a view can reference it's superview's Dims. This code also assumes the host does
- // not have a margin/borderframe/padding.
- if (!IsInitialized) {
- return;
- }
-
- if (_showBothScrollIndicator) {
- Width = _vertical ? 1 : Host != SuperView ? Dim.Width (Host) - 1 : Dim.Fill () - 1;
- Height = _vertical ? Host != SuperView ? Dim.Height (Host) - 1 : Dim.Fill () - 1 : 1;
-
- _otherScrollBarView.Width = _otherScrollBarView._vertical ? 1 : Host != SuperView ? Dim.Width (Host) - 1 : Dim.Fill () - 1;
- _otherScrollBarView.Height = _otherScrollBarView._vertical ? Host != SuperView ? Dim.Height (Host) - 1 : Dim.Fill () - 1 : 1;
- } else if (_showScrollIndicator) {
- Width = _vertical ? 1 : Host != SuperView ? Dim.Width (Host) : Dim.Fill ();
- Height = _vertical ? Host != SuperView ? Dim.Height (Host) : Dim.Fill () : 1;
- } else if (_otherScrollBarView?._showScrollIndicator == true) {
- _otherScrollBarView.Width = _otherScrollBarView._vertical ? 1 : Host != SuperView ? Dim.Width (Host) : Dim.Fill () - 0;
- _otherScrollBarView.Height = _otherScrollBarView._vertical ? Host != SuperView ? Dim.Height (Host) : Dim.Fill () - 0 : 1;
- }
- }
-
- int _posTopTee;
- int _posLeftTee;
- int _posBottomTee;
- int _posRightTee;
-
- ///
- public override void OnDrawContent (Rect contentArea)
- {
- if (ColorScheme == null || ((!_showScrollIndicator || Size == 0) && AutoHideScrollBars && Visible)) {
- if ((!_showScrollIndicator || Size == 0) && AutoHideScrollBars && Visible) {
- ShowHideScrollBars (false);
- }
- return;
- }
-
- if (Size == 0 || (_vertical && Bounds.Height == 0) || (!_vertical && Bounds.Width == 0)) {
- return;
- }
-
- Driver.SetAttribute (Host.HasFocus ? ColorScheme.Focus : GetNormalColor ());
-
- if (_vertical) {
- if (Bounds.Right < Bounds.Width - 1) {
- return;
- }
-
- var col = Bounds.Width - 1;
- var bh = Bounds.Height;
- Rune special;
-
- if (bh < 4) {
- var by1 = _position * bh / Size;
- var by2 = (_position + bh) * bh / Size;
-
- Move (col, 0);
- if (Bounds.Height == 1) {
- Driver.AddRune (CM.Glyphs.Diamond);
- } else {
- Driver.AddRune (CM.Glyphs.UpArrow);
- }
- if (Bounds.Height == 3) {
- Move (col, 1);
- Driver.AddRune (CM.Glyphs.Diamond);
- }
- if (Bounds.Height > 1) {
- Move (col, Bounds.Height - 1);
- Driver.AddRune (CM.Glyphs.DownArrow);
- }
- } else {
- bh -= 2;
- var by1 = KeepContentAlwaysInViewport ? _position * bh / Size : _position * bh / (Size + bh);
- var by2 = KeepContentAlwaysInViewport ? Math.Min (((_position + bh) * bh / Size) + 1, bh - 1) : (_position + bh) * bh / (Size + bh);
- if (KeepContentAlwaysInViewport && by1 == by2) {
- by1 = Math.Max (by1 - 1, 0);
- }
-
- Move (col, 0);
- Driver.AddRune (CM.Glyphs.UpArrow);
-
- bool hasTopTee = false;
- bool hasDiamond = false;
- bool hasBottomTee = false;
- for (int y = 0; y < bh; y++) {
- Move (col, y + 1);
- if ((y < by1 || y > by2) && ((_position > 0 && !hasTopTee) || (hasTopTee && hasBottomTee))) {
- special = CM.Glyphs.Stipple;
- } else {
- if (y != by2 && y > 1 && by2 - by1 == 0 && by1 < bh - 1 && hasTopTee && !hasDiamond) {
- hasDiamond = true;
- special = CM.Glyphs.Diamond;
- } else {
- if (y == by1 && !hasTopTee) {
- hasTopTee = true;
- _posTopTee = y;
- special = CM.Glyphs.TopTee;
- } else if ((_position == 0 && y == bh - 1 || y >= by2 || by2 == 0) && !hasBottomTee) {
- hasBottomTee = true;
- _posBottomTee = y;
- special = CM.Glyphs.BottomTee;
- } else {
- special = CM.Glyphs.VLine;
- }
- }
- }
- Driver.AddRune (special);
- }
- if (!hasTopTee) {
- Move (col, Bounds.Height - 2);
- Driver.AddRune (CM.Glyphs.TopTee);
- }
- Move (col, Bounds.Height - 1);
- Driver.AddRune (CM.Glyphs.DownArrow);
- }
- } else {
- if (Bounds.Bottom < Bounds.Height - 1) {
- return;
- }
-
- var row = Bounds.Height - 1;
- var bw = Bounds.Width;
- Rune special;
-
- if (bw < 4) {
- var bx1 = _position * bw / Size;
- var bx2 = (_position + bw) * bw / Size;
-
- Move (0, row);
- Driver.AddRune (CM.Glyphs.LeftArrow);
- Driver.AddRune (CM.Glyphs.RightArrow);
- } else {
- bw -= 2;
- var bx1 = KeepContentAlwaysInViewport ? _position * bw / Size : _position * bw / (Size + bw);
- var bx2 = KeepContentAlwaysInViewport ? Math.Min (((_position + bw) * bw / Size) + 1, bw - 1) : (_position + bw) * bw / (Size + bw);
- if (KeepContentAlwaysInViewport && bx1 == bx2) {
- bx1 = Math.Max (bx1 - 1, 0);
- }
-
- Move (0, row);
- Driver.AddRune (CM.Glyphs.LeftArrow);
-
- bool hasLeftTee = false;
- bool hasDiamond = false;
- bool hasRightTee = false;
- for (int x = 0; x < bw; x++) {
- if ((x < bx1 || x >= bx2 + 1) && ((_position > 0 && !hasLeftTee) || (hasLeftTee && hasRightTee))) {
- special = CM.Glyphs.Stipple;
- } else {
- if (x != bx2 && x > 1 && bx2 - bx1 == 0 && bx1 < bw - 1 && hasLeftTee && !hasDiamond) {
- hasDiamond = true;
- special = CM.Glyphs.Diamond;
- } else {
- if (x == bx1 && !hasLeftTee) {
- hasLeftTee = true;
- _posLeftTee = x;
- special = CM.Glyphs.LeftTee;
- } else if ((_position == 0 && x == bw - 1 || x >= bx2 || bx2 == 0) && !hasRightTee) {
- hasRightTee = true;
- _posRightTee = x;
- special = CM.Glyphs.RightTee;
- } else {
- special = CM.Glyphs.HLine;
- }
- }
- }
- Driver.AddRune (special);
- }
- if (!hasLeftTee) {
- Move (Bounds.Width - 2, row);
- Driver.AddRune (CM.Glyphs.LeftTee);
- }
-
- Driver.AddRune (CM.Glyphs.RightArrow);
- }
- }
- }
-
- int _lastLocation = -1;
- int _posBarOffset;
-
- ///
- public override bool MouseEvent (MouseEvent mouseEvent)
- {
- if (mouseEvent.Flags != MouseFlags.Button1Pressed && mouseEvent.Flags != MouseFlags.Button1DoubleClicked &&
- !mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) &&
- mouseEvent.Flags != MouseFlags.Button1Released && mouseEvent.Flags != MouseFlags.WheeledDown &&
- mouseEvent.Flags != MouseFlags.WheeledUp && mouseEvent.Flags != MouseFlags.WheeledRight &&
- mouseEvent.Flags != MouseFlags.WheeledLeft && mouseEvent.Flags != MouseFlags.Button1TripleClicked) {
-
- return false;
- }
-
- if (!Host.CanFocus) {
- return true;
- }
- if (Host?.HasFocus == false) {
- Host.SetFocus ();
- }
-
- int location = _vertical ? mouseEvent.Y : mouseEvent.X;
- int barsize = _vertical ? Bounds.Height : Bounds.Width;
- int posTopLeftTee = _vertical ? _posTopTee + 1 : _posLeftTee + 1;
- int posBottomRightTee = _vertical ? _posBottomTee + 1 : _posRightTee + 1;
- barsize -= 2;
- var pos = Position;
-
- if (mouseEvent.Flags != MouseFlags.Button1Released
- && (Application.MouseGrabView == null || Application.MouseGrabView != this)) {
- Application.GrabMouse (this);
- } else if (mouseEvent.Flags == MouseFlags.Button1Released && Application.MouseGrabView != null && Application.MouseGrabView == this) {
- _lastLocation = -1;
+ if (Application.MouseGrabView != null && Application.MouseGrabView == this) {
Application.UngrabMouse ();
- return true;
}
- if (_showScrollIndicator && (mouseEvent.Flags == MouseFlags.WheeledDown || mouseEvent.Flags == MouseFlags.WheeledUp ||
- mouseEvent.Flags == MouseFlags.WheeledRight || mouseEvent.Flags == MouseFlags.WheeledLeft)) {
-
- return Host.MouseEvent (mouseEvent);
- }
-
- if (mouseEvent.Flags == MouseFlags.Button1Pressed && location == 0) {
- if (pos > 0) {
- Position = pos - 1;
- }
- } else if (mouseEvent.Flags == MouseFlags.Button1Pressed && location == barsize + 1) {
- if (CanScroll (1, out _, _vertical)) {
- Position = pos + 1;
- }
- } else if (location > 0 && location < barsize + 1) {
- //var b1 = pos * (Size > 0 ? barsize / Size : 0);
- //var b2 = Size > 0
- // ? (KeepContentAlwaysInViewport ? Math.Min (((pos + barsize) * barsize / Size) + 1, barsize - 1) : (pos + barsize) * barsize / Size)
- // : 0;
- //if (KeepContentAlwaysInViewport && b1 == b2) {
- // b1 = Math.Max (b1 - 1, 0);
- //}
-
- if (_lastLocation > -1 || (location >= posTopLeftTee && location <= posBottomRightTee
- && mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition))) {
- if (_lastLocation == -1) {
- _lastLocation = location;
- _posBarOffset = _keepContentAlwaysInViewport ? Math.Max (location - posTopLeftTee, 1) : 0;
- return true;
- }
-
- if (location > _lastLocation) {
- if (location - _posBarOffset < barsize) {
- var np = ((location - _posBarOffset) * Size / barsize) + (Size / barsize);
- if (CanScroll (np - pos, out int nv, _vertical)) {
- Position = pos + nv;
- }
- } else if (CanScroll (Size - pos, out int nv, _vertical)) {
- Position = Math.Min (pos + nv, Size);
- }
- } else if (location < _lastLocation) {
- if (location - _posBarOffset > 0) {
- var np = ((location - _posBarOffset) * Size / barsize) - (Size / barsize);
- if (CanScroll (np - pos, out int nv, _vertical)) {
- Position = pos + nv;
- }
- } else {
- Position = 0;
- }
- } else if (location - _posBarOffset >= barsize && posBottomRightTee - posTopLeftTee >= 3 && CanScroll (Size - pos, out int nv, _vertical)) {
- Position = Math.Min (pos + nv, Size);
- } else if (location - _posBarOffset >= barsize - 1 && posBottomRightTee - posTopLeftTee <= 3 && CanScroll (Size - pos, out nv, _vertical)) {
- Position = Math.Min (pos + nv, Size);
- } else if (location - _posBarOffset <= 0 && posBottomRightTee - posTopLeftTee <= 3) {
- Position = 0;
- }
- } else if (location > posBottomRightTee) {
- if (CanScroll (barsize, out int nv, _vertical)) {
- Position = pos + nv;
- }
- } else if (location < posTopLeftTee) {
- if (CanScroll (-barsize, out int nv, _vertical)) {
- Position = pos + nv;
- }
- } else if (location == 1 && posTopLeftTee <= 3) {
- Position = 0;
- } else if (location == barsize) {
- if (CanScroll (Size - pos, out int nv, _vertical)) {
- Position = Math.Min (pos + nv, Size);
- }
- }
- }
-
- return true;
+ } else if (_contentBottomRightCorner != null) {
+ _contentBottomRightCorner.Visible = false;
+ } else if (_otherScrollBarView != null && _otherScrollBarView._contentBottomRightCorner != null) {
+ _otherScrollBarView._contentBottomRightCorner.Visible = false;
+ }
+ if (Host?.Visible == true && _showScrollIndicator && !Visible) {
+ Visible = true;
+ }
+ if (Host?.Visible == true && _otherScrollBarView?._showScrollIndicator == true && !_otherScrollBarView.Visible) {
+ _otherScrollBarView.Visible = true;
}
- internal bool CanScroll (int n, out int max, bool isVertical = false)
- {
- if (Host?.Bounds.IsEmpty != false) {
- max = 0;
- return false;
+ if (!redraw) {
+ return;
+ }
+
+ if (_showScrollIndicator) {
+ Draw ();
+ }
+ if (_otherScrollBarView != null && _otherScrollBarView._showScrollIndicator) {
+ _otherScrollBarView.Draw ();
+ }
+ if (_contentBottomRightCorner != null && _contentBottomRightCorner.Visible) {
+ _contentBottomRightCorner.Draw ();
+ } else if (_otherScrollBarView != null && _otherScrollBarView._contentBottomRightCorner != null && _otherScrollBarView._contentBottomRightCorner.Visible) {
+ _otherScrollBarView._contentBottomRightCorner.Draw ();
+ }
+ }
+
+ bool CheckBothScrollBars (ScrollBarView scrollBarView, bool pending = false)
+ {
+ var barsize = scrollBarView._vertical ? scrollBarView.Bounds.Height : scrollBarView.Bounds.Width;
+
+ if (barsize == 0 || barsize >= scrollBarView._size) {
+ if (scrollBarView._showScrollIndicator) {
+ scrollBarView.ShowScrollIndicator = false;
}
- int s = GetBarsize (isVertical);
- var newSize = Math.Max (Math.Min (_size - s, _position + n), 0);
- max = _size > s + newSize ? (newSize == 0 ? -_position : n) : _size - (s + _position) - 1;
- if (_size >= s + newSize && max != 0) {
- return true;
+ if (scrollBarView.Visible) {
+ scrollBarView.Visible = false;
}
+ } else if (barsize > 0 && barsize == scrollBarView._size && scrollBarView.OtherScrollBarView != null && pending) {
+ if (scrollBarView._showScrollIndicator) {
+ scrollBarView.ShowScrollIndicator = false;
+ }
+ if (scrollBarView.Visible) {
+ scrollBarView.Visible = false;
+ }
+ if (scrollBarView.OtherScrollBarView != null && scrollBarView._showBothScrollIndicator) {
+ scrollBarView.OtherScrollBarView.ShowScrollIndicator = false;
+ }
+ if (scrollBarView.OtherScrollBarView.Visible) {
+ scrollBarView.OtherScrollBarView.Visible = false;
+ }
+ } else if (barsize > 0 && barsize == _size && scrollBarView.OtherScrollBarView != null && !pending) {
+ pending = true;
+ } else {
+ if (scrollBarView.OtherScrollBarView != null && pending) {
+ if (!scrollBarView._showBothScrollIndicator) {
+ scrollBarView.OtherScrollBarView.ShowScrollIndicator = true;
+ }
+ if (!scrollBarView.OtherScrollBarView.Visible) {
+ scrollBarView.OtherScrollBarView.Visible = true;
+ }
+ }
+ if (!scrollBarView._showScrollIndicator) {
+ scrollBarView.ShowScrollIndicator = true;
+ }
+ if (!scrollBarView.Visible) {
+ scrollBarView.Visible = true;
+ }
+ }
+
+ return pending;
+ }
+
+ // BUGBUG: v2 - rationalize this with View.SetMinWidthHeight
+ void SetWidthHeight ()
+ {
+ // BUGBUG: v2 - If Host is also the ScrollBarView's superview, this is all bogus because it's not
+ // supported that a view can reference it's superview's Dims. This code also assumes the host does
+ // not have a margin/borderframe/padding.
+ if (!IsInitialized) {
+ return;
+ }
+
+ if (_showBothScrollIndicator) {
+ Width = _vertical ? 1 : Host != SuperView ? Dim.Width (Host) - 1 : Dim.Fill () - 1;
+ Height = _vertical ? Host != SuperView ? Dim.Height (Host) - 1 : Dim.Fill () - 1 : 1;
+
+ _otherScrollBarView.Width = _otherScrollBarView._vertical ? 1 : Host != SuperView ? Dim.Width (Host) - 1 : Dim.Fill () - 1;
+ _otherScrollBarView.Height = _otherScrollBarView._vertical ? Host != SuperView ? Dim.Height (Host) - 1 : Dim.Fill () - 1 : 1;
+ } else if (_showScrollIndicator) {
+ Width = _vertical ? 1 : Host != SuperView ? Dim.Width (Host) : Dim.Fill ();
+ Height = _vertical ? Host != SuperView ? Dim.Height (Host) : Dim.Fill () : 1;
+ } else if (_otherScrollBarView?._showScrollIndicator == true) {
+ _otherScrollBarView.Width = _otherScrollBarView._vertical ? 1 : Host != SuperView ? Dim.Width (Host) : Dim.Fill () - 0;
+ _otherScrollBarView.Height = _otherScrollBarView._vertical ? Host != SuperView ? Dim.Height (Host) : Dim.Fill () - 0 : 1;
+ }
+ }
+
+ ///
+ public override void OnDrawContent (Rect contentArea)
+ {
+ if (ColorScheme == null || (!_showScrollIndicator || Size == 0) && AutoHideScrollBars && Visible) {
+ if ((!_showScrollIndicator || Size == 0) && AutoHideScrollBars && Visible) {
+ ShowHideScrollBars (false);
+ }
+ return;
+ }
+
+ if (Size == 0 || _vertical && Bounds.Height == 0 || !_vertical && Bounds.Width == 0) {
+ return;
+ }
+
+ Driver.SetAttribute (Host.HasFocus ? ColorScheme.Focus : GetNormalColor ());
+
+ if (_vertical) {
+ if (Bounds.Right < Bounds.Width - 1) {
+ return;
+ }
+
+ var col = Bounds.Width - 1;
+ var bh = Bounds.Height;
+ Rune special;
+
+ if (bh < 4) {
+ var by1 = _position * bh / Size;
+ var by2 = (_position + bh) * bh / Size;
+
+ Move (col, 0);
+ if (Bounds.Height == 1) {
+ Driver.AddRune (Glyphs.Diamond);
+ } else {
+ Driver.AddRune (Glyphs.UpArrow);
+ }
+ if (Bounds.Height == 3) {
+ Move (col, 1);
+ Driver.AddRune (Glyphs.Diamond);
+ }
+ if (Bounds.Height > 1) {
+ Move (col, Bounds.Height - 1);
+ Driver.AddRune (Glyphs.DownArrow);
+ }
+ } else {
+ bh -= 2;
+ var by1 = KeepContentAlwaysInViewport ? _position * bh / Size : _position * bh / (Size + bh);
+ var by2 = KeepContentAlwaysInViewport ? Math.Min ((_position + bh) * bh / Size + 1, bh - 1) : (_position + bh) * bh / (Size + bh);
+ if (KeepContentAlwaysInViewport && by1 == by2) {
+ by1 = Math.Max (by1 - 1, 0);
+ }
+
+ Move (col, 0);
+ Driver.AddRune (Glyphs.UpArrow);
+
+ var hasTopTee = false;
+ var hasDiamond = false;
+ var hasBottomTee = false;
+ for (var y = 0; y < bh; y++) {
+ Move (col, y + 1);
+ if ((y < by1 || y > by2) && (_position > 0 && !hasTopTee || hasTopTee && hasBottomTee)) {
+ special = Glyphs.Stipple;
+ } else {
+ if (y != by2 && y > 1 && by2 - by1 == 0 && by1 < bh - 1 && hasTopTee && !hasDiamond) {
+ hasDiamond = true;
+ special = Glyphs.Diamond;
+ } else {
+ if (y == by1 && !hasTopTee) {
+ hasTopTee = true;
+ _posTopTee = y;
+ special = Glyphs.TopTee;
+ } else if ((_position == 0 && y == bh - 1 || y >= by2 || by2 == 0) && !hasBottomTee) {
+ hasBottomTee = true;
+ _posBottomTee = y;
+ special = Glyphs.BottomTee;
+ } else {
+ special = Glyphs.VLine;
+ }
+ }
+ }
+ Driver.AddRune (special);
+ }
+ if (!hasTopTee) {
+ Move (col, Bounds.Height - 2);
+ Driver.AddRune (Glyphs.TopTee);
+ }
+ Move (col, Bounds.Height - 1);
+ Driver.AddRune (Glyphs.DownArrow);
+ }
+ } else {
+ if (Bounds.Bottom < Bounds.Height - 1) {
+ return;
+ }
+
+ var row = Bounds.Height - 1;
+ var bw = Bounds.Width;
+ Rune special;
+
+ if (bw < 4) {
+ var bx1 = _position * bw / Size;
+ var bx2 = (_position + bw) * bw / Size;
+
+ Move (0, row);
+ Driver.AddRune (Glyphs.LeftArrow);
+ Driver.AddRune (Glyphs.RightArrow);
+ } else {
+ bw -= 2;
+ var bx1 = KeepContentAlwaysInViewport ? _position * bw / Size : _position * bw / (Size + bw);
+ var bx2 = KeepContentAlwaysInViewport ? Math.Min ((_position + bw) * bw / Size + 1, bw - 1) : (_position + bw) * bw / (Size + bw);
+ if (KeepContentAlwaysInViewport && bx1 == bx2) {
+ bx1 = Math.Max (bx1 - 1, 0);
+ }
+
+ Move (0, row);
+ Driver.AddRune (Glyphs.LeftArrow);
+
+ var hasLeftTee = false;
+ var hasDiamond = false;
+ var hasRightTee = false;
+ for (var x = 0; x < bw; x++) {
+ if ((x < bx1 || x >= bx2 + 1) && (_position > 0 && !hasLeftTee || hasLeftTee && hasRightTee)) {
+ special = Glyphs.Stipple;
+ } else {
+ if (x != bx2 && x > 1 && bx2 - bx1 == 0 && bx1 < bw - 1 && hasLeftTee && !hasDiamond) {
+ hasDiamond = true;
+ special = Glyphs.Diamond;
+ } else {
+ if (x == bx1 && !hasLeftTee) {
+ hasLeftTee = true;
+ _posLeftTee = x;
+ special = Glyphs.LeftTee;
+ } else if ((_position == 0 && x == bw - 1 || x >= bx2 || bx2 == 0) && !hasRightTee) {
+ hasRightTee = true;
+ _posRightTee = x;
+ special = Glyphs.RightTee;
+ } else {
+ special = Glyphs.HLine;
+ }
+ }
+ }
+ Driver.AddRune (special);
+ }
+ if (!hasLeftTee) {
+ Move (Bounds.Width - 2, row);
+ Driver.AddRune (Glyphs.LeftTee);
+ }
+
+ Driver.AddRune (Glyphs.RightArrow);
+ }
+ }
+ }
+
+ ///
+ public override bool MouseEvent (MouseEvent mouseEvent)
+ {
+ if (mouseEvent.Flags != MouseFlags.Button1Pressed &&
+ mouseEvent.Flags != MouseFlags.Button1DoubleClicked &&
+ !mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) &&
+ mouseEvent.Flags != MouseFlags.Button1Released &&
+ mouseEvent.Flags != MouseFlags.WheeledDown &&
+ mouseEvent.Flags != MouseFlags.WheeledUp &&
+ mouseEvent.Flags != MouseFlags.WheeledRight &&
+ mouseEvent.Flags != MouseFlags.WheeledLeft &&
+ mouseEvent.Flags != MouseFlags.Button1TripleClicked) {
+
return false;
}
- int GetBarsize (bool isVertical)
- {
- if (Host?.Bounds.IsEmpty != false) {
- return 0;
+ if (!Host.CanFocus) {
+ return true;
+ }
+ if (Host?.HasFocus == false) {
+ Host.SetFocus ();
+ }
+
+ var location = _vertical ? mouseEvent.Y : mouseEvent.X;
+ var barsize = _vertical ? Bounds.Height : Bounds.Width;
+ var posTopLeftTee = _vertical ? _posTopTee + 1 : _posLeftTee + 1;
+ var posBottomRightTee = _vertical ? _posBottomTee + 1 : _posRightTee + 1;
+ barsize -= 2;
+ var pos = Position;
+
+ if (mouseEvent.Flags != MouseFlags.Button1Released && (Application.MouseGrabView == null || Application.MouseGrabView != this)) {
+ Application.GrabMouse (this);
+ } else if (mouseEvent.Flags == MouseFlags.Button1Released && Application.MouseGrabView != null && Application.MouseGrabView == this) {
+ _lastLocation = -1;
+ Application.UngrabMouse ();
+ return true;
+ }
+ if (_showScrollIndicator &&
+ (mouseEvent.Flags == MouseFlags.WheeledDown ||
+ mouseEvent.Flags == MouseFlags.WheeledUp ||
+ mouseEvent.Flags == MouseFlags.WheeledRight ||
+ mouseEvent.Flags == MouseFlags.WheeledLeft)) {
+
+ return Host.MouseEvent (mouseEvent);
+ }
+
+ if (mouseEvent.Flags == MouseFlags.Button1Pressed && location == 0) {
+ if (pos > 0) {
+ Position = pos - 1;
+ }
+ } else if (mouseEvent.Flags == MouseFlags.Button1Pressed && location == barsize + 1) {
+ if (CanScroll (1, out _, _vertical)) {
+ Position = pos + 1;
+ }
+ } else if (location > 0 && location < barsize + 1) {
+ //var b1 = pos * (Size > 0 ? barsize / Size : 0);
+ //var b2 = Size > 0
+ // ? (KeepContentAlwaysInViewport ? Math.Min (((pos + barsize) * barsize / Size) + 1, barsize - 1) : (pos + barsize) * barsize / Size)
+ // : 0;
+ //if (KeepContentAlwaysInViewport && b1 == b2) {
+ // b1 = Math.Max (b1 - 1, 0);
+ //}
+
+ if (_lastLocation > -1 || location >= posTopLeftTee && location <= posBottomRightTee && mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition)) {
+ if (_lastLocation == -1) {
+ _lastLocation = location;
+ _posBarOffset = _keepContentAlwaysInViewport ? Math.Max (location - posTopLeftTee, 1) : 0;
+ return true;
+ }
+
+ if (location > _lastLocation) {
+ if (location - _posBarOffset < barsize) {
+ var np = (location - _posBarOffset) * Size / barsize + Size / barsize;
+ if (CanScroll (np - pos, out var nv, _vertical)) {
+ Position = pos + nv;
+ }
+ } else if (CanScroll (Size - pos, out var nv, _vertical)) {
+ Position = Math.Min (pos + nv, Size);
+ }
+ } else if (location < _lastLocation) {
+ if (location - _posBarOffset > 0) {
+ var np = (location - _posBarOffset) * Size / barsize - Size / barsize;
+ if (CanScroll (np - pos, out var nv, _vertical)) {
+ Position = pos + nv;
+ }
+ } else {
+ Position = 0;
+ }
+ } else if (location - _posBarOffset >= barsize && posBottomRightTee - posTopLeftTee >= 3 && CanScroll (Size - pos, out var nv, _vertical)) {
+ Position = Math.Min (pos + nv, Size);
+ } else if (location - _posBarOffset >= barsize - 1 && posBottomRightTee - posTopLeftTee <= 3 && CanScroll (Size - pos, out nv, _vertical)) {
+ Position = Math.Min (pos + nv, Size);
+ } else if (location - _posBarOffset <= 0 && posBottomRightTee - posTopLeftTee <= 3) {
+ Position = 0;
+ }
+ } else if (location > posBottomRightTee) {
+ if (CanScroll (barsize, out var nv, _vertical)) {
+ Position = pos + nv;
+ }
+ } else if (location < posTopLeftTee) {
+ if (CanScroll (-barsize, out var nv, _vertical)) {
+ Position = pos + nv;
+ }
+ } else if (location == 1 && posTopLeftTee <= 3) {
+ Position = 0;
+ } else if (location == barsize) {
+ if (CanScroll (Size - pos, out var nv, _vertical)) {
+ Position = Math.Min (pos + nv, Size);
+ }
}
- return isVertical ?
- (KeepContentAlwaysInViewport ? Host.Bounds.Height + (_showBothScrollIndicator ? -2 : -1) : 0) :
- (KeepContentAlwaysInViewport ? Host.Bounds.Width + (_showBothScrollIndicator ? -2 : -1) : 0);
}
- ///
- public override bool OnEnter (View view)
- {
- Application.Driver.SetCursorVisibility (CursorVisibility.Invisible);
-
- return base.OnEnter (view);
- }
+ return true;
}
-}
+
+ internal bool CanScroll (int n, out int max, bool isVertical = false)
+ {
+ if (Host?.Bounds.IsEmpty != false) {
+ max = 0;
+ return false;
+ }
+ var s = GetBarsize (isVertical);
+ var newSize = Math.Max (Math.Min (_size - s, _position + n), 0);
+ max = _size > s + newSize ? newSize == 0 ? -_position : n : _size - (s + _position) - 1;
+ if (_size >= s + newSize && max != 0) {
+ return true;
+ }
+ return false;
+ }
+
+ int GetBarsize (bool isVertical)
+ {
+ if (Host?.Bounds.IsEmpty != false) {
+ return 0;
+ }
+ return isVertical ?
+ KeepContentAlwaysInViewport ? Host.Bounds.Height + (_showBothScrollIndicator ? -2 : -1) : 0 :
+ KeepContentAlwaysInViewport ? Host.Bounds.Width + (_showBothScrollIndicator ? -2 : -1) : 0;
+ }
+
+ ///
+ public override bool OnEnter (View view)
+ {
+ Application.Driver.SetCursorVisibility (CursorVisibility.Invisible);
+
+ return base.OnEnter (view);
+ }
+}
\ No newline at end of file
diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs
index 024942f11..f8354eee4 100644
--- a/Terminal.Gui/Views/TextField.cs
+++ b/Terminal.Gui/Views/TextField.cs
@@ -227,7 +227,6 @@ namespace Terminal.Gui {
if (Frame.Height > 1) {
Height = 1;
}
- Adjust ();
}
diff --git a/UICatalog/Scenarios/TabViewExample.cs b/UICatalog/Scenarios/TabViewExample.cs
index 2ef1bb235..e847afb23 100644
--- a/UICatalog/Scenarios/TabViewExample.cs
+++ b/UICatalog/Scenarios/TabViewExample.cs
@@ -59,7 +59,7 @@ namespace UICatalog.Scenarios {
};
tabView.AddTab (new Tab ("Tab1", new Label ("hodor!")), false);
- tabView.AddTab (new Tab ("Tab2", new Label ("durdur")), false);
+ tabView.AddTab (new Tab ("Tab2", new TextField ("durdur")), false);
tabView.AddTab (new Tab ("Interactive Tab", GetInteractiveTab ()), false);
tabView.AddTab (new Tab ("Big Text", GetBigTextFileTab ()), false);
tabView.AddTab (new Tab (
diff --git a/UICatalog/Scenarios/Text.cs b/UICatalog/Scenarios/Text.cs
index 48320ee4b..b58007e11 100644
--- a/UICatalog/Scenarios/Text.cs
+++ b/UICatalog/Scenarios/Text.cs
@@ -233,7 +233,7 @@ namespace UICatalog.Scenarios {
};
var appendAutocompleteTextField = new TextField () {
X = Pos.Right (labelAppendAutocomplete),
- Y = labelAppendAutocomplete.Y,
+ Y = Pos.Bottom (labelAppendAutocomplete),
Width = Dim.Fill ()
};
appendAutocompleteTextField.Autocomplete = new AppendAutocomplete (appendAutocompleteTextField);
diff --git a/UnitTests/Dialogs/DialogTests.cs b/UnitTests/Dialogs/DialogTests.cs
index 1e97ccdd5..32a81a43c 100644
--- a/UnitTests/Dialogs/DialogTests.cs
+++ b/UnitTests/Dialogs/DialogTests.cs
@@ -181,18 +181,33 @@ namespace Terminal.Gui.DialogTests {
// This is because of PostionTopLevels and EnsureVisibleBounds
Assert.Equal (new Point (3, 2), d.Frame.Location);
- Assert.Equal (new Size (17, 8), d.Frame.Size);
+ // #3127: Before
+ // Assert.Equal (new Size (17, 8), d.Frame.Size);
+ // TestHelpers.AssertDriverContentsWithFrameAre (@"
+ //╔══════════════════╗
+ //║ ║
+ //║ ┌───────────────┐
+ //║ │ │
+ //║ │ │
+ //║ │ │
+ //║ │ │
+ //║ │ │
+ //║ │ │
+ //╚══└───────────────┘", output);
+
+ // #3127: After: Because Toplevel is now Width/Height = Dim.Filll
+ Assert.Equal (new Size (15, 6), d.Frame.Size);
TestHelpers.AssertDriverContentsWithFrameAre (@"
╔══════════════════╗
║ ║
-║ ┌───────────────┐
-║ │ │
-║ │ │
-║ │ │
-║ │ │
-║ │ │
-║ │ │
-╚══└───────────────┘", output);
+║ ┌─────────────┐ ║
+║ │ │ ║
+║ │ │ ║
+║ │ │ ║
+║ │ │ ║
+║ └─────────────┘ ║
+║ ║
+╚══════════════════╝", output);
} else if (iterations > 0) {
Application.RequestStop ();
@@ -971,20 +986,11 @@ namespace Terminal.Gui.DialogTests {
Application.Refresh ();
Assert.Equal (new Rect (10, 0, 6, 1), btn.Frame);
Assert.Equal (new Rect (0, 0, 6, 1), btn.Bounds);
- // #3127: Before: This test was clearly wrong before. The math above is correct, but the result is wrong.
- // var expected = @$"
- //┌──────────────────┐
- //│┌────────────────┐│
- //││23456789 {b}││
- //│└────────────────┘│
- //└──────────────────┘";
- // #3127: After: This test was clearly wrong before. The math above is correct, but the result is wrong.
- // See also `PosDimFunction` in SetRelativeLayoutTests.cs
var expected = @$"
┌──────────────────┐
│┌────────────────┐│
-││012345678 {b}││
+││23456789 {b}││
│└────────────────┘│
└──────────────────┘";
@@ -998,7 +1004,7 @@ namespace Terminal.Gui.DialogTests {
expected = @$"
┌──────────────────┐
│┌────────────────┐│
-││012345678 {b}││
+││23456789 {b}││
│└────────────────┘│
└──────────────────┘";
_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
diff --git a/UnitTests/View/ViewTests.cs b/UnitTests/View/ViewTests.cs
index 6e6debc5a..d008c81e1 100644
--- a/UnitTests/View/ViewTests.cs
+++ b/UnitTests/View/ViewTests.cs
@@ -265,20 +265,20 @@ namespace Terminal.Gui.ViewTests {
int tc = 0, wc = 0, v1c = 0, v2c = 0, sv1c = 0;
winAddedToTop.Added += (s, e) => {
- Assert.Equal (e.Parent.Bounds.Width, winAddedToTop.Frame.Width);
- Assert.Equal (e.Parent.Bounds.Height, winAddedToTop.Frame.Height);
+ Assert.Equal (e.Parent.Frame.Width, winAddedToTop.Frame.Width);
+ Assert.Equal (e.Parent.Frame.Height, winAddedToTop.Frame.Height);
};
v1AddedToWin.Added += (s, e) => {
- Assert.Equal (e.Parent.Bounds.Width, v1AddedToWin.Frame.Width);
- Assert.Equal (e.Parent.Bounds.Height, v1AddedToWin.Frame.Height);
+ Assert.Equal (e.Parent.Frame.Width, v1AddedToWin.Frame.Width);
+ Assert.Equal (e.Parent.Frame.Height, v1AddedToWin.Frame.Height);
};
v2AddedToWin.Added += (s, e) => {
- Assert.Equal (e.Parent.Bounds.Width, v2AddedToWin.Frame.Width);
- Assert.Equal (e.Parent.Bounds.Height, v2AddedToWin.Frame.Height);
+ Assert.Equal (e.Parent.Frame.Width, v2AddedToWin.Frame.Width);
+ Assert.Equal (e.Parent.Frame.Height, v2AddedToWin.Frame.Height);
};
svAddedTov1.Added += (s, e) => {
- Assert.Equal (e.Parent.Bounds.Width, svAddedTov1.Frame.Width);
- Assert.Equal (e.Parent.Bounds.Height, svAddedTov1.Frame.Height);
+ Assert.Equal (e.Parent.Frame.Width, svAddedTov1.Frame.Width);
+ Assert.Equal (e.Parent.Frame.Height, svAddedTov1.Frame.Height);
};
top.Initialized += (s, e) => {
diff --git a/UnitTests/Views/ScrollViewTests.cs b/UnitTests/Views/ScrollViewTests.cs
index c5cf6bc1e..0b55e5538 100644
--- a/UnitTests/Views/ScrollViewTests.cs
+++ b/UnitTests/Views/ScrollViewTests.cs
@@ -188,6 +188,8 @@ namespace Terminal.Gui.ViewsTests {
Application.Top.Add (sv);
Application.Begin (Application.Top);
+ Assert.Equal (new Rect (0, 0, 10, 10), sv.Bounds);
+
Assert.False (sv.AutoHideScrollBars);
Assert.True (sv.ShowHorizontalScrollIndicator);
Assert.True (sv.ShowVerticalScrollIndicator);
@@ -206,7 +208,9 @@ namespace Terminal.Gui.ViewsTests {
", output);
sv.ShowHorizontalScrollIndicator = false;
+ Assert.Equal (new Rect (0, 0, 10, 10), sv.Bounds);
sv.ShowVerticalScrollIndicator = true;
+ Assert.Equal (new Rect (0, 0, 10, 10), sv.Bounds);
Assert.False (sv.AutoHideScrollBars);
Assert.False (sv.ShowHorizontalScrollIndicator);
@@ -220,6 +224,7 @@ namespace Terminal.Gui.ViewsTests {
│
│
│
+ │
┴
▼
", output);
@@ -241,7 +246,7 @@ namespace Terminal.Gui.ViewsTests {
-◄├─────┤►
+◄├──────┤►
", output);
sv.ShowHorizontalScrollIndicator = false;
diff --git a/UnitTests/Views/TabViewTests.cs b/UnitTests/Views/TabViewTests.cs
index ea049a299..f62b95789 100644
--- a/UnitTests/Views/TabViewTests.cs
+++ b/UnitTests/Views/TabViewTests.cs
@@ -33,7 +33,7 @@ namespace Terminal.Gui.ViewsTests {
tv.BeginInit ();
tv.EndInit ();
tv.ColorScheme = new ColorScheme ();
- tv.AddTab (tab1 = new Tab ("Tab1", new TextField ("hi")), false);
+ tv.AddTab (tab1 = new Tab ("Tab1", new TextField ("hi") { Width = 2 }), false);
tv.AddTab (tab2 = new Tab ("Tab2", new Label ("hi2")), false);
return tv;
}