mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-11-01 01:25:27 +08:00 
			
		
		
		
	 Patrik Svensson
					Patrik Svensson
				
			
				
					committed by
					
						 Patrik Svensson
						Patrik Svensson
					
				
			
			
				
	
			
			
			 Patrik Svensson
						Patrik Svensson
					
				
			
						parent
						
							fa85216554
						
					
				
				
					commit
					2dd0eb9f74
				
			| @@ -67,31 +67,38 @@ namespace Sample | ||||
|                                 Text.New( | ||||
|                                     "[underline]I[/] heard [underline on blue]you[/] like 📦\n\n\n\n" + | ||||
|                                     "So I put a 📦 in a 📦\nin a 📦 in a 📦\n\n" + | ||||
|                                     "😅", foreground: Color.White), | ||||
|                                 content: Justify.Center, | ||||
|                                 border: BorderKind.Rounded))), | ||||
|                     border: BorderKind.Ascii)); | ||||
|                                     "😅", foreground: Color.White)) | ||||
|                             { Alignment = Justify.Center, Border = BorderKind.Rounded }))) | ||||
|                 { | ||||
|                     Border = BorderKind.Ascii | ||||
|                 }); | ||||
|  | ||||
|             // Reset colors | ||||
|             AnsiConsole.ResetColors(); | ||||
|  | ||||
|             // Left adjusted panel with text | ||||
|             AnsiConsole.Render(new Panel( | ||||
|                 Text.New("Left adjusted\nLeft", | ||||
|                     foreground: Color.White), | ||||
|                 fit: true)); | ||||
|                 Text.New("Left adjusted\nLeft")) | ||||
|             { | ||||
|                 Expand = true, | ||||
|                 Alignment = Justify.Left, | ||||
|             }); | ||||
|  | ||||
|             // Centered panel with text | ||||
|             AnsiConsole.Render(new Panel( | ||||
|                 Text.New("Centered\nCenter", | ||||
|                     foreground: Color.White), | ||||
|                 fit: true, content: Justify.Center)); | ||||
|                 Text.New("Centered\nCenter")) | ||||
|             { | ||||
|                 Expand = true, | ||||
|                 Alignment = Justify.Center, | ||||
|             }); | ||||
|  | ||||
|             // Right adjusted panel with text | ||||
|             AnsiConsole.Render(new Panel( | ||||
|                 Text.New("Right adjusted\nRight", | ||||
|                     foreground: Color.White), | ||||
|                 fit: true, content: Justify.Right)); | ||||
|                 Text.New("Right adjusted\nRight")) | ||||
|             { | ||||
|                 Expand = true, | ||||
|                 Alignment = Justify.Right, | ||||
|             }); | ||||
|  | ||||
|             // A normal, square table | ||||
|             var table = new Table(); | ||||
| @@ -145,7 +152,7 @@ namespace Sample | ||||
|             AnsiConsole.Render(table); | ||||
|  | ||||
|             // Render a table in some panels. | ||||
|             AnsiConsole.Render(new Panel(new Panel(table, border: BorderKind.Ascii))); | ||||
|             AnsiConsole.Render(new Panel(new Panel(table) { Border = BorderKind.Ascii }) { Padding = new Padding(0, 0) }); | ||||
|  | ||||
|             // Draw another table | ||||
|             table = new Table { Expand = false }; | ||||
|   | ||||
| @@ -55,7 +55,7 @@ namespace Spectre.Console.Tests.Unit.Composition | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Render_Grid_With_No_Border_Correctly() | ||||
|         public void Should_Render_Grid_Correctly() | ||||
|         { | ||||
|             // Given | ||||
|             var console = new PlainConsole(width: 80); | ||||
| @@ -75,13 +75,58 @@ namespace Spectre.Console.Tests.Unit.Composition | ||||
|             console.Lines[1].ShouldBe("Grault  Garply  Fred "); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Render_Grid_Column_Alignment_Correctly() | ||||
|         { | ||||
|             // Given | ||||
|             var console = new PlainConsole(width: 80); | ||||
|             var grid = new Grid(); | ||||
|             grid.AddColumn(new GridColumn { Alignment = Justify.Right }); | ||||
|             grid.AddColumn(new GridColumn { Alignment = Justify.Center }); | ||||
|             grid.AddColumn(new GridColumn { Alignment = Justify.Left }); | ||||
|             grid.AddRow("Foo", "Bar", "Baz"); | ||||
|             grid.AddRow("Qux", "Corgi", "Waldo"); | ||||
|             grid.AddRow("Grault", "Garply", "Fred"); | ||||
|  | ||||
|             // When | ||||
|             console.Render(grid); | ||||
|  | ||||
|             // Then | ||||
|             console.Lines.Count.ShouldBe(3); | ||||
|             console.Lines[0].ShouldBe("   Foo   Bar    Baz  "); | ||||
|             console.Lines[1].ShouldBe("   Qux  Corgi   Waldo"); | ||||
|             console.Lines[2].ShouldBe("Grault  Garply  Fred "); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Render_Grid_Column_Padding_Correctly() | ||||
|         { | ||||
|             // Given | ||||
|             var console = new PlainConsole(width: 80); | ||||
|             var grid = new Grid(); | ||||
|             grid.AddColumn(new GridColumn { Padding = new Padding(3, 0) }); | ||||
|             grid.AddColumns(2); | ||||
|             grid.AddRow("Foo", "Bar", "Baz"); | ||||
|             grid.AddRow("Qux", "Corgi", "Waldo"); | ||||
|             grid.AddRow("Grault", "Garply", "Fred"); | ||||
|  | ||||
|             // When | ||||
|             console.Render(grid); | ||||
|  | ||||
|             // Then | ||||
|             console.Lines.Count.ShouldBe(3); | ||||
|             console.Lines[0].ShouldBe("   Foo    Bar     Baz  "); | ||||
|             console.Lines[1].ShouldBe("   Qux    Corgi   Waldo"); | ||||
|             console.Lines[2].ShouldBe("   Grault Garply  Fred "); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Render_Grid() | ||||
|         { | ||||
|             var console = new PlainConsole(width: 80); | ||||
|             var grid = new Grid(); | ||||
|             grid.AddColumn(new GridColumn { NoWrap = true }); | ||||
|             grid.AddColumn(); | ||||
|             grid.AddColumn(new GridColumn { Padding = new Padding(2, 0) }); | ||||
|             grid.AddRow("[bold]Options[/]", string.Empty); | ||||
|             grid.AddRow("  [blue]-h[/], [blue]--help[/]", "Show command line help."); | ||||
|             grid.AddRow("  [blue]-c[/], [blue]--configuration[/]", "The configuration to run for.\nThe default for most projects is [green]Debug[/]."); | ||||
| @@ -91,10 +136,10 @@ namespace Spectre.Console.Tests.Unit.Composition | ||||
|  | ||||
|             // Then | ||||
|             console.Lines.Count.ShouldBe(4); | ||||
|             console.Lines[0].ShouldBe("Options                                                                     "); | ||||
|             console.Lines[1].ShouldBe("  -h, --help           Show command line help.                              "); | ||||
|             console.Lines[2].ShouldBe("  -c, --configuration  The configuration to run for.                        "); | ||||
|             console.Lines[3].ShouldBe("                       The default for most projects is Debug.              "); | ||||
|             console.Lines[0].ShouldBe("Options                                                         "); | ||||
|             console.Lines[1].ShouldBe("  -h, --help             Show command line help.                "); | ||||
|             console.Lines[2].ShouldBe("  -c, --configuration    The configuration to run for.          "); | ||||
|             console.Lines[3].ShouldBe("                         The default for most projects is Debug."); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -21,6 +21,25 @@ namespace Spectre.Console.Tests.Unit | ||||
|             console.Lines[2].ShouldBe("└─────────────┘"); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Render_Panel_With_Padding() | ||||
|         { | ||||
|             // Given | ||||
|             var console = new PlainConsole(width: 80); | ||||
|  | ||||
|             // When | ||||
|             console.Render(new Panel(Text.New("Hello World")) | ||||
|             { | ||||
|                 Padding = new Padding(3, 5), | ||||
|             }); | ||||
|  | ||||
|             // Then | ||||
|             console.Lines.Count.ShouldBe(3); | ||||
|             console.Lines[0].ShouldBe("┌───────────────────┐"); | ||||
|             console.Lines[1].ShouldBe("│   Hello World     │"); | ||||
|             console.Lines[2].ShouldBe("└───────────────────┘"); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Render_Panel_With_Unicode_Correctly() | ||||
|         { | ||||
| @@ -62,8 +81,7 @@ namespace Spectre.Console.Tests.Unit | ||||
|             // Given | ||||
|             var console = new PlainConsole(width: 80); | ||||
|             var text = new Panel( | ||||
|                 Text.New("I heard [underline on blue]you[/] like 📦\n\n\n\nSo I put a 📦 in a 📦"), | ||||
|                 content: Justify.Center); | ||||
|                 Text.New("I heard [underline on blue]you[/] like 📦\n\n\n\nSo I put a 📦 in a 📦")); | ||||
|  | ||||
|             // When | ||||
|             console.Render(text); | ||||
| @@ -71,7 +89,7 @@ namespace Spectre.Console.Tests.Unit | ||||
|             // Then | ||||
|             console.Lines.Count.ShouldBe(7); | ||||
|             console.Lines[0].ShouldBe("┌───────────────────────┐"); | ||||
|             console.Lines[1].ShouldBe("│  I heard you like 📦  │"); | ||||
|             console.Lines[1].ShouldBe("│ I heard you like 📦   │"); | ||||
|             console.Lines[2].ShouldBe("│                       │"); | ||||
|             console.Lines[3].ShouldBe("│                       │"); | ||||
|             console.Lines[4].ShouldBe("│                       │"); | ||||
| @@ -80,19 +98,23 @@ namespace Spectre.Console.Tests.Unit | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Fit_Panel_To_Parent_If_Enabled() | ||||
|         public void Should_Expand_Panel_If_Enabled() | ||||
|         { | ||||
|             // Given | ||||
|             var console = new PlainConsole(width: 25); | ||||
|             var console = new PlainConsole(width: 80); | ||||
|  | ||||
|             // When | ||||
|             console.Render(new Panel(Text.New("Hello World"), fit: true)); | ||||
|             console.Render(new Panel(Text.New("Hello World")) | ||||
|             { | ||||
|                 Expand = true, | ||||
|             }); | ||||
|  | ||||
|             // Then | ||||
|             console.Lines.Count.ShouldBe(3); | ||||
|             console.Lines[0].ShouldBe("┌───────────────────────┐"); | ||||
|             console.Lines[1].ShouldBe("│ Hello World           │"); | ||||
|             console.Lines[2].ShouldBe("└───────────────────────┘"); | ||||
|             console.Lines[0].Length.ShouldBe(80); | ||||
|             console.Lines[0].ShouldBe("┌──────────────────────────────────────────────────────────────────────────────┐"); | ||||
|             console.Lines[1].ShouldBe("│ Hello World                                                                  │"); | ||||
|             console.Lines[2].ShouldBe("└──────────────────────────────────────────────────────────────────────────────┘"); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
| @@ -102,7 +124,12 @@ namespace Spectre.Console.Tests.Unit | ||||
|             var console = new PlainConsole(width: 25); | ||||
|  | ||||
|             // When | ||||
|             console.Render(new Panel(Text.New("Hello World"), fit: true, content: Justify.Right)); | ||||
|             console.Render( | ||||
|                 new Panel( | ||||
|                     Text.New("Hello World").WithAlignment(Justify.Right)) | ||||
|                 { | ||||
|                     Expand = true, | ||||
|                 }); | ||||
|  | ||||
|             // Then | ||||
|             console.Lines.Count.ShouldBe(3); | ||||
| @@ -118,7 +145,12 @@ namespace Spectre.Console.Tests.Unit | ||||
|             var console = new PlainConsole(width: 25); | ||||
|  | ||||
|             // When | ||||
|             console.Render(new Panel(Text.New("Hello World"), fit: true, content: Justify.Center)); | ||||
|             console.Render( | ||||
|                 new Panel( | ||||
|                     Text.New("Hello World").WithAlignment(Justify.Center)) | ||||
|                 { | ||||
|                     Expand = true, | ||||
|                 }); | ||||
|  | ||||
|             // Then | ||||
|             console.Lines.Count.ShouldBe(3); | ||||
|   | ||||
| @@ -32,7 +32,7 @@ namespace Spectre.Console.Tests.Unit.Composition | ||||
|                 var table = new Table(); | ||||
|  | ||||
|                 // When | ||||
|                 var result = Record.Exception(() => table.AddColumns(null)); | ||||
|                 var result = Record.Exception(() => table.AddColumns((string[])null)); | ||||
|  | ||||
|                 // Then | ||||
|                 result.ShouldBeOfType<ArgumentNullException>() | ||||
| @@ -88,31 +88,6 @@ namespace Spectre.Console.Tests.Unit.Composition | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Measure_Table_Correctly() | ||||
|         { | ||||
|             // Given | ||||
|             var console = new PlainConsole(width: 80); | ||||
|             var table = new Table(); | ||||
|             table.AddColumns("Foo", "Bar", "Baz"); | ||||
|             table.AddRow("Qux", "Corgi", "Waldo"); | ||||
|             table.AddRow("Grault", "Garply", "Fred"); | ||||
|  | ||||
|             // When | ||||
|             console.Render(new Panel(table)); | ||||
|  | ||||
|             // Then | ||||
|             console.Lines.Count.ShouldBe(8); | ||||
|             console.Lines[0].ShouldBe("┌─────────────────────────────┐"); | ||||
|             console.Lines[1].ShouldBe("│ ┌────────┬────────┬───────┐ │"); | ||||
|             console.Lines[2].ShouldBe("│ │ Foo    │ Bar    │ Baz   │ │"); | ||||
|             console.Lines[3].ShouldBe("│ ├────────┼────────┼───────┤ │"); | ||||
|             console.Lines[4].ShouldBe("│ │ Qux    │ Corgi  │ Waldo │ │"); | ||||
|             console.Lines[5].ShouldBe("│ │ Grault │ Garply │ Fred  │ │"); | ||||
|             console.Lines[6].ShouldBe("│ └────────┴────────┴───────┘ │"); | ||||
|             console.Lines[7].ShouldBe("└─────────────────────────────┘"); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Render_Table_Correctly() | ||||
|         { | ||||
| @@ -136,6 +111,64 @@ namespace Spectre.Console.Tests.Unit.Composition | ||||
|             console.Lines[5].ShouldBe("└────────┴────────┴───────┘"); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Render_Table_Nested_In_Panels_Correctly() | ||||
|         { | ||||
|             // A simple table | ||||
|             var console = new PlainConsole(width: 80); | ||||
|             var table = new Table() { Border = BorderKind.Rounded }; | ||||
|             table.AddColumn("Foo"); | ||||
|             table.AddColumn("Bar"); | ||||
|             table.AddColumn(new TableColumn("Baz") { Alignment = Justify.Right }); | ||||
|             table.AddRow("Qux\nQuuuuuux", "[blue]Corgi[/]", "Waldo"); | ||||
|             table.AddRow("Grault", "Garply", "Fred"); | ||||
|  | ||||
|             // Render a table in some panels. | ||||
|             console.Render(new Panel(new Panel(table) | ||||
|             { | ||||
|                 Border = BorderKind.Ascii, | ||||
|             })); | ||||
|  | ||||
|             // Then | ||||
|             console.Lines.Count.ShouldBe(11); | ||||
|             console.Lines[00].ShouldBe("┌───────────────────────────────────┐"); | ||||
|             console.Lines[01].ShouldBe("│ +-------------------------------+ │"); | ||||
|             console.Lines[02].ShouldBe("│ | ╭──────────┬────────┬───────╮ | │"); | ||||
|             console.Lines[03].ShouldBe("│ | │ Foo      │ Bar    │   Baz │ | │"); | ||||
|             console.Lines[04].ShouldBe("│ | ├──────────┼────────┼───────┤ | │"); | ||||
|             console.Lines[05].ShouldBe("│ | │ Qux      │ Corgi  │ Waldo │ | │"); | ||||
|             console.Lines[06].ShouldBe("│ | │ Quuuuuux │        │       │ | │"); | ||||
|             console.Lines[07].ShouldBe("│ | │ Grault   │ Garply │  Fred │ | │"); | ||||
|             console.Lines[08].ShouldBe("│ | ╰──────────┴────────┴───────╯ | │"); | ||||
|             console.Lines[09].ShouldBe("│ +-------------------------------+ │"); | ||||
|             console.Lines[10].ShouldBe("└───────────────────────────────────┘"); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Render_Table_With_Column_Justification_Correctly() | ||||
|         { | ||||
|             // Given | ||||
|             var console = new PlainConsole(width: 80); | ||||
|             var table = new Table(); | ||||
|             table.AddColumn(new TableColumn("Foo") { Alignment = Justify.Left }); | ||||
|             table.AddColumn(new TableColumn("Bar") { Alignment = Justify.Right }); | ||||
|             table.AddColumn(new TableColumn("Baz") { Alignment = Justify.Center }); | ||||
|             table.AddRow("Qux", "Corgi", "Waldo"); | ||||
|             table.AddRow("Grault", "Garply", "Lorem ipsum dolor sit amet"); | ||||
|  | ||||
|             // When | ||||
|             console.Render(table); | ||||
|  | ||||
|             // Then | ||||
|             console.Lines.Count.ShouldBe(6); | ||||
|             console.Lines[0].ShouldBe("┌────────┬────────┬────────────────────────────┐"); | ||||
|             console.Lines[1].ShouldBe("│ Foo    │    Bar │            Baz             │"); | ||||
|             console.Lines[2].ShouldBe("├────────┼────────┼────────────────────────────┤"); | ||||
|             console.Lines[3].ShouldBe("│ Qux    │  Corgi │           Waldo            │"); | ||||
|             console.Lines[4].ShouldBe("│ Grault │ Garply │ Lorem ipsum dolor sit amet │"); | ||||
|             console.Lines[5].ShouldBe("└────────┴────────┴────────────────────────────┘"); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Expand_Table_To_Available_Space_If_Specified() | ||||
|         { | ||||
| @@ -249,5 +282,30 @@ namespace Spectre.Console.Tests.Unit.Composition | ||||
|             console.Lines[5].ShouldBe("│ Grault │ Garply │ Fred  │"); | ||||
|             console.Lines[6].ShouldBe("└────────┴────────┴───────┘"); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Render_Table_With_Cell_Padding_Correctly() | ||||
|         { | ||||
|             // Given | ||||
|             var console = new PlainConsole(width: 80); | ||||
|             var table = new Table(); | ||||
|             table.AddColumns("Foo", "Bar"); | ||||
|             table.AddColumn(new TableColumn("Baz") { Padding = new Padding(3, 2) }); | ||||
|             table.AddRow("Qux\nQuuux", "Corgi", "Waldo"); | ||||
|             table.AddRow("Grault", "Garply", "Fred"); | ||||
|  | ||||
|             // When | ||||
|             console.Render(table); | ||||
|  | ||||
|             // Then | ||||
|             console.Lines.Count.ShouldBe(7); | ||||
|             console.Lines[0].ShouldBe("┌────────┬────────┬──────────┐"); | ||||
|             console.Lines[1].ShouldBe("│ Foo    │ Bar    │   Baz    │"); | ||||
|             console.Lines[2].ShouldBe("├────────┼────────┼──────────┤"); | ||||
|             console.Lines[3].ShouldBe("│ Qux    │ Corgi  │   Waldo  │"); | ||||
|             console.Lines[4].ShouldBe("│ Quuux  │        │          │"); | ||||
|             console.Lines[5].ShouldBe("│ Grault │ Garply │   Fred   │"); | ||||
|             console.Lines[6].ShouldBe("└────────┴────────┴──────────┘"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -20,6 +20,7 @@ namespace Spectre.Console | ||||
|             { | ||||
|                 Border = BorderKind.None, | ||||
|                 ShowHeaders = false, | ||||
|                 IsGrid = true, | ||||
|             }; | ||||
|         } | ||||
|  | ||||
| @@ -40,7 +41,7 @@ namespace Spectre.Console | ||||
|         /// </summary> | ||||
|         public void AddColumn() | ||||
|         { | ||||
|             _table.AddColumn(string.Empty); | ||||
|             AddColumn(new GridColumn()); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
| @@ -58,11 +59,40 @@ namespace Spectre.Console | ||||
|             { | ||||
|                 Width = column.Width, | ||||
|                 NoWrap = column.NoWrap, | ||||
|                 LeftPadding = 0, | ||||
|                 RightPadding = 1, | ||||
|                 Padding = column.Padding, | ||||
|                 Alignment = column.Alignment, | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Adds a column to the grid. | ||||
|         /// </summary> | ||||
|         /// <param name="count">The number of columns to add.</param> | ||||
|         public void AddColumns(int count) | ||||
|         { | ||||
|             for (var index = 0; index < count; index++) | ||||
|             { | ||||
|                 AddColumn(new GridColumn()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Adds a column to the grid. | ||||
|         /// </summary> | ||||
|         /// <param name="columns">The columns to add.</param> | ||||
|         public void AddColumns(params GridColumn[] columns) | ||||
|         { | ||||
|             if (columns is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(columns)); | ||||
|             } | ||||
|  | ||||
|             foreach (var column in columns) | ||||
|             { | ||||
|                 AddColumn(column); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Adds a new row to the grid. | ||||
|         /// </summary> | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| namespace Spectre.Console | ||||
| namespace Spectre.Console | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Represents a grid column. | ||||
| @@ -9,12 +9,22 @@ | ||||
|         /// Gets or sets the width of the column. | ||||
|         /// If <c>null</c>, the column will adapt to it's contents. | ||||
|         /// </summary> | ||||
|         public int? Width { get; set; } | ||||
|         public int? Width { get; set; } = null; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets a value indicating whether wrapping of | ||||
|         /// text within the column should be prevented. | ||||
|         /// </summary> | ||||
|         public bool NoWrap { get; set; } | ||||
|         public bool NoWrap { get; set; } = false; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the padding of the column. | ||||
|         /// </summary> | ||||
|         public Padding Padding { get; set; } = new Padding(0, 1); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the alignment of the column. | ||||
|         /// </summary> | ||||
|         public Justify? Alignment { get; set; } = null; | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										86
									
								
								src/Spectre.Console/Composition/Padding.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/Spectre.Console/Composition/Padding.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| using System; | ||||
|  | ||||
| namespace Spectre.Console | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Represents a measurement. | ||||
|     /// </summary> | ||||
|     public struct Padding : IEquatable<Padding> | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Gets the left padding. | ||||
|         /// </summary> | ||||
|         public int Left { get; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets the right padding. | ||||
|         /// </summary> | ||||
|         public int Right { get; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="Padding"/> struct. | ||||
|         /// </summary> | ||||
|         /// <param name="left">The left padding.</param> | ||||
|         /// <param name="right">The right padding.</param> | ||||
|         public Padding(int left, int right) | ||||
|         { | ||||
|             Left = left; | ||||
|             Right = right; | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public override bool Equals(object obj) | ||||
|         { | ||||
|             return obj is Padding padding && Equals(padding); | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public override int GetHashCode() | ||||
|         { | ||||
|             unchecked | ||||
|             { | ||||
|                 var hash = (int)2166136261; | ||||
|                 hash = (hash * 16777619) ^ Left.GetHashCode(); | ||||
|                 hash = (hash * 16777619) ^ Right.GetHashCode(); | ||||
|                 return hash; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public bool Equals(Padding other) | ||||
|         { | ||||
|             return Left == other.Left && Right == other.Right; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Checks if two <see cref="Padding"/> instances are equal. | ||||
|         /// </summary> | ||||
|         /// <param name="left">The first <see cref="Padding"/> instance to compare.</param> | ||||
|         /// <param name="right">The second <see cref="Padding"/> instance to compare.</param> | ||||
|         /// <returns><c>true</c> if the two instances are equal, otherwise <c>false</c>.</returns> | ||||
|         public static bool operator ==(Padding left, Padding right) | ||||
|         { | ||||
|             return left.Equals(right); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Checks if two <see cref="Padding"/> instances are not equal. | ||||
|         /// </summary> | ||||
|         /// <param name="left">The first <see cref="Padding"/> instance to compare.</param> | ||||
|         /// <param name="right">The second <see cref="Padding"/> instance to compare.</param> | ||||
|         /// <returns><c>true</c> if the two instances are not equal, otherwise <c>false</c>.</returns> | ||||
|         public static bool operator !=(Padding left, Padding right) | ||||
|         { | ||||
|             return !(left == right); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets the horizontal padding. | ||||
|         /// </summary> | ||||
|         /// <returns>The horizontal padding.</returns> | ||||
|         public int GetHorizontalPadding() | ||||
|         { | ||||
|             return Left + Right; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -10,9 +10,6 @@ namespace Spectre.Console | ||||
|     public sealed class Panel : IRenderable | ||||
|     { | ||||
|         private readonly IRenderable _child; | ||||
|         private readonly bool _fit; | ||||
|         private readonly Justify _content; | ||||
|         private readonly BorderKind _border; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets a value indicating whether or not to use | ||||
| @@ -21,113 +18,109 @@ namespace Spectre.Console | ||||
|         /// </summary> | ||||
|         public bool SafeBorder { get; set; } = true; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the kind of border to use. | ||||
|         /// </summary> | ||||
|         public BorderKind Border { get; set; } = BorderKind.Square; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the alignment of the panel contents. | ||||
|         /// </summary> | ||||
|         public Justify? Alignment { get; set; } = null; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets a value indicating whether or not the panel should | ||||
|         /// fit the available space. If <c>false</c>, the panel width will be | ||||
|         /// auto calculated. Defaults to <c>false</c>. | ||||
|         /// </summary> | ||||
|         public bool Expand { get; set; } = false; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the padding. | ||||
|         /// </summary> | ||||
|         public Padding Padding { get; set; } = new Padding(1, 1); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="Panel"/> class. | ||||
|         /// </summary> | ||||
|         /// <param name="child">The child.</param> | ||||
|         /// <param name="fit">Whether or not to fit the panel to it's parent.</param> | ||||
|         /// <param name="content">The justification of the panel content.</param> | ||||
|         /// <param name="border">The border to use.</param> | ||||
|         public Panel( | ||||
|             IRenderable child, | ||||
|             bool fit = false, | ||||
|             Justify content = Justify.Left, | ||||
|             BorderKind border = BorderKind.Square) | ||||
|         /// <param name="content">The panel content.</param> | ||||
|         public Panel(IRenderable content) | ||||
|         { | ||||
|             _child = child ?? throw new System.ArgumentNullException(nameof(child)); | ||||
|             _fit = fit; | ||||
|             _content = content; | ||||
|             _border = border; | ||||
|             _child = content ?? throw new System.ArgumentNullException(nameof(content)); | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         Measurement IRenderable.Measure(RenderContext context, int maxWidth) | ||||
|         { | ||||
|             var childWidth = _child.Measure(context, maxWidth); | ||||
|             return new Measurement(childWidth.Min + 4, childWidth.Max + 4); | ||||
|             return new Measurement(childWidth.Min + 2 + Padding.GetHorizontalPadding(), childWidth.Max + 2 + Padding.GetHorizontalPadding()); | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         IEnumerable<Segment> IRenderable.Render(RenderContext context, int width) | ||||
|         { | ||||
|             var border = Border.GetBorder(_border, (context.LegacyConsole || !context.Unicode) && SafeBorder); | ||||
|             var border = Composition.Border.GetBorder(Border, (context.LegacyConsole || !context.Unicode) && SafeBorder); | ||||
|  | ||||
|             var childWidth = width - 4; | ||||
|             if (!_fit) | ||||
|             var edgeWidth = 2; | ||||
|             var paddingWidth = Padding.GetHorizontalPadding(); | ||||
|             var childWidth = width - edgeWidth - paddingWidth; | ||||
|  | ||||
|             if (!Expand) | ||||
|             { | ||||
|                 var measurement = _child.Measure(context, width - 2); | ||||
|                 var measurement = _child.Measure(context, width - edgeWidth - paddingWidth); | ||||
|                 childWidth = measurement.Max; | ||||
|             } | ||||
|  | ||||
|             var result = new List<Segment>(); | ||||
|             var panelWidth = childWidth + 2; | ||||
|             var panelWidth = childWidth + paddingWidth; | ||||
|  | ||||
|             result.Add(new Segment(border.GetPart(BorderPart.HeaderTopLeft))); | ||||
|             result.Add(new Segment(border.GetPart(BorderPart.HeaderTop, panelWidth))); | ||||
|             result.Add(new Segment(border.GetPart(BorderPart.HeaderTopRight))); | ||||
|             result.Add(new Segment("\n")); | ||||
|             // Panel top | ||||
|             var result = new List<Segment> | ||||
|             { | ||||
|                 new Segment(border.GetPart(BorderPart.HeaderTopLeft)), | ||||
|                 new Segment(border.GetPart(BorderPart.HeaderTop, panelWidth)), | ||||
|                 new Segment(border.GetPart(BorderPart.HeaderTopRight)), | ||||
|                 new Segment("\n"), | ||||
|             }; | ||||
|  | ||||
|             // Render the child. | ||||
|             var childSegments = _child.Render(context, childWidth); | ||||
|             var childContext = context.WithJustification(Alignment); | ||||
|             var childSegments = _child.Render(childContext, childWidth); | ||||
|  | ||||
|             // Split the child segments into lines. | ||||
|             var lines = Segment.SplitLines(childSegments, childWidth); | ||||
|             foreach (var line in lines) | ||||
|             foreach (var line in Segment.SplitLines(childSegments, panelWidth)) | ||||
|             { | ||||
|                 result.Add(new Segment(border.GetPart(BorderPart.CellLeft))); | ||||
|                 result.Add(new Segment(" ")); // Left padding | ||||
|  | ||||
|                 // Left padding | ||||
|                 if (Padding.Left > 0) | ||||
|                 { | ||||
|                     result.Add(new Segment(new string(' ', Padding.Left))); | ||||
|                 } | ||||
|  | ||||
|                 var content = new List<Segment>(); | ||||
|                 content.AddRange(line); | ||||
|  | ||||
|                 // Do we need to pad the panel? | ||||
|                 var length = line.Sum(segment => segment.CellLength(context.Encoding)); | ||||
|                 if (length < childWidth) | ||||
|                 { | ||||
|                     // Justify right side | ||||
|                     if (_content == Justify.Right) | ||||
|                     { | ||||
|                         var diff = childWidth - length; | ||||
|                         content.Add(new Segment(new string(' ', diff))); | ||||
|                     } | ||||
|                     else if (_content == Justify.Center) | ||||
|                     { | ||||
|                         var diff = (childWidth - length) / 2; | ||||
|                         content.Add(new Segment(new string(' ', diff))); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 foreach (var segment in line) | ||||
|                 { | ||||
|                     content.Add(segment.StripLineEndings()); | ||||
|                 } | ||||
|  | ||||
|                 // Justify left side | ||||
|                 if (length < childWidth) | ||||
|                 { | ||||
|                     if (_content == Justify.Left) | ||||
|                     { | ||||
|                         var diff = childWidth - length; | ||||
|                         content.Add(new Segment(new string(' ', diff))); | ||||
|                     } | ||||
|                     else if (_content == Justify.Center) | ||||
|                     { | ||||
|                         var diff = (childWidth - length) / 2; | ||||
|                         content.Add(new Segment(new string(' ', diff))); | ||||
|  | ||||
|                         var remainder = (childWidth - length) % 2; | ||||
|                         if (remainder != 0) | ||||
|                         { | ||||
|                             content.Add(new Segment(new string(' ', remainder))); | ||||
|                         } | ||||
|                     } | ||||
|                     var diff = childWidth - length; | ||||
|                     content.Add(new Segment(new string(' ', diff))); | ||||
|                 } | ||||
|  | ||||
|                 result.AddRange(content); | ||||
|  | ||||
|                 result.Add(new Segment(" ")); | ||||
|                 // Right padding | ||||
|                 if (Padding.Right > 0) | ||||
|                 { | ||||
|                     result.Add(new Segment(new string(' ', Padding.Right))); | ||||
|                 } | ||||
|  | ||||
|                 result.Add(new Segment(border.GetPart(BorderPart.CellRight))); | ||||
|                 result.Add(new Segment("\n")); | ||||
|             } | ||||
|  | ||||
|             // Panel bottom | ||||
|             result.Add(new Segment(border.GetPart(BorderPart.FooterBottomLeft))); | ||||
|             result.Add(new Segment(border.GetPart(BorderPart.FooterBottom, panelWidth))); | ||||
|             result.Add(new Segment(border.GetPart(BorderPart.FooterBottomRight))); | ||||
|   | ||||
| @@ -22,16 +22,33 @@ namespace Spectre.Console.Composition | ||||
|         /// </summary> | ||||
|         public bool Unicode { get; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets the current justification. | ||||
|         /// </summary> | ||||
|         public Justify? Justification { get; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="RenderContext"/> class. | ||||
|         /// </summary> | ||||
|         /// <param name="encoding">The console's output encoding.</param> | ||||
|         /// <param name="legacyConsole">A value indicating whether or not this a legacy console (i.e. cmd.exe).</param> | ||||
|         public RenderContext(Encoding encoding, bool legacyConsole) | ||||
|         /// <param name="justification">The justification to use when rendering.</param> | ||||
|         public RenderContext(Encoding encoding, bool legacyConsole, Justify? justification = null) | ||||
|         { | ||||
|             Encoding = encoding ?? throw new System.ArgumentNullException(nameof(encoding)); | ||||
|             LegacyConsole = legacyConsole; | ||||
|             Justification = justification; | ||||
|             Unicode = Encoding == Encoding.UTF8 || Encoding == Encoding.Unicode; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Creates a new context with the specified justification. | ||||
|         /// </summary> | ||||
|         /// <param name="justification">The justification.</param> | ||||
|         /// <returns>A new <see cref="RenderContext"/> instance with the specified justification.</returns> | ||||
|         public RenderContext WithJustification(Justify? justification) | ||||
|         { | ||||
|             return new RenderContext(Encoding, LegacyConsole, justification); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -21,47 +21,6 @@ namespace Spectre.Console | ||||
|  | ||||
|             var tableWidth = widths.Sum(); | ||||
|  | ||||
|             if (ShouldExpand()) | ||||
|             { | ||||
|                 var ratios = _columns.Select(c => c.Ratio ?? 0).ToList(); | ||||
|                 if (ratios.Any(r => r != 0)) | ||||
|                 { | ||||
|                     var fixedWidths = new List<int>(); | ||||
|                     foreach (var (range, column) in width_ranges.Zip(_columns, (a, b) => (a, b))) | ||||
|                     { | ||||
|                         fixedWidths.Add(column.IsFlexible() ? 0 : range.Max); | ||||
|                     } | ||||
|  | ||||
|                     var flexMinimum = new List<int>(); | ||||
|                     foreach (var column in _columns) | ||||
|                     { | ||||
|                         if (column.IsFlexible()) | ||||
|                         { | ||||
|                             flexMinimum.Add(column.Width ?? 1 + column.GetPadding()); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             flexMinimum.Add(0); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     var flexibleWidth = maxWidth - fixedWidths.Sum(); | ||||
|                     var flexWidths = Ratio.Distribute(flexibleWidth, ratios, flexMinimum); | ||||
|  | ||||
|                     var flexWidthsIterator = flexWidths.GetEnumerator(); | ||||
|                     foreach (var (index, _, _, column) in _columns.Enumerate()) | ||||
|                     { | ||||
|                         if (column.IsFlexible()) | ||||
|                         { | ||||
|                             flexWidthsIterator.MoveNext(); | ||||
|                             widths[index] = fixedWidths[index] + flexWidthsIterator.Current; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             tableWidth = widths.Sum(); | ||||
|  | ||||
|             if (tableWidth > maxWidth) | ||||
|             { | ||||
|                 var wrappable = _columns.Select(c => !c.NoWrap).ToList(); | ||||
| @@ -72,7 +31,7 @@ namespace Spectre.Console | ||||
|                 if (tableWidth > maxWidth) | ||||
|                 { | ||||
|                     var excessWidth = tableWidth - maxWidth; | ||||
|                     widths = Ratio.Reduce(excessWidth, widths.Select(w => 1).ToList(), widths, widths); | ||||
|                     widths = Ratio.Reduce(excessWidth, widths.Select(_ => 1).ToList(), widths, widths); | ||||
|                     tableWidth = widths.Sum(); | ||||
|                 } | ||||
|             } | ||||
| @@ -96,26 +55,17 @@ namespace Spectre.Console | ||||
|  | ||||
|             if (wrappable.AnyTrue()) | ||||
|             { | ||||
|                 while (totalWidth > 0 && excessWidth > 0) | ||||
|                 while (totalWidth != 0 && excessWidth > 0) | ||||
|                 { | ||||
|                     var maxColumn = widths.Zip(wrappable, (first, second) => (width: first, isWrappable: second)) | ||||
|                         .Where(x => x.isWrappable) | ||||
|                     var maxColumn = widths.Zip(wrappable, (first, second) => (width: first, allowWrap: second)) | ||||
|                         .Where(x => x.allowWrap) | ||||
|                         .Max(x => x.width); | ||||
|  | ||||
|                     var secondMaxColumn = widths.Zip(wrappable, (width, isWrappable) => isWrappable && width != maxColumn ? maxColumn : 0).Max(); | ||||
|                     var secondMaxColumn = widths.Zip(wrappable, (width, allowWrap) => allowWrap && width != maxColumn ? width : 0).Max(); | ||||
|                     var columnDifference = maxColumn - secondMaxColumn; | ||||
|  | ||||
|                     var ratios = widths.Zip(wrappable, (width, allowWrap) => | ||||
|                     { | ||||
|                         if (width == maxColumn && allowWrap) | ||||
|                         { | ||||
|                             return 1; | ||||
|                         } | ||||
|  | ||||
|                         return 0; | ||||
|                     }).ToList(); | ||||
|  | ||||
|                     if (!ratios.Any(x => x > 0) || columnDifference == 0) | ||||
|                     var ratios = widths.Zip(wrappable, (width, allowWrap) => width == maxColumn && allowWrap ? 1 : 0).ToList(); | ||||
|                     if (!ratios.Any(x => x != 0) || columnDifference == 0) | ||||
|                     { | ||||
|                         break; | ||||
|                     } | ||||
| @@ -133,10 +83,11 @@ namespace Spectre.Console | ||||
|  | ||||
|         private (int Min, int Max) MeasureColumn(TableColumn column, RenderContext options, int maxWidth) | ||||
|         { | ||||
|             var padding = column.Padding.GetHorizontalPadding(); | ||||
|  | ||||
|             // Predetermined width? | ||||
|             if (column.Width != null) | ||||
|             { | ||||
|                 var padding = column.GetPadding(); | ||||
|                 return (column.Width.Value + padding, column.Width.Value + padding); | ||||
|             } | ||||
|  | ||||
| @@ -152,7 +103,7 @@ namespace Spectre.Console | ||||
|                 maxWidths.Add(measure.Max); | ||||
|             } | ||||
|  | ||||
|             return (minWidths.Count > 0 ? minWidths.Max() : 1, | ||||
|             return (minWidths.Count > 0 ? minWidths.Max() : padding, | ||||
|                     maxWidths.Count > 0 ? maxWidths.Max() : maxWidth); | ||||
|         } | ||||
|  | ||||
| @@ -160,7 +111,7 @@ namespace Spectre.Console | ||||
|         { | ||||
|             var edges = 2; | ||||
|             var separators = _columns.Count - 1; | ||||
|             var padding = includePadding ? _columns.Select(x => x.GetPadding()).Sum() : 0; | ||||
|             var padding = includePadding ? _columns.Select(x => x.Padding.GetHorizontalPadding()).Sum() : 0; | ||||
|             return separators + edges + padding; | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -53,6 +53,8 @@ namespace Spectre.Console | ||||
|         /// </summary> | ||||
|         public bool SafeBorder { get; set; } = true; | ||||
|  | ||||
|         internal bool IsGrid { get; set; } = false; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="Table"/> class. | ||||
|         /// </summary> | ||||
| @@ -104,6 +106,20 @@ namespace Spectre.Console | ||||
|             _columns.AddRange(columns.Select(column => new TableColumn(column))); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Adds multiple columns to the table. | ||||
|         /// </summary> | ||||
|         /// <param name="columns">The columns to add.</param> | ||||
|         public void AddColumns(params TableColumn[] columns) | ||||
|         { | ||||
|             if (columns is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(columns)); | ||||
|             } | ||||
|  | ||||
|             _columns.AddRange(columns.Select(column => column)); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Adds a row to the table. | ||||
|         /// </summary> | ||||
| @@ -175,7 +191,7 @@ namespace Spectre.Console | ||||
|             var columnWidths = CalculateColumnWidths(context, maxWidth); | ||||
|  | ||||
|             // Update the table width. | ||||
|             width = columnWidths.Sum() + GetExtraWidth(includePadding: false); | ||||
|             width = columnWidths.Sum() + GetExtraWidth(includePadding: true); | ||||
|  | ||||
|             var rows = new List<List<Text>>(); | ||||
|             if (ShowHeaders) | ||||
| @@ -195,9 +211,12 @@ namespace Spectre.Console | ||||
|  | ||||
|                 // Get the list of cells for the row and calculate the cell height | ||||
|                 var cells = new List<List<SegmentLine>>(); | ||||
|                 foreach (var (rowWidth, cell) in columnWidths.Zip(row, (f, s) => (f, s))) | ||||
|                 foreach (var (columnIndex, _, _, (rowWidth, cell)) in columnWidths.Zip(row).Enumerate()) | ||||
|                 { | ||||
|                     var lines = Segment.SplitLines(((IRenderable)cell).Render(context, rowWidth)); | ||||
|                     var justification = _columns[columnIndex].Alignment; | ||||
|                     var childContext = context.WithJustification(justification); | ||||
|  | ||||
|                     var lines = Segment.SplitLines(((IRenderable)cell).Render(childContext, rowWidth)); | ||||
|                     cellHeight = Math.Max(cellHeight, lines.Count); | ||||
|                     cells.Add(lines); | ||||
|                 } | ||||
| @@ -208,9 +227,11 @@ namespace Spectre.Console | ||||
|                     result.Add(new Segment(border.GetPart(BorderPart.HeaderTopLeft))); | ||||
|                     foreach (var (columnIndex, _, lastColumn, columnWidth) in columnWidths.Enumerate()) | ||||
|                     { | ||||
|                         result.Add(new Segment(border.GetPart(BorderPart.HeaderTop))); // Left padding | ||||
|                         var padding = _columns[columnIndex].Padding; | ||||
|  | ||||
|                         result.Add(new Segment(border.GetPart(BorderPart.HeaderTop, padding.Left))); // Left padding | ||||
|                         result.Add(new Segment(border.GetPart(BorderPart.HeaderTop, columnWidth))); | ||||
|                         result.Add(new Segment(border.GetPart(BorderPart.HeaderTop))); // Right padding | ||||
|                         result.Add(new Segment(border.GetPart(BorderPart.HeaderTop, padding.Right))); // Right padding | ||||
|  | ||||
|                         if (!lastColumn) | ||||
|                         { | ||||
| @@ -237,9 +258,9 @@ namespace Spectre.Console | ||||
|                         } | ||||
|  | ||||
|                         // Pad column on left side. | ||||
|                         if (showBorder) | ||||
|                         if (showBorder || IsGrid) | ||||
|                         { | ||||
|                             var leftPadding = _columns[cellIndex].LeftPadding; | ||||
|                             var leftPadding = _columns[cellIndex].Padding.Left; | ||||
|                             if (leftPadding > 0) | ||||
|                             { | ||||
|                                 result.Add(new Segment(new string(' ', leftPadding))); | ||||
| @@ -257,9 +278,9 @@ namespace Spectre.Console | ||||
|                         } | ||||
|  | ||||
|                         // Pad column on the right side | ||||
|                         if (showBorder || (hideBorder && !lastCell)) | ||||
|                         if (showBorder || (hideBorder && !lastCell) || (IsGrid && !lastCell)) | ||||
|                         { | ||||
|                             var rightPadding = _columns[cellIndex].RightPadding; | ||||
|                             var rightPadding = _columns[cellIndex].Padding.Right; | ||||
|                             if (rightPadding > 0) | ||||
|                             { | ||||
|                                 result.Add(new Segment(new string(' ', rightPadding))); | ||||
| @@ -281,15 +302,17 @@ namespace Spectre.Console | ||||
|                     result.Add(Segment.LineBreak()); | ||||
|                 } | ||||
|  | ||||
|                 // Show bottom of header? | ||||
|                 // Show header separator? | ||||
|                 if (firstRow && showBorder && ShowHeaders) | ||||
|                 { | ||||
|                     result.Add(new Segment(border.GetPart(BorderPart.HeaderBottomLeft))); | ||||
|                     foreach (var (columnIndex, first, lastColumn, columnWidth) in columnWidths.Enumerate()) | ||||
|                     { | ||||
|                         result.Add(new Segment(border.GetPart(BorderPart.HeaderBottom))); // Left padding | ||||
|                         var padding = _columns[columnIndex].Padding; | ||||
|  | ||||
|                         result.Add(new Segment(border.GetPart(BorderPart.HeaderBottom, padding.Left))); // Left padding | ||||
|                         result.Add(new Segment(border.GetPart(BorderPart.HeaderBottom, columnWidth))); | ||||
|                         result.Add(new Segment(border.GetPart(BorderPart.HeaderBottom))); // Right padding | ||||
|                         result.Add(new Segment(border.GetPart(BorderPart.HeaderBottom, padding.Right))); // Right padding | ||||
|  | ||||
|                         if (!lastColumn) | ||||
|                         { | ||||
| @@ -307,9 +330,11 @@ namespace Spectre.Console | ||||
|                     result.Add(new Segment(border.GetPart(BorderPart.FooterBottomLeft))); | ||||
|                     foreach (var (columnIndex, first, lastColumn, columnWidth) in columnWidths.Enumerate()) | ||||
|                     { | ||||
|                         result.Add(new Segment(border.GetPart(BorderPart.FooterBottom))); | ||||
|                         var padding = _columns[columnIndex].Padding; | ||||
|  | ||||
|                         result.Add(new Segment(border.GetPart(BorderPart.FooterBottom, padding.Left))); // Left padding | ||||
|                         result.Add(new Segment(border.GetPart(BorderPart.FooterBottom, columnWidth))); | ||||
|                         result.Add(new Segment(border.GetPart(BorderPart.FooterBottom))); | ||||
|                         result.Add(new Segment(border.GetPart(BorderPart.FooterBottom, padding.Right))); // Right padding | ||||
|  | ||||
|                         if (!lastColumn) | ||||
|                         { | ||||
|   | ||||
| @@ -19,20 +19,9 @@ namespace Spectre.Console | ||||
|         public int? Width { get; set; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the left padding. | ||||
|         /// Gets or sets the padding of the column. | ||||
|         /// </summary> | ||||
|         public int LeftPadding { get; set; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the right padding. | ||||
|         /// </summary> | ||||
|         public int RightPadding { get; set; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the ratio to use when calculating column width. | ||||
|         /// If <c>null</c>, the column will adapt to it's contents. | ||||
|         /// </summary> | ||||
|         public int? Ratio { get; set; } | ||||
|         public Padding Padding { get; set; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets a value indicating whether wrapping of | ||||
| @@ -40,6 +29,11 @@ namespace Spectre.Console | ||||
|         /// </summary> | ||||
|         public bool NoWrap { get; set; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the alignment of the column. | ||||
|         /// </summary> | ||||
|         public Justify? Alignment { get; set; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="TableColumn"/> class. | ||||
|         /// </summary> | ||||
| @@ -48,20 +42,9 @@ namespace Spectre.Console | ||||
|         { | ||||
|             Text = Text.New(text ?? throw new ArgumentNullException(nameof(text))); | ||||
|             Width = null; | ||||
|             LeftPadding = 1; | ||||
|             RightPadding = 1; | ||||
|             Ratio = null; | ||||
|             Padding = new Padding(1, 1); | ||||
|             NoWrap = false; | ||||
|         } | ||||
|  | ||||
|         internal int GetPadding() | ||||
|         { | ||||
|             return LeftPadding + RightPadding; | ||||
|         } | ||||
|  | ||||
|         internal bool IsFlexible() | ||||
|         { | ||||
|             return Width == null; | ||||
|             Alignment = null; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -18,6 +18,11 @@ namespace Spectre.Console | ||||
|         private readonly List<Span> _spans; | ||||
|         private string _text; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the text alignment. | ||||
|         /// </summary> | ||||
|         public Justify Alignment { get; set; } = Justify.Left; | ||||
|  | ||||
|         private sealed class Span | ||||
|         { | ||||
|             public int Start { get; } | ||||
| @@ -38,7 +43,7 @@ namespace Spectre.Console | ||||
|         /// <param name="text">The text.</param> | ||||
|         internal Text(string text) | ||||
|         { | ||||
|             _text = text ?? throw new ArgumentNullException(nameof(text)); | ||||
|             _text = text?.NormalizeLineEndings() ?? throw new ArgumentNullException(nameof(text)); | ||||
|             _spans = new List<Span>(); | ||||
|         } | ||||
|  | ||||
| @@ -51,12 +56,26 @@ namespace Spectre.Console | ||||
|         /// <param name="decoration">The text decoration.</param> | ||||
|         /// <returns>A <see cref="Text"/> instance.</returns> | ||||
|         public static Text New( | ||||
|             string text, Color? foreground = null, Color? background = null, Decoration? decoration = null) | ||||
|             string text, | ||||
|             Color? foreground = null, | ||||
|             Color? background = null, | ||||
|             Decoration? decoration = null) | ||||
|         { | ||||
|             var result = MarkupParser.Parse(text, new Style(foreground, background, decoration)); | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Sets the text alignment. | ||||
|         /// </summary> | ||||
|         /// <param name="alignment">The text alignment.</param> | ||||
|         /// <returns>The same <see cref="Text"/> instance.</returns> | ||||
|         public Text WithAlignment(Justify alignment) | ||||
|         { | ||||
|             Alignment = alignment; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Appends some text with the specified color and decorations. | ||||
|         /// </summary> | ||||
| @@ -99,14 +118,17 @@ namespace Spectre.Console | ||||
|         /// <inheritdoc/> | ||||
|         Measurement IRenderable.Measure(RenderContext context, int maxWidth) | ||||
|         { | ||||
|             var lines = Segment.SplitLines(((IRenderable)this).Render(context, maxWidth)); | ||||
|             if (lines.Count == 0) | ||||
|             if (string.IsNullOrEmpty(_text)) | ||||
|             { | ||||
|                 return new Measurement(0, maxWidth); | ||||
|                 return new Measurement(1, 1); | ||||
|             } | ||||
|  | ||||
|             var max = lines.Max(line => line.Length); | ||||
|             var min = lines.SelectMany(line => line.Select(segment => segment.Text.Length)).Max(); | ||||
|             // TODO: Write some kind of tokenizer for this | ||||
|             var min = Segment.SplitLines(((IRenderable)this).Render(context, maxWidth)) | ||||
|                 .SelectMany(line => line.Select(segment => segment.Text.Length)) | ||||
|                 .Max(); | ||||
|  | ||||
|             var max = _text.SplitLines().Max(x => Cell.GetCellLength(context.Encoding, x)); | ||||
|  | ||||
|             return new Measurement(min, max); | ||||
|         } | ||||
| @@ -122,13 +144,49 @@ namespace Spectre.Console | ||||
|             var result = new List<Segment>(); | ||||
|             var segments = SplitLineBreaks(CreateSegments()); | ||||
|  | ||||
|             var justification = context.Justification ?? Alignment; | ||||
|  | ||||
|             foreach (var (_, _, last, line) in Segment.SplitLines(segments, width).Enumerate()) | ||||
|             { | ||||
|                 var length = line.Sum(l => l.StripLineEndings().CellLength(context.Encoding)); | ||||
|  | ||||
|                 if (length < width) | ||||
|                 { | ||||
|                     // Justify right side | ||||
|                     if (justification == Justify.Right) | ||||
|                     { | ||||
|                         var diff = width - length; | ||||
|                         result.Add(new Segment(new string(' ', diff))); | ||||
|                     } | ||||
|                     else if (justification == Justify.Center) | ||||
|                     { | ||||
|                         var diff = (width - length) / 2; | ||||
|                         result.Add(new Segment(new string(' ', diff))); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 // Render the line. | ||||
|                 foreach (var segment in line) | ||||
|                 { | ||||
|                     result.Add(segment.StripLineEndings()); | ||||
|                 } | ||||
|  | ||||
|                 // Justify left side | ||||
|                 if (length < width) | ||||
|                 { | ||||
|                     if (justification == Justify.Center) | ||||
|                     { | ||||
|                         var diff = (width - length) / 2; | ||||
|                         result.Add(new Segment(new string(' ', diff))); | ||||
|  | ||||
|                         var remainder = (width - length) % 2; | ||||
|                         if (remainder != 0) | ||||
|                         { | ||||
|                             result.Add(new Segment(new string(' ', remainder))); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 if (!last) | ||||
|                 { | ||||
|                     result.Add(Segment.LineBreak()); | ||||
|   | ||||
| @@ -17,6 +17,11 @@ namespace Spectre.Console.Internal | ||||
|  | ||||
|         public static string NormalizeLineEndings(this string text, bool native = false) | ||||
|         { | ||||
|             if (text == null) | ||||
|             { | ||||
|                 return null; | ||||
|             } | ||||
|  | ||||
|             var normalized = text?.Replace("\r\n", "\n") | ||||
|                 ?.Replace("\r", string.Empty); | ||||
|  | ||||
|   | ||||
| @@ -14,7 +14,7 @@ namespace Spectre.Console.Internal | ||||
|         { | ||||
|             ratios = ratios.Zip(maximums, (a, b) => (ratio: a, max: b)).Select(a => a.max > 0 ? a.ratio : 0).ToList(); | ||||
|             var totalRatio = ratios.Sum(); | ||||
|             if (totalRatio == 0) | ||||
|             if (totalRatio <= 0) | ||||
|             { | ||||
|                 return values; | ||||
|             } | ||||
| @@ -24,9 +24,9 @@ namespace Spectre.Console.Internal | ||||
|  | ||||
|             foreach (var (ratio, maximum, value) in ratios.Zip(maximums, values)) | ||||
|             { | ||||
|                 if (ratio > 0 && totalRatio > 0) | ||||
|                 if (ratio != 0 && totalRatio > 0) | ||||
|                 { | ||||
|                     var distributed = (int)Math.Min(maximum, Math.Round(ratio * totalRemaining / (double)totalRatio)); | ||||
|                     var distributed = (int)Math.Min(maximum, Math.Round((double)(ratio * totalRemaining / totalRatio))); | ||||
|                     result.Add(value - distributed); | ||||
|                     totalRemaining -= distributed; | ||||
|                     totalRatio -= ratio; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user