Browse Source

ScrollView

Miguel de Icaza 7 years ago
parent
commit
3f2c6b6f97
2 changed files with 195 additions and 65 deletions
  1. 192 64
      Terminal.Gui/Views/ScrollView.cs
  2. 3 1
      demo.cs

+ 192 - 64
Terminal.Gui/Views/ScrollView.cs

@@ -1,7 +1,167 @@
-using System;
+//
+// ScrollView.cs: ScrollView and ScrollBarView views.
+//
+// Authors:
+//   Miguel de Icaza ([email protected])
+//
+//
+// TODO:
+// - Mouse handling in scrollbarview
+// - focus in scrollview
+// - keyboard handling in scrollview to scroll
+// - focus handling in scrollview to auto scroll to focused view
+// - Raise events
+using System;
 namespace Terminal.Gui {
 	/// <summary>
-	/// 
+	/// ScrollBarViews are views that display a 1-character scrollbar, either horizontal or vertical
+	/// </summary>
+	/// <remarks>
+	/// The scrollbar is drawn to be a representation of the Size, assuming that the 
+	/// scroll position is set at Position.
+	/// </remarks>
+	public class ScrollBarView : View {
+		bool vertical;
+		int size, position;
+
+		/// <summary>
+		/// The size that this scrollbar represents
+		/// </summary>
+		/// <value>The size.</value>
+		public int Size {
+			get => size;
+			set {
+				size = value;
+				SetNeedsDisplay ();
+			}
+		}
+
+		/// <summary>
+		/// The position to show the scrollbar at.
+		/// </summary>
+		/// <value>The position.</value>
+		public int Position {
+			get => position;
+			set {
+				position = value;
+				SetNeedsDisplay ();
+			}
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="T:Terminal.Gui.ScrollBarView"/> class.
+		/// </summary>
+		/// <param name="rect">Frame for the scrollbar.</param>
+		/// <param name="size">The size that this scrollbar represents.</param>
+		/// <param name="position">The position within this scrollbar.</param>
+		/// <param name="isVertical">If set to <c>true</c> this is a vertical scrollbar, otherwize, the scrollbar is horizontal.</param>
+		public ScrollBarView (Rect rect, int size, int position, bool isVertical) : base (rect)
+		{
+			vertical = isVertical;
+			this.position = position;
+			this.size = size;
+		}
+
+		/// <summary>
+		/// Redraw the scrollbar
+		/// </summary>
+		/// <param name="region">Region to be redrawn.</param>
+		public override void Redraw(Rect region)
+		{
+			Driver.SetAttribute (ColorScheme.Normal);
+
+			if (vertical) {
+				if (region.Right < Bounds.Width - 1)
+					return;
+
+				var col = Bounds.Width - 1;
+				var bh = Bounds.Height;
+				SpecialChar special;
+
+				if (bh < 3) {
+					var by1 = position * bh / Size;
+					var by2 = (position + bh) * bh / Size;
+
+					for (int y = 0; y < bh; y++) {
+						Move (col, y);
+						if (y < by1 || y > by2)
+							special = SpecialChar.Stipple;
+						else
+							special = SpecialChar.Diamond;
+						Driver.AddSpecial (special);
+					}
+				} else {
+					bh -= 2;
+					var by1 = position * bh / Size;
+					var by2 = (position + bh) * bh / Size;
+
+					
+					Move (col, 0);
+					Driver.AddRune ('^');
+					Move (col, Bounds.Height - 1);
+					Driver.AddRune ('v');
+					for (int y = 0; y < bh; y++) {
+						Move (col, y+1);
+
+						if (y < by1 || y > by2)
+							special = SpecialChar.Stipple;
+						else {
+							if (by2 - by1 == 0)
+								special = SpecialChar.Diamond;
+							else {
+								if (y == by1)
+									special = SpecialChar.TopTee;
+								else if (y == by2)
+									special = SpecialChar.BottomTee;
+								else
+									special = SpecialChar.VLine;
+							}
+						}
+						Driver.AddSpecial (special);
+					}
+				}
+			} else {
+				if (region.Bottom < Bounds.Height - 1)
+					return;
+
+				var row = Bounds.Height - 1;
+				var bw = Bounds.Width;
+				if (bw < 3) {
+				} else {
+					bw -= 2;
+					var bx1 = position * bw / Size;
+					var bx2 = (position + bw) * bw / Size;
+
+					Move (0, row);
+					Driver.AddRune ('<');
+
+					for (int x = 0; x < bw; x++) {
+						SpecialChar special;
+
+						if (x < bx1 || x > bx2) {
+							special = SpecialChar.Stipple;
+						} else {
+							if (bx2 - bx1 == 0)
+								special = SpecialChar.Diamond;
+							else {
+								if (x == bx1)
+									special = SpecialChar.LeftTee;
+								else if (x == bx2)
+									special = SpecialChar.RightTee;
+								else
+									special = SpecialChar.HLine;
+							}
+						}
+						Driver.AddSpecial (special);
+					}
+					Driver.AddRune ('>');
+				}
+			}
+		}
+	}
+
+	/// <summary>
+	/// Scrollviews are views that present a window into a virtual space where children views are added.  Similar to the iOS UIScrollView.
 	/// </summary>
 	/// <remarks>
 	/// <para>
@@ -15,11 +175,15 @@ namespace Terminal.Gui {
 	/// </remarks>
 	public class ScrollView : View {
 		View contentView;
+		ScrollBarView vertical, horizontal;
 
 		public ScrollView (Rect frame) : base (frame)
 		{
 			contentView = new View (frame);
+			vertical = new ScrollBarView (new Rect (frame.Width - 1, 0, 1, frame.Height), frame.Height, 0, isVertical: true);
+			horizontal = new ScrollBarView (new Rect (0, frame.Height-1, frame.Width-1, 1), frame.Width-1, 0, isVertical: false);
 			base.Add (contentView);
+			CanFocus = true;
 		}
 
 		Size contentSize;
@@ -38,6 +202,8 @@ namespace Terminal.Gui {
 			set {
 				contentSize = value;
 				contentView.Frame = new Rect (contentOffset, value);
+				vertical.Size = contentSize.Height;
+				horizontal.Size = contentSize.Width;
 			}
 		}
 
@@ -52,6 +218,8 @@ namespace Terminal.Gui {
 			set {
 				contentOffset = new Point (-value.X, -value.Y);
 				contentView.Frame = new Rect (contentOffset, contentSize);
+				vertical.Position = Math.Max (0, -contentOffset.Y);
+				horizontal.Position = Math.Max (0, -contentOffset.X);
 			}
 		}
 
@@ -71,8 +239,15 @@ namespace Terminal.Gui {
 		public bool ShowHorizontalScrollIndicator {
 			get => showHorizontalScrollIndicator;
 			set {
+				if (value == showHorizontalScrollIndicator)
+					return;
+				
 				showHorizontalScrollIndicator = value;
 				SetNeedsDisplay ();
+				if (value)
+					base.Add (horizontal);
+				else
+					Remove (horizontal);
 			}
 		}
 
@@ -84,8 +259,15 @@ namespace Terminal.Gui {
 		public bool ShowVerticalScrollIndicator {
 			get => showVerticalScrollIndicator;
 			set {
+				if (value == showVerticalScrollIndicator)
+					return;
+				
 				showVerticalScrollIndicator = value;
 				SetNeedsDisplay ();
+				if (value)
+					base.Add (vertical);
+				else
+					Remove (vertical);
 			}
 		}
 
@@ -98,70 +280,16 @@ namespace Terminal.Gui {
 		{
 			var oldClip = ClipToBounds ();
 			base.Redraw(region);
-			Attribute last = ColorScheme.Normal;
-			Driver.SetAttribute (last);
-
-			void SetColor (Attribute a)
-			{
-				if (a != last)
-					Driver.SetAttribute (a);
-				last = a;
-			}
-
 			Driver.Clip = oldClip;
+			Driver.SetAttribute (ColorScheme.Normal);
+		}
 
-			if (true || ShowVerticalScrollIndicator) {
-				var bh = Bounds.Height;
-				var by1 = -contentOffset.Y * bh/ contentSize.Height;
-				var by2 = (-contentOffset.Y+bh) * bh/ contentSize.Height;
-
-				for (int y = 0; y < bh; y++) {
-					Move (Bounds.Width - 1, y);
-					SpecialChar special;
-
-					if (y < by1 || y > by2)
-						special = SpecialChar.Stipple;
-					else {
-						if (by2 - by1 == 0)
-							special = SpecialChar.Diamond;
-						else {
-							if (y == by1)
-								special = SpecialChar.TopTee;
-							else if (y == by2)
-								special = SpecialChar.BottomTee;
-							else
-								special = SpecialChar.VLine;
-						}
-					}
-					Driver.AddSpecial (special);
-				}
-			}
-			if (true || ShowHorizontalScrollIndicator){
-				var bw = Bounds.Width;
-				var bx1 = -contentOffset.X * bw / contentSize.Width;
-				var bx2 = (-contentOffset.X + bw) * bw / contentSize.Width;
-
-				Move (0, Bounds.Height - 1);
-				for (int x = 0; x < bw; x++) {
-					SpecialChar special;
-
-					if (x < bx1 || x > bx2){
-						special = SpecialChar.Stipple;
-					} else {
-						if (bx2 - bx1 == 0)
-							special = SpecialChar.Diamond;
-						else {
-							if (x == bx1)
-								special = SpecialChar.LeftTee;
-							else if (x == bx2)
-								special = SpecialChar.RightTee;
-							else
-								special = SpecialChar.HLine;
-						}
-					}
-					Driver.AddSpecial (special);
-				}
-			}
+		public override void PositionCursor()
+		{
+			if (Subviews.Count == 0)
+				Driver.Move (0, 0);
+			else
+				base.PositionCursor ();
 		}
 	}
 }

+ 3 - 1
demo.cs

@@ -67,7 +67,9 @@ class Demo {
 	{
 		var scrollView = new ScrollView (new Rect (50, 10, 20, 8)) {
 			ContentSize = new Size (100, 100),
-			ContentOffset = new Point (5, -2)
+			ContentOffset = new Point (5, -2),
+			ShowVerticalScrollIndicator = true,
+			ShowHorizontalScrollIndicator = true
 		};
 
 		//scrollView.Add (new Box10x (0, 0));