2
0
Эх сурвалжийг харах

Merge pull request #2339 from tznind/line-canvas-style-mixing

LineCanvas support for mixing double and single lines
Tig 2 жил өмнө
parent
commit
2ead01a3c8

+ 135 - 20
Terminal.Gui/Core/Graphs/LineCanvas.cs

@@ -14,6 +14,22 @@ namespace Terminal.Gui.Graphs {
 		
 		private List<StraightLine> lines = new List<StraightLine> ();
 
+		Dictionary<IntersectionRuneType, IntersectionRuneResolver> runeResolvers = new Dictionary<IntersectionRuneType, IntersectionRuneResolver> { 
+			{IntersectionRuneType.ULCorner,new ULIntersectionRuneResolver()},
+			{IntersectionRuneType.URCorner,new URIntersectionRuneResolver()},
+			{IntersectionRuneType.LLCorner,new LLIntersectionRuneResolver()},
+			{IntersectionRuneType.LRCorner,new LRIntersectionRuneResolver()},
+
+			{IntersectionRuneType.TopTee,new TopTeeIntersectionRuneResolver()},
+			{IntersectionRuneType.LeftTee,new LeftTeeIntersectionRuneResolver()},
+			{IntersectionRuneType.RightTee,new RightTeeIntersectionRuneResolver()},
+			{IntersectionRuneType.BottomTee,new BottomTeeIntersectionRuneResolver()},
+
+
+			{IntersectionRuneType.Crosshair,new CrosshairIntersectionRuneResolver()},
+			// TODO: Add other resolvers
+		};
+
 		/// <summary>
 		/// Add a new line to the canvas starting at <paramref name="from"/>.
 		/// Use positive <paramref name="length"/> for Right and negative for Left
@@ -84,45 +100,144 @@ namespace Terminal.Gui.Graphs {
 			}
 		}
 
+		private abstract class IntersectionRuneResolver
+		{
+			readonly Rune round;
+			readonly Rune doubleH;
+			readonly Rune doubleV;
+			readonly Rune doubleBoth;
+			readonly Rune normal;
+
+			public IntersectionRuneResolver(Rune round, Rune doubleH, Rune doubleV, Rune doubleBoth, Rune normal)
+			{
+				this.round = round;
+				this.doubleH = doubleH;
+				this.doubleV = doubleV;
+				this.doubleBoth = doubleBoth;
+				this.normal = normal;
+			}
+
+			public Rune? GetRuneForIntersects (ConsoleDriver driver, IntersectionDefinition [] intersects)
+			{
+				var useRounded = intersects.Any (i => i.Line.Style == BorderStyle.Rounded && i.Line.Length != 0);
+
+				bool doubleHorizontal = intersects.Any(l=>l.Line.Orientation == Orientation.Horizontal && l.Line.Style == BorderStyle.Double);
+				bool doubleVertical = intersects.Any(l=>l.Line.Orientation == Orientation.Vertical && l.Line.Style == BorderStyle.Double);
+
+
+				if(doubleHorizontal)
+				{
+						return doubleVertical ? doubleBoth : doubleH;
+				}
+				
+				if(doubleVertical)
+				{
+					return doubleV;
+				}
+
+				return useRounded ? round : normal;
+			}
+		}
+
+		private class ULIntersectionRuneResolver : IntersectionRuneResolver 
+		{
+			public ULIntersectionRuneResolver() :
+				base('╭','╒','╓','╔','┌')
+			{
+				
+			}
+		}
+		private class URIntersectionRuneResolver : IntersectionRuneResolver 
+		{
+
+			public URIntersectionRuneResolver() :
+				base('╮','╕','╖','╗','┐')
+			{
+				
+			}
+		}
+		private class LLIntersectionRuneResolver : IntersectionRuneResolver 
+		{
+
+			public LLIntersectionRuneResolver() :
+				base('╰','╘','╙','╚','└')
+			{
+				
+			}
+		}
+		private class LRIntersectionRuneResolver : IntersectionRuneResolver 
+		{
+			public LRIntersectionRuneResolver() :
+				base('╯','╛','╜','╝','┘')
+			{
+				
+			}
+		}
+
+		private class TopTeeIntersectionRuneResolver : IntersectionRuneResolver 
+		{
+			public TopTeeIntersectionRuneResolver():
+				base('┬','╤','╥','╦','┬'){
+					
+				}
+		}
+		private class LeftTeeIntersectionRuneResolver : IntersectionRuneResolver 
+		{
+			public LeftTeeIntersectionRuneResolver():
+				base('├','╞','╟','╠','├'){
+					
+				}
+		}
+		private class RightTeeIntersectionRuneResolver : IntersectionRuneResolver 
+		{
+			public RightTeeIntersectionRuneResolver():
+				base('┤','╡','╢','╣','┤'){
+					
+				}
+		}
+		private class BottomTeeIntersectionRuneResolver : IntersectionRuneResolver 
+		{
+			public BottomTeeIntersectionRuneResolver():
+				base('┴','╧','╨','╩','┴'){
+					
+				}
+		}
+		private class CrosshairIntersectionRuneResolver : IntersectionRuneResolver 
+		{
+			public CrosshairIntersectionRuneResolver():
+				base('┼','╪','╫','╬','┼'){
+					
+				}
+		}
+
 		private Rune? GetRuneForIntersects (ConsoleDriver driver, IntersectionDefinition [] intersects)
 		{
 			if (!intersects.Any ())
 				return null;
 
 			var runeType = GetRuneTypeForIntersects (intersects);
+
+			if(runeResolvers.ContainsKey (runeType)) {
+				return runeResolvers [runeType].GetRuneForIntersects (driver, intersects);
+			}
+
+			// TODO: Remove these two once we have all of the below ported to IntersectionRuneResolvers
 			var useDouble = intersects.Any (i => i.Line.Style == BorderStyle.Double && i.Line.Length != 0);
 			var useRounded = intersects.Any (i => i.Line.Style == BorderStyle.Rounded && i.Line.Length != 0);
 
+			// TODO: maybe make these resolvers to for simplicity?
+			// or for dotted lines later on or that kind of thing?
 			switch (runeType) {
 			case IntersectionRuneType.None: 
 				return null;
 			case IntersectionRuneType.Dot: 
 				return (Rune)'.';
-			case IntersectionRuneType.ULCorner:
-				return useDouble ? driver.ULDCorner : useRounded ? driver.ULRCorner : driver.ULCorner;
-			case IntersectionRuneType.URCorner: 
-				return useDouble ? driver.URDCorner : useRounded ? driver.URRCorner : driver.URCorner;
-			case IntersectionRuneType.LLCorner: 
-				return useDouble ? driver.LLDCorner : useRounded ? driver.LLRCorner : driver.LLCorner;
-			case IntersectionRuneType.LRCorner: 
-				return useDouble ? driver.LRDCorner : useRounded ? driver.LRRCorner : driver.LRCorner;
-			case IntersectionRuneType.TopTee: 
-				return useDouble ? '╦' : driver.TopTee;
-			case IntersectionRuneType.BottomTee: 
-				return useDouble ? '╩' : driver.BottomTee;
-			case IntersectionRuneType.RightTee: 
-				return useDouble ? '╣' : driver.RightTee;
-			case IntersectionRuneType.LeftTee: 
-				return useDouble ? '╠' : driver.LeftTee;
-			case IntersectionRuneType.Crosshair: 
-				return useDouble ? '╬' : '┼';
 			case IntersectionRuneType.HLine: 
 				return useDouble ? driver.HDLine : driver.HLine;
 			case IntersectionRuneType.VLine: 
 				return useDouble ? driver.VDLine : driver.VLine;
-			default: throw new ArgumentOutOfRangeException (nameof (runeType));
+			default: throw new Exception ("Could not find resolver or switch case for " + nameof (runeType) + ":" + runeType);
 			}
-
 		}
 
 

+ 63 - 1
UnitTests/LineCanvasTests.cs → UnitTests/Core/LineCanvasTests.cs

@@ -2,7 +2,7 @@
 using Xunit;
 using Xunit.Abstractions;
 
-namespace Terminal.Gui.Core {
+namespace Terminal.Gui.CoreTests {
 	public class LineCanvasTests {
 
 		readonly ITestOutputHelper output;
@@ -218,6 +218,68 @@ namespace Terminal.Gui.Core {
 			TestHelpers.AssertDriverContentsAre (looksLike, output);
 		}
 
+
+		[Theory, AutoInitShutdown]
+		[InlineData(BorderStyle.Single)]
+		[InlineData(BorderStyle.Rounded)]
+		public void TestLineCanvas_Window_DoubleTop_SingleSides (BorderStyle thinStyle)
+		{
+			var v = GetCanvas (out var canvas);
+
+			// outer box
+			canvas.AddLine (new Point (0, 0), 9, Orientation.Horizontal, BorderStyle.Double);
+			canvas.AddLine (new Point (9, 0), 4, Orientation.Vertical, thinStyle);
+			canvas.AddLine (new Point (9, 4), -9, Orientation.Horizontal, BorderStyle.Double);
+			canvas.AddLine (new Point (0, 4), -4, Orientation.Vertical, thinStyle);
+
+
+			canvas.AddLine (new Point (5, 0), 4, Orientation.Vertical,thinStyle);
+			canvas.AddLine (new Point (0, 2), 9, Orientation.Horizontal, BorderStyle.Double);
+
+			v.Redraw (v.Bounds);
+
+			string looksLike =
+@"    
+╒════╤═══╕
+│    │   │
+╞════╪═══╡
+│    │   │
+╘════╧═══╛
+";
+			TestHelpers.AssertDriverContentsAre (looksLike, output);
+		}
+
+		[Theory, AutoInitShutdown]
+		[InlineData(BorderStyle.Single)]
+		[InlineData(BorderStyle.Rounded)]
+		public void TestLineCanvas_Window_SingleTop_DoubleSides (BorderStyle thinStyle)
+		{
+			var v = GetCanvas (out var canvas);
+
+			// outer box
+			canvas.AddLine (new Point (0, 0), 9, Orientation.Horizontal, thinStyle);
+			canvas.AddLine (new Point (9, 0), 4, Orientation.Vertical, BorderStyle.Double);
+			canvas.AddLine (new Point (9, 4), -9, Orientation.Horizontal,thinStyle);
+			canvas.AddLine (new Point (0, 4), -4, Orientation.Vertical,  BorderStyle.Double);
+
+
+			canvas.AddLine (new Point (5, 0), 4, Orientation.Vertical, BorderStyle.Double);
+			canvas.AddLine (new Point (0, 2), 9, Orientation.Horizontal, thinStyle);
+
+			v.Redraw (v.Bounds);
+
+			string looksLike =
+@"    
+╓────╥───╖
+║    ║   ║
+╟────╫───╢
+║    ║   ║
+╙────╨───╜
+
+";
+			TestHelpers.AssertDriverContentsAre (looksLike, output);
+		}
+
 		private View GetCanvas (out LineCanvas canvas)
 		{
 			var v = new View {