Browse Source

Allowing negative coordinates for scrolling purpose.

BDisp 5 years ago
parent
commit
2766af8afe
4 changed files with 138 additions and 68 deletions
  1. 31 6
      Example/demo.cs
  2. 44 9
      Terminal.Gui/Core/View.cs
  3. 31 46
      Terminal.Gui/Views/ScrollView.cs
  4. 32 7
      UICatalog/Scenarios/Scrolling.cs

+ 31 - 6
Example/demo.cs

@@ -9,7 +9,6 @@ using System.Reflection;
 using Terminal.Gui;
 using Terminal.Gui;
 
 
 static class Demo {
 static class Demo {
-	//class Box10x : View, IScrollView {
 	class Box10x : View {
 	class Box10x : View {
 		int w = 40;
 		int w = 40;
 		int h = 50;
 		int h = 50;
@@ -49,24 +48,42 @@ static class Demo {
 	}
 	}
 
 
 	class Filler : View {
 	class Filler : View {
+		int w = 40;
+		int h = 50;
+
 		public Filler (Rect rect) : base (rect)
 		public Filler (Rect rect) : base (rect)
 		{
 		{
+			w = rect.Width;
+			h = rect.Height;
+		}
+
+		public Size GetContentSize ()
+		{
+			return new Size (w, h);
 		}
 		}
 
 
 		public override void Redraw (Rect bounds)
 		public override void Redraw (Rect bounds)
 		{
 		{
 			Driver.SetAttribute (ColorScheme.Focus);
 			Driver.SetAttribute (ColorScheme.Focus);
 			var f = Frame;
 			var f = Frame;
+			w = 0;
+			h = 0;
 
 
 			for (int y = 0; y < f.Width; y++) {
 			for (int y = 0; y < f.Width; y++) {
-				Move (0, y);
+				Move (0, y, true);
+				var nw = 0;
 				for (int x = 0; x < f.Height; x++) {
 				for (int x = 0; x < f.Height; x++) {
 					Rune r;
 					Rune r;
 					switch (x % 3) {
 					switch (x % 3) {
 					case 0:
 					case 0:
-						Driver.AddRune (y.ToString ().ToCharArray (0, 1) [0]);
-						if (y > 9)
-							Driver.AddRune (y.ToString ().ToCharArray (1, 1) [0]);
+						var er = y.ToString ().ToCharArray (0, 1) [0];
+						nw += er.ToString ().Length;
+						Driver.AddRune (er);
+						if (y > 9) {
+							er = y.ToString ().ToCharArray (1, 1) [0];
+							nw += er.ToString ().Length;
+							Driver.AddRune (er);
+						}
 						r = '.';
 						r = '.';
 						break;
 						break;
 					case 1:
 					case 1:
@@ -77,7 +94,11 @@ static class Demo {
 						break;
 						break;
 					}
 					}
 					Driver.AddRune (r);
 					Driver.AddRune (r);
+					nw += Rune.RuneLen (r);
 				}
 				}
+				if (nw > w)
+					w = nw;
+				h = y + 1;
 			}
 			}
 		}
 		}
 	}
 	}
@@ -118,7 +139,11 @@ static class Demo {
 #if false
 #if false
 		scrollView.Add (new Box10x (0, 0));
 		scrollView.Add (new Box10x (0, 0));
 #else
 #else
-		scrollView.Add (new Filler (new Rect (0, 0, 40, 40)));
+		var filler = new Filler (new Rect (0, 0, 40, 40));
+		scrollView.Add (filler);
+		scrollView.DrawContent = (r) => {
+			scrollView.ContentSize = filler.GetContentSize ();
+		};
 #endif
 #endif
 
 
 		// This is just to debug the visuals of the scrollview when small
 		// This is just to debug the visuals of the scrollview when small

+ 44 - 9
Terminal.Gui/Core/View.cs

@@ -118,7 +118,7 @@ namespace Terminal.Gui {
 	///    in color as well as black and white displays.
 	///    in color as well as black and white displays.
 	/// </para>
 	/// </para>
 	/// <para>
 	/// <para>
-	///    Views that are focusable should implement the <see cref="PositionCursor"/> to make sure that
+	///    Views that are focusable should implement the <see cref="PositionCursor ()"/> to make sure that
 	///    the cursor is placed in a location that makes sense.  Unix terminals do not have
 	///    the cursor is placed in a location that makes sense.  Unix terminals do not have
 	///    a way of hiding the cursor, so it can be distracting to have the cursor left at
 	///    a way of hiding the cursor, so it can be distracting to have the cursor left at
 	///    the last focused view.   So views should make sure that they place the cursor
 	///    the last focused view.   So views should make sure that they place the cursor
@@ -667,7 +667,8 @@ namespace Terminal.Gui {
 		/// <param name="rcol">Absolute column; screen-relative.</param>
 		/// <param name="rcol">Absolute column; screen-relative.</param>
 		/// <param name="rrow">Absolute row; screen-relative.</param>
 		/// <param name="rrow">Absolute row; screen-relative.</param>
 		/// <param name="clipped">Whether to clip the result of the ViewToScreen method, if set to <c>true</c>, the rcol, rrow values are clamped to the screen (terminal) dimensions (0..TerminalDim-1).</param>
 		/// <param name="clipped">Whether to clip the result of the ViewToScreen method, if set to <c>true</c>, the rcol, rrow values are clamped to the screen (terminal) dimensions (0..TerminalDim-1).</param>
-		internal void ViewToScreen (int col, int row, out int rcol, out int rrow, bool clipped = true)
+		/// <param name="force">If <true/>force negative coordinates <false/>otherwise.</param>
+		internal void ViewToScreen (int col, int row, out int rcol, out int rrow, bool clipped = true, bool force = true)
 		{
 		{
 			// Computes the real row, col relative to the screen.
 			// Computes the real row, col relative to the screen.
 			rrow = row + frame.Y;
 			rrow = row + frame.Y;
@@ -680,10 +681,14 @@ namespace Terminal.Gui {
 			}
 			}
 
 
 			// The following ensures that the cursor is always in the screen boundaries.
 			// The following ensures that the cursor is always in the screen boundaries.
-			if (clipped) {
+			if (clipped && !force) {
 				rrow = Math.Max (0, Math.Min (rrow, Driver.Rows - 1));
 				rrow = Math.Max (0, Math.Min (rrow, Driver.Rows - 1));
 				rcol = Math.Max (0, Math.Min (rcol, Driver.Cols - 1));
 				rcol = Math.Max (0, Math.Min (rcol, Driver.Cols - 1));
 			}
 			}
+			if (clipped && force) {
+				rrow = Math.Min (rrow, Driver.Rows - 1);
+				rcol = Math.Min (rcol, Driver.Cols - 1);
+			}
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
@@ -799,18 +804,31 @@ namespace Terminal.Gui {
 		/// This moves the cursor to the specified column and row in the view.
 		/// This moves the cursor to the specified column and row in the view.
 		/// </summary>
 		/// </summary>
 		/// <returns>The move.</returns>
 		/// <returns>The move.</returns>
-		/// <param name="col">Col (view-relative).</param>
-		/// <param name="row">Row (view-relative).</param>
-		public void Move (int col, int row)
+		/// <param name="col">Col.</param>
+		/// <param name="row">Row.</param>
+		/// <param name="force">If <true/>force move <false/>otherwise.</param>
+		public void Move (int col, int row, bool force = true)
 		{
 		{
-			ViewToScreen (col, row, out var rcol, out var rrow);
+			int c = 0, r = 0;
+
+			if (!force && SuperView != null) {
+				if (col == 0)
+					c = SuperView.Frame.X;
+				if (row == 0)
+					r = SuperView.Frame.Y;
+			} else if (SuperView != null) {
+				c = col;
+				r = row;
+			}
+
+			ViewToScreen (c, r, out var rcol, out var rrow, force);
 			Driver.Move (rcol, rrow);
 			Driver.Move (rcol, rrow);
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
 		///   Positions the cursor in the right position based on the currently focused view in the chain.
 		///   Positions the cursor in the right position based on the currently focused view in the chain.
 		/// </summary>
 		/// </summary>
-		///    Views that are focusable should override <see cref="PositionCursor"/> to ensure
+		///    Views that are focusable should override <see cref="PositionCursor ()"/> to ensure
 		///    the cursor is placed in a location that makes sense. Unix terminals do not have
 		///    the cursor is placed in a location that makes sense. Unix terminals do not have
 		///    a way of hiding the cursor, so it can be distracting to have the cursor left at
 		///    a way of hiding the cursor, so it can be distracting to have the cursor left at
 		///    the last focused view. Views should make sure that they place the cursor
 		///    the last focused view. Views should make sure that they place the cursor
@@ -823,6 +841,23 @@ namespace Terminal.Gui {
 				Move (frame.X, frame.Y);
 				Move (frame.X, frame.Y);
 		}
 		}
 
 
+		/// <summary>
+		///   Positions the cursor in the right position based on the currently focused view in the chain.
+		/// </summary>
+		/// <param name="force">If <true/>force move <false/>otherwise.</param>
+		///    Views that are focusable should override <see cref="PositionCursor (bool)"/> to ensure
+		///    the cursor is placed in a location that makes sense. Unix terminals do not have
+		///    a way of hiding the cursor, so it can be distracting to have the cursor left at
+		///    the last focused view. Views should make sure that they place the cursor
+		///    in a visually sensible place.
+		public virtual void PositionCursor (bool force = true)
+		{
+			if (focused != null)
+				focused.PositionCursor (force);
+			else
+				Move (frame.X, frame.Y, force);
+		}
+
 		/// <inheritdoc/>
 		/// <inheritdoc/>
 		public override bool HasFocus {
 		public override bool HasFocus {
 			get {
 			get {
@@ -977,7 +1012,7 @@ namespace Terminal.Gui {
 			if (subviews != null) {
 			if (subviews != null) {
 				foreach (var view in subviews) {
 				foreach (var view in subviews) {
 					if (view.NeedDisplay != null && (!view.NeedDisplay.IsEmpty || view.childNeedsDisplay)) {
 					if (view.NeedDisplay != null && (!view.NeedDisplay.IsEmpty || view.childNeedsDisplay)) {
-						if (view.Frame.IntersectsWith (clipRect) && view.Frame.IntersectsWith (bounds)) {
+						if (view.Frame.IntersectsWith (clipRect) && (view.Frame.IntersectsWith (bounds) || bounds.X < 0 || bounds.Y < 0)) {
 
 
 							// FIXED: optimize this by computing the intersection of region and view.Bounds
 							// FIXED: optimize this by computing the intersection of region and view.Bounds
 							if (view.layoutNeeded)
 							if (view.layoutNeeded)

+ 31 - 46
Terminal.Gui/Views/ScrollView.cs

@@ -142,14 +142,10 @@ namespace Terminal.Gui {
 					var by1 = position * bh / Size;
 					var by1 = position * bh / Size;
 					var by2 = (position + bh) * bh / Size;
 					var by2 = (position + bh) * bh / Size;
 
 
-					for (int y = 0; y < bh; y++) {
-						Move (col, y);
-						if (y < by1 || y > by2)
-							special = Driver.Stipple;
-						else
-							special = Driver.Diamond;
-						Driver.AddRune (special);
-					}
+					Move (col, 0);
+					Driver.AddRune (Driver.UpArrow);
+					Move (col, Bounds.Height - 1);
+					Driver.AddRune (Driver.DownArrow);
 				} else {
 				} else {
 					bh -= 2;
 					bh -= 2;
 					var by1 = position * bh / Size;
 					var by1 = position * bh / Size;
@@ -202,14 +198,9 @@ namespace Terminal.Gui {
 					var bx1 = position * bw / Size;
 					var bx1 = position * bw / Size;
 					var bx2 = (position + bw) * bw / Size;
 					var bx2 = (position + bw) * bw / Size;
 
 
-					for (int x = 0; x < bw; x++) {
-						Move (0, x);
-						if (x < bx1 || x > bx2)
-							special = Driver.Stipple;
-						else
-							special = Driver.Diamond;
-						Driver.AddRune (special);
-					}
+					Move (0, row);
+					Driver.AddRune (Driver.LeftArrow);
+					Driver.AddRune (Driver.RightArrow);
 				} else {
 				} else {
 					bw -= 2;
 					bw -= 2;
 					var bx1 = position * bw / Size;
 					var bx1 = position * bw / Size;
@@ -262,33 +253,27 @@ namespace Terminal.Gui {
 			int location = vertical ? me.Y : me.X;
 			int location = vertical ? me.Y : me.X;
 			int barsize = vertical ? Bounds.Height : Bounds.Width;
 			int barsize = vertical ? Bounds.Height : Bounds.Width;
 
 
-			if (barsize < 4) {
-				// Handle scrollbars with no buttons
-				Console.WriteLine ("TODO at ScrollBarView2");
+			barsize -= 2;
+			var pos = Position;
+			if (location == 0) {
+				if (pos > 0)
+					SetPosition (pos - 1);
+			} else if (location == barsize + 1) {
+				if (pos + 1 < Size)
+					SetPosition (pos + 1);
 			} else {
 			} else {
-				barsize -= 2;
-				// Handle scrollbars with arrow buttons
-				var pos = Position;
-				if (location == 0) {
-					if (pos > 0)
-						SetPosition (pos - 1);
-				} else if (location == barsize + 1) {
-					if (pos + 1 <= Size + 2)
-						SetPosition (pos + 1);
-				} else {
-					var b1 = pos * barsize / Size;
-					var b2 = (pos + barsize) * barsize / Size;
-
-					if (b2 == 0 && location == 1 && pos == 0 ||
-						(b2 == barsize && location == barsize) ||
-						(location > b1 && location < b2)) {
-						return true;
-					} else if (location <= barsize) {
-						if (location > 1 && location >= b2)
-							SetPosition (Math.Min (pos + barsize, Size));
-						else if (location <= b2 && pos > 0 || pos > 0)
-							SetPosition (Math.Max (pos - barsize, 0));
-					}
+				var b1 = pos * barsize / Size;
+				var b2 = (pos + barsize) * barsize / Size;
+
+				if (b2 == 0 && location == 1 && pos == 0 ||
+					(b2 == barsize && location == barsize) ||
+					(location > b1 && location < b2)) {
+					return true;
+				} else if (location <= barsize) {
+					if (location > 1 && location >= b2)
+						SetPosition (Math.Min (pos + barsize, Size));
+					else if (location <= b2 && pos > 0 || pos > 0)
+						SetPosition (Math.Max (pos - barsize, 0));
 				}
 				}
 			}
 			}
 
 
@@ -501,7 +486,7 @@ namespace Terminal.Gui {
 			OnDrawContent (new Rect (ContentOffset,
 			OnDrawContent (new Rect (ContentOffset,
 				new Size (Bounds.Width - (ShowVerticalScrollIndicator ? 1 : 0),
 				new Size (Bounds.Width - (ShowVerticalScrollIndicator ? 1 : 0),
 					Bounds.Height - (ShowHorizontalScrollIndicator ? 1 : 0))));
 					Bounds.Height - (ShowHorizontalScrollIndicator ? 1 : 0))));
-			contentView.Redraw (contentView.Bounds);
+			contentView.Redraw (contentView.Frame);
 			Driver.Clip = savedClip;
 			Driver.Clip = savedClip;
 
 
 			if (ShowVerticalScrollIndicator) {
 			if (ShowVerticalScrollIndicator) {
@@ -530,9 +515,9 @@ namespace Terminal.Gui {
 		public override void PositionCursor ()
 		public override void PositionCursor ()
 		{
 		{
 			if (InternalSubviews.Count == 0)
 			if (InternalSubviews.Count == 0)
-				Driver.Move (0, 0);
+				Move (0, 0);
 			else
 			else
-				base.PositionCursor ();
+				base.PositionCursor (true);
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
@@ -571,7 +556,7 @@ namespace Terminal.Gui {
 		public bool ScrollDown (int lines)
 		public bool ScrollDown (int lines)
 		{
 		{
 			var ny = Math.Max (-contentSize.Height, contentOffset.Y - lines);
 			var ny = Math.Max (-contentSize.Height, contentOffset.Y - lines);
-			if (ny == contentOffset.Y)
+			if (Math.Abs (ny) == contentSize.Height)
 				return false;
 				return false;
 			ContentOffset = new Point (contentOffset.X, ny);
 			ContentOffset = new Point (contentOffset.X, ny);
 			return true;
 			return true;

+ 32 - 7
UICatalog/Scenarios/Scrolling.cs

@@ -8,7 +8,6 @@ namespace UICatalog {
 
 
 	class Scrolling : Scenario {
 	class Scrolling : Scenario {
 
 
-		//class Box10x : View, IScrollView {
 		class Box10x : View {
 		class Box10x : View {
 			int w = 40;
 			int w = 40;
 			int h = 50;
 			int h = 50;
@@ -48,24 +47,42 @@ namespace UICatalog {
 		}
 		}
 
 
 		class Filler : View {
 		class Filler : View {
+			int w = 40;
+			int h = 50;
+
 			public Filler (Rect rect) : base (rect)
 			public Filler (Rect rect) : base (rect)
 			{
 			{
+				w = rect.Width;
+				h = rect.Height;
+			}
+
+			public Size GetContentSize ()
+			{
+				return new Size (w, h);
 			}
 			}
 
 
 			public override void Redraw (Rect bounds)
 			public override void Redraw (Rect bounds)
 			{
 			{
 				Driver.SetAttribute (ColorScheme.Focus);
 				Driver.SetAttribute (ColorScheme.Focus);
 				var f = Frame;
 				var f = Frame;
+				w = 0;
+				h = 0;
 
 
 				for (int y = 0; y < f.Width; y++) {
 				for (int y = 0; y < f.Width; y++) {
-					Move (0, y);
+					Move (0, y, true);
+					var nw = 0;
 					for (int x = 0; x < f.Height; x++) {
 					for (int x = 0; x < f.Height; x++) {
 						Rune r;
 						Rune r;
 						switch (x % 3) {
 						switch (x % 3) {
 						case 0:
 						case 0:
-							Driver.AddRune (y.ToString ().ToCharArray (0, 1) [0]);
-							if (y > 9)
-								Driver.AddRune (y.ToString ().ToCharArray (1, 1) [0]);
+							var er = y.ToString ().ToCharArray (0, 1) [0];
+							nw += er.ToString ().Length;
+							Driver.AddRune (er);
+							if (y > 9) {
+								er = y.ToString ().ToCharArray (1, 1) [0];
+								nw += er.ToString ().Length;
+								Driver.AddRune (er);
+							}
 							r = '.';
 							r = '.';
 							break;
 							break;
 						case 1:
 						case 1:
@@ -76,7 +93,11 @@ namespace UICatalog {
 							break;
 							break;
 						}
 						}
 						Driver.AddRune (r);
 						Driver.AddRune (r);
+						nw += Rune.RuneLen (r);
 					}
 					}
+					if (nw > w)
+						w = nw;
+					h = y + 1;
 				}
 				}
 			}
 			}
 		}
 		}
@@ -202,7 +223,11 @@ namespace UICatalog {
 				ShowVerticalScrollIndicator = true,
 				ShowVerticalScrollIndicator = true,
 				ShowHorizontalScrollIndicator = true
 				ShowHorizontalScrollIndicator = true
 			};
 			};
-			scrollView2.Add (new Filler (new Rect (0, 0, 60, 40)));
+			var filler = new Filler (new Rect (0, 0, 60, 40));
+			scrollView2.Add (filler);
+			scrollView2.DrawContent = (r) => {
+				scrollView2.ContentSize = filler.GetContentSize ();
+			};
 
 
 			// This is just to debug the visuals of the scrollview when small
 			// This is just to debug the visuals of the scrollview when small
 			var scrollView3 = new ScrollView (new Rect (55, 15, 3, 3)) {
 			var scrollView3 = new ScrollView (new Rect (55, 15, 3, 3)) {
@@ -223,7 +248,7 @@ namespace UICatalog {
 			};
 			};
 
 
 			var progress = new ProgressBar ();
 			var progress = new ProgressBar ();
-			progress.X = 5;
+			progress.X = Pos.Right (scrollView) + 1;
 			progress.Y = Pos.AnchorEnd (2);
 			progress.Y = Pos.AnchorEnd (2);
 			progress.Width = 50;
 			progress.Width = 50;
 			bool timer (MainLoop caller)
 			bool timer (MainLoop caller)