Compare commits

..

18 Commits

Author SHA1 Message Date
Patrik Svensson
03334f693d Add support for table footers 2020-10-26 00:01:13 +01:00
Patrik Svensson
c9c0ad733f Fix rendering bug when splitting lines
The bug might occur if there are wide characters such as emojis
at the end of a line. The SplitLines method mixed cell width
and text length, which might give incorrect results. This commit
makes sure that comparison and calculation is done using cell width
where it's appropriate.
2020-10-24 01:45:41 +02:00
Patrik Svensson
041bd016a2 Remove verbs from extension methods
Removed the verbs from all extension methods that manipulate
properties which makes the API more succinct and easier to read.

Also added implicit conversion from string to Style.

As a good OSS citizen, I've obsoleted the old methods with
a warning for now, so this shouldn't break anyone using
the old methods.
2020-10-23 15:08:18 +02:00
Takahito Yamatoya
037a215a78 Add documentation for rule 2020-10-22 08:53:20 +02:00
Patrik Svensson
9afc1ea721 Add support for aligning tables 2020-10-21 18:15:51 +02:00
Takahito Yamatoya
b52056ee49 Update documentation for calendar 2020-10-20 21:55:29 +02:00
Patrik Svensson
3941fd81ab Move table and calendar docs 2020-10-20 02:32:04 +02:00
Patrik Svensson
5a1b8a1710 Add rule widget
Adds a new rule widget.
Also fixes some bugs I encountered while testing
some unrelated things in an extremely small console.
2020-10-20 01:45:53 +02:00
Patrik Svensson
1410cba6c5 Add Markdown.Escape method 2020-10-20 01:09:26 +02:00
Patrik Svensson
70fc14e9cd Add new ctors for Rows and Columns widgets 2020-10-19 21:25:28 +02:00
Takahito Yamatoya
0b4359a52a Add installation instructions
* to add how to install

* fix

* remove empty line
2020-10-19 16:50:54 +02:00
Patrik Svensson
cb2924a609 Add table heading and footnote support
Closes #116
2020-10-19 15:46:57 +02:00
Takahito Yamatoya
5c119ee0c3 Add documentation for calendar 2020-10-19 15:44:16 +02:00
Takahito Yamatoya
b9d182b6e3 Add README.jp.md 2020-10-19 15:42:33 +02:00
Patrik Svensson
bfffef630f Do not draw tables that can't be drawn
This is a temporary fix for undrawable tables until we've
implemented a proper strategy. What this does is that it replaces
an undrawable table with an ellipsis (...). This should only
occur in either super big tables or deeply nested tables in a
console with a small buffer width.
2020-10-18 08:03:40 +02:00
Takahito Yamatoya
a2f507e58f update sample output 2020-10-18 06:56:47 +02:00
Takahito Yamatoya
d1d06d6a6b fix incorrect command 2020-10-18 06:56:47 +02:00
Patrik Svensson
52718c499c Fix minor typo in example 2020-10-17 13:22:01 +02:00
107 changed files with 3734 additions and 652 deletions

413
README.jp.md Normal file
View File

@@ -0,0 +1,413 @@
# `Spectre.Console`
_[![Spectre.Console NuGet Version](https://img.shields.io/nuget/v/spectre.console.svg?style=flat&label=NuGet%3A%20Spectre.Console)](https://www.nuget.org/packages/spectre.console)_
綺麗なコンソールアプリケーションを簡単に作成するための.NET Standard 2.0ライブラリです。
Python用の素晴らしい[Rich ライブラリ](https://github.com/willmcgugan/rich)に強く影響を受けています。
## 目次
1. [特徴](#特徴)
2. [](#例)
3. [使用方法](#使用方法)
3.1. [Static APIの利用](#static-apiの利用)
3.2. [コンソールの作成](#コンソールの作成)
4. [例の実行](#例の実行)
5. [クイックスタート](#クイックスタート)
6. [マークアップ](#マークアップ)
7. [絵文字](#絵文字)
8. [テーブル](#テーブル)
9. [例外](#例外)
## 特徴
* ユニットテストを意識して書いています。
* table、grid、panel、マークアップ言語に影響を受けた [rich](https://github.com/willmcgugan/rich) に対応しています。
* 太字、薄字、斜字、下線、斜線、点滅などの一般的なSGR parameters に対応しています。
* ターミナルで 3/4/8/24ビットカラーに対応しています。
ライブラリは現在のターミナルの性能を検知し、必要なカラーにダウングレードします
## 例
![Example](resources/gfx/screenshots/example.png)
## 使用方法
`Spectre.Console` APIはステートフルで、スレッドセーフではありません。
異なるスレッドからコンソールに書く必要がある場合、通常の`System.Console` APIを使用するときと同様、適切な注意を払ってください。
現在の端末がANSIエスケープシーケンスに対応していない場合、
`Spectre.Console`は、`System.Console` APIの利用に切り替わります。
_メモ: このライブラリは現在開発中で、APIは1.0のリリースまでの間に変更されたり、
削除されたりする可能性があります。_
### Static APIの利用
`System.Console` APIでするように、テキストを出力したいだけの時にはstatic APIが最適ですが、綺麗です。
```csharp
AnsiConsole.Foreground = Color.CornflowerBlue;
AnsiConsole.Decoration = Decoration.Underline | Decoration.Bold;
AnsiConsole.WriteLine("Hello World!");
AnsiConsole.Reset();
AnsiConsole.MarkupLine("[bold yellow on red]{0}[/] [underline]world[/]!", "Goodbye");
```
もし、デフォルトの`IAnsiConsole`への参照を取得したい場合、
`AnsiConsole.Console`経由でアクセスできます。
### コンソールの作成
単体テスト中にコードの実行環境を制御したい場合など、
特定の機能をもつコンソールを明示的に作成すると便利なことがあります。
単体テストの一部としてコードで `AnsiConsole`を使わないことを推奨します。
```csharp
IAnsiConsole console = AnsiConsole.Create(
new AnsiConsoleSettings()
{
Ansi = AnsiSupport.Yes,
ColorSystem = ColorSystemSupport.TrueColor,
Out = new StringWriter(),
});
```
_メモ: 主導でコンソールを作成しているときに特定のカラーシステムを指定できたとしても、
ユーザーのターミナルでは使えないかもしれないことを覚えておいてください。
テスト用にIAnsiConsoleを作成していない限り、
常に`ColorSystemSupport.Detect``AnsiSupport.Detect`を使用してください。_
## 例の実行
Spectre.Consoleでできることを見るために、
[dotnet-example](https://github.com/patriksvensson/dotnet-example)グローバルツールをインストールします。
```
> dotnet tool install -g dotnet-example
```
このリポジトリで提供している例が一覧表示されます
```
> dotnet example
╭────────────┬───────────────────────────────────────┬──────────────────────────────────────────────────────╮
│ Name │ Path │ Description │
├────────────┼───────────────────────────────────────┼──────────────────────────────────────────────────────┤
│ Borders │ examples/Borders/Borders.csproj │ Demonstrates the different kind of borders. │
│ Calendars │ examples/Calendars/Calendars.csproj │ Demonstrates how to render calendars. │
│ Colors │ examples/Colors/Colors.csproj │ Demonstrates how to use colors in the console. │
│ Columns │ examples/Columns/Columns.csproj │ Demonstrates how to render data into columns. │
│ Emojis │ examples/Emojis/Emojis.csproj │ Demonstrates how to render emojis. │
│ Exceptions │ examples/Exceptions/Exceptions.csproj │ Demonstrates how to render formatted exceptions. │
│ Grids │ examples/Grids/Grids.csproj │ Demonstrates how to render grids in a console. │
│ Info │ examples/Info/Info.csproj │ Displays the capabilities of the current console. │
│ Links │ examples/Links/Links.csproj │ Demonstrates how to render links in a console. │
│ Panels │ examples/Panels/Panels.csproj │ Demonstrates how to render items in panels. │
│ Rules │ examples/Rules/Rules.csproj │ Demonstrates how to render horizontal rules (lines). │
│ Tables │ examples/Tables/Tables.csproj │ Demonstrates how to render tables in a console. │
╰────────────┴───────────────────────────────────────┴──────────────────────────────────────────────────────╯
```
そして、例を実行します
```
> dotnet example tables
┌──────────┬──────────┬────────┐
│ Foo │ Bar │ Baz │
├──────────┼──────────┼────────┤
│ Hello │ World! │ │
│ Bonjour │ le │ monde! │
│ Hej │ Världen! │ │
└──────────┴──────────┴────────┘
```
## クイックスタート
pectre.Consoleの利用を開始する最初の方法は、Nugetパッケージをインストールすることです。
```shell
> dotnet add package Spectre.Console
```
その後、`Spectre.Console`名前空間を参照する必要があります。一度参照したら、提供されている全ての機能を使用できます。
```csharp
using Spectre.Console
public static class Program
{
public static void Main(string[] args)
{
AnsiConsole.Markup("[underline red]Hello[/] World!");
}
}
```
## マークアップ
`Markup`クラスは、コンソールにリッチなテキストを出力することができます。
### 文法
コンソールマークアップはbbcodeに影響を受けた文法を利用します。角括弧でスタイルを書いたらスタイルを参照、例えば、`[bold red]`
は、`[/]`で閉じるまでスタイルが適用されます。
```csharp
AnsiConsole.Render(new Markup("[bold yellow]Hello[/] [red]World![/]"));
```
`Markup` クラスは`IRenderable`を実装しており、table、grid、Panelを使用できることを意味します。
`IRenderable`のレンダリングに対応している多くのクラスは、リッチテキストの描画を上書きます。
```csharp
var table = new Table();
table.AddColumn(new TableColumn(new Markup("[yellow]Foo[/]")));
table.AddColumn(new TableColumn("[blue]Bar[/]"));
```
### 便利なメソッド
`AnsiConsole`には、新しい`Markup`インスタンスをインスタンス化することなく、コンソールにマークアップテキストを書き込める便利なメソッドがあります。
```csharp
AnsiConsole.Markup("[underline green]Hello[/] ");
AnsiConsole.MarkupLine("[bold]World[/]");
```
### エスケープ文字列
`[`を出力するために、 `[[`を利用し、`]`を出力するために`]]`を利用します。
```csharp
AnsiConsole.Markup("[[Hello]] "); // [Hello]
AnsiConsole.Markup("[red][[World]][/]"); // [World]
```
`SafeMarkup`拡張メソッドを使用することもできます。
```csharp
AnsiConsole.Markup("[red]{0}[/]", "Hello [World]".SafeMarkup());
```
### カラー
`new Style(foreground: Color.Maroon)`のようなコード、または、`AnsiConsole.Markup("[maroon on blue]Hello[/]")`のようなマークアップテキストで色を使用できます。
### 背景色の設定
カラー指定の際に、`on`を付けることで、マークアップで背景色を設定できます。
```
[bold yellow on blue]Hello[/]
[default on blue]World[/]
```
### 絵文字の描画
マークアップの一部として絵文字を出力するために、emojiショートコードが使用できます。
```csharp
AnsiConsole.MarkupLine("Hello :globe_showing_europe_africa:!");
```
emojiのスタイルについては、付録の[Emoji](./appendix/emojis) を参照してください。
### カラー
上の例では、全ての色は名前で参照されています。
しかし、16進数やRGB表現をマークダウンで色指定に使用できます。
```csharp
AnsiConsole.Markup("[red]Foo[/] ");
AnsiConsole.Markup("[#ff0000]Bar[/] ");
AnsiConsole.Markup("[rgb(255,0,0)]Baz[/] ");
```
## 絵文字
どのような絵文字が使用できるかは、使用しているOSやターミナルに依存し、どのように表示されるかは保証されません。絵文字の幅計算は正確ではないため、表、パネル、グリッドで使用する場合は表示がずれるかもしれません。
完全な互換性を確保するために、Unicode 13.0 より以前の`Emoji_Presentation`カテゴリにあるものだけを使用することを検討してください。
公式の絵文字一覧
https://www.unicode.org/Public/UCD/latest/ucd/emoji/emoji-data.txt
```csharp
// Markup
AnsiConsole.MarkupLine("Hello :globe_showing_europe_africa:!");
// Constant
var hello = "Hello " + Emoji.Known.GlobeShowingEuropeAfrica;
```
テキスト内の絵文字を置き換えることができます。
```csharp
var phrase = "Mmmm :birthday_cake:";
var rendered
```
既存の絵文字を別のものにしたり、完全に新しい物を追加したいことがあります。このために、`Emoji.Remap`メソッドを使用できます。
この方法は、マークアップ文字と`Emoji.Replace`の両方で動作します。
```csharp
// Remap the emoji
Emoji.Remap("globe_showing_europe_africa", "😄");
// Render markup
AnsiConsole.MarkupLine("Hello :globe_showing_europe_africa:!");
// Replace emojis in string
var phrase = "Hello :globe_showing_europe_africa:!";
var rendered = Emoji.Replace(phrase);
```
## テーブル
テーブルはターミナルで表データを表示するのに完璧な方法です。
`Spectre.Console` は、テーブルの描画にとても優れていて、全てのカラムは中に合わせて調整してくれます。
`IRenderable`を実装しているものは、列ヘッダやセル、別のテーブルとして使用できます。
### 使い方
テーブルを描画するために、`Table`インスタンスを作成し、必要な数の列を追加し、行を追加します。
テーブルをコンソールの`Render`メソッドに渡して終わりです。
```csharp
// テーブルの作成
var table = new Table();
// 列の追加
table.AddColumn("Foo");
table.AddColumn(new TableColumn("Bar").Centered());
// 行の追加
table.AddRow("Baz", "[green]Qux[/]");
table.AddRow(new Markup("[blue]Corgi[/]"), new Panel("Waldo"));
// コンソールにテーブルの描画
AnsiConsole.Render(table);
```
これは次のように出力を描画します。
![Table](docs/input/assets/images/table.png)
### 罫線
```csharp
// 罫線を設定します
table.SetBorder(Border.None);
table.SetBorder(Border.Ascii);
table.SetBorder(Border.Square);
table.SetBorder(Border.Rounded);
```
### 拡大 / 縮小
```csharp
// テーブル幅を最大に設定します
table.Expand();
// テーブル幅を最小に設定します
table.Collapse();
```
### ヘッダーを隠す
```csharp
// 全ての列のヘッダーを隠します
table.HideHeaders();
```
### テーブル幅の設定
```csharp
// テーブル幅50セルに設定します
table.SetWidth(50);
```
### 整列(アライメント)
```csharp
// 整列を明示的に設定する
column.SetAlignment(Justify.Right);
```
### パディング
```csharp
// 左と右のパディングを設定する
column.SetPadding(left: 3, right: 5);
// 個別にパディングを設定する
column.PadLeft(3);
column.PadRight(5);
```
### 列改行の無効化
```csharp
// 列改行の無効化
column.NoWrap();
```
### 列幅の設定
```csharp
// 列幅の設定(これはまだ柔軟な拡張メソッドがありません)
column.Width = 15;
```
## 例外
例外はターミナルで見たときに読みやすいとは限りません。
`WriteException`メソッドを使用することで、例外をもう少し読みやすくすることができます。
```csharp
AnsiConsole.WriteException(ex);
```
![exception](docs/input/assets/images/exception.png)
### 例外の省略表示
例外の特定部分を短くして、さらに読みやすくしたり、パスをクリック可能なハイパーリンクにすることもできます。
ハイパーリンクがクリックできるかはターミナル次第です。
```csharp
AnsiConsole.WriteException(ex,
ExceptionFormat.ShortenPaths | ExceptionFormat.ShortenTypes |
ExceptionFormat.ShortenMethods | ExceptionFormat.ShowLinks);
```
![exception](docs/input/assets/images/compact_exception.png)
### 例外出力のカスタマイズ
例外の特定部分を短縮するだけでなく、デフォルトのスタイルを上書きすることもできます。
```csharp
AnsiConsole.WriteException(ex, new ExceptionSettings
{
Format = ExceptionFormats.ShortenEverything | ExceptionFormats.ShowLinks,
Style = new ExceptionStyle
{
Exception = Style.WithForeground(Color.Grey),
Message = Style.WithForeground(Color.White),
NonEmphasized = Style.WithForeground(Color.Cornsilk1),
Parenthesis = Style.WithForeground(Color.Cornsilk1),
Method = Style.WithForeground(Color.Red),
ParameterName = Style.WithForeground(Color.Cornsilk1),
ParameterType = Style.WithForeground(Color.Red),
Path = Style.WithForeground(Color.Red),
LineNumber = Style.WithForeground(Color.Cornsilk1),
}
});
```
![exception](docs/input/assets/images/custom_exception.png)

View File

@@ -10,10 +10,11 @@ for Python.
1. [Features](#features)
2. [Example](#example)
3. [Usage](#usage)
3.1. [Using the static API](#using-the-static-api)
3.2. [Creating a console](#creating-a-console)
4. [Running examples](#running-examples)
3. [Installing](#installing)
4. [Usage](#usage)
4.1. [Using the static API](#using-the-static-api)
4.2. [Creating a console](#creating-a-console)
5. [Running examples](#running-examples)
## Features
@@ -30,6 +31,14 @@ for Python.
![Example](resources/gfx/screenshots/example.png)
## Installing
The fastest way of getting started using Spectre.Console is to install the NuGet package.
```csharp
dotnet add package Spectre.Console
```
## Usage
The `Spectre.Console` API is stateful and is not thread-safe.
@@ -99,20 +108,28 @@ Now you can list available examples in this repository:
```
> dotnet example
╭───────────────────────────────────────┬─────────────────────────────────────────────────╮
│ Name │ Path │ Description │
├───────────────────────────────────────┼─────────────────────────────────────────────────┤
Colors │ examples/Colors/Colors.csproj │ Demonstrates how to use colors in the console.
Grid │ examples/Grid/Grid.csproj │ Demonstrates how to render grids in a console.
Panel │ examples/Panel/Panel.csproj │ Demonstrates how to render items in panels.
Table │ examples/Table/Table.csproj │ Demonstrates how to render tables in a console.
╰────────┴───────────────────────────────┴─────────────────────────────────────────────────╯
╭────────────┬───────────────────────────────────────┬──────────────────────────────────────────────────────
│ Name │ Path │ Description
├────────────┼───────────────────────────────────────┼──────────────────────────────────────────────────────
Borders │ examples/Borders/Borders.csproj │ Demonstrates the different kind of borders.
Calendars │ examples/Calendars/Calendars.csproj │ Demonstrates how to render calendars.
Colors │ examples/Colors/Colors.csproj │ Demonstrates how to use colors in the console.
Columns │ examples/Columns/Columns.csproj │ Demonstrates how to render data into columns.
│ Emojis │ examples/Emojis/Emojis.csproj │ Demonstrates how to render emojis. │
│ Exceptions │ examples/Exceptions/Exceptions.csproj │ Demonstrates how to render formatted exceptions. │
│ Grids │ examples/Grids/Grids.csproj │ Demonstrates how to render grids in a console. │
│ Info │ examples/Info/Info.csproj │ Displays the capabilities of the current console. │
│ Links │ examples/Links/Links.csproj │ Demonstrates how to render links in a console. │
│ Panels │ examples/Panels/Panels.csproj │ Demonstrates how to render items in panels. │
│ Rules │ examples/Rules/Rules.csproj │ Demonstrates how to render horizontal rules (lines). │
│ Tables │ examples/Tables/Tables.csproj │ Demonstrates how to render tables in a console. │
╰────────────┴───────────────────────────────────────┴──────────────────────────────────────────────────────╯
```
And to run an example:
```
> dotnet example table
> dotnet example tables
┌──────────┬──────────┬────────┐
│ Foo │ Bar │ Baz │
├──────────┼──────────┼────────┤

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@@ -20,7 +20,7 @@ for Python written by Will McGugan.
The library will detect the capabilities of the current terminal
and downgrade colors as needed.
## Example
## Examples
<img width="100%"
src="https://github.com/spectresystems/spectre.console/raw/main/resources/gfx/screenshots/example.png" />
<img src="assets/images/table.gif" style="max-width: 100%; margin-top: 15px; margin-bottom: 25px;" />
<img src="https://github.com/spectresystems/spectre.console/raw/main/resources/gfx/screenshots/example.png" style="max-width: 100%;" />

View File

@@ -0,0 +1,114 @@
Title: Calendar
Order: 4
RedirectFrom: calendar
---
The `Calendar` is used to render a calendar to the terminal.
# Usage
To render a calendar, create a `Calendar` instance with a target date.
```csharp
var calendar = new Calendar(2020,10);
AnsiConsole.Render(calendar);
2020 October
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐
Sun Mon Tue Wed Thu Fri Sat
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
```
## Culture
You can set the calendar's culture to show localized weekdays.
```csharp
var calendar = new Calendar(2020,10);
calendar.SetCulture("ja-JP");
AnsiConsole.Render(calendar);
202010
┌────┬────┬────┬────┬────┬────┬────┐
├────┼────┼────┼────┼────┼────┼────┤
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
└────┴────┴────┴────┴────┴────┴────┘
```
## Header
You can hide the calendar header.
```csharp
var calendar = new Calendar(2020,10);
calendar.ShowHeader = false;
AnsiConsole.Render(calendar);
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐
Sun Mon Tue Wed Thu Fri Sat
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
```
You can set the header style of the calendar.
```csharp
var calendar = new Calendar(2020,10);
calendar.SetHeaderStyle(Style.Parse("blue bold"));
AnsiConsole.Render(calendar);
```
## Calendar Event
You can add an event to the calendar.
If a date has an event associated with it, the date gets highlighted in the calendar.
```csharp
var calendar = new Calendar(2020,10);
calendar.AddCalendarEvent(2020, 10, 11);
AnsiConsole.Render(calendar);
2020 October
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐
Sun Mon Tue Wed Thu Fri Sat
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
1 2 3
4 5 6 7 8 9 10
11* 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
```
### Highlight style
You can set the highlight style for a calendar event via `SetHighlightStyle`.
```csharp
var calendar = new Calendar(2020,10);
calendar.AddCalendarEvent(2020, 10, 11);
calendar.SetHighlightStyle(Style.Parse("yellow bold"));
AnsiConsole.Render(calendar);
```

View File

@@ -0,0 +1,3 @@
Title: Widgets
Order: 9
---

View File

@@ -0,0 +1,71 @@
Title: Rule
Order: 5
RedirectFrom: rule
---
The `Rule` class is used to render a horizontal rule (line) to the terminal.
![Rule](../assets/images/rule.png)
# Usage
To render a rule without a title:
```csharp
var rule = new Rule();
AnsiConsole.Render(rule);
```
## Title
You can set the rule title markup text.
```csharp
var rule = new Rule("[red]Hello[/]");
AnsiConsole.Render(rule);
// output
───────────────────────────────── Hello ─────────────────────────────────
```
### Title alignment
You can set the rule's title alignment.
```csharp
var rule = new Rule("[red]Hello[/]");
rule.Alignment = Justify.Left;
AnsiConsole.Render(rule);
//output
── Hello ────────────────────────────────────────────────────────────────
```
You can also specify it with a method
```csharp
var rule = new Rule("[red]Hello[/]");
rule.LeftAligned();
AnsiConsole.Render(rule);
//output
── Hello ────────────────────────────────────────────────────────────────
```
## Style
You can set the rule style.
```csharp
var rule = new Rule("[red]Hello[/]");
rule.Style = Style.Parse("red dim");
AnsiConsole.Render(rule);
```
You can also specify it with a method
```csharp
var rule = new Rule("[red]Hello[/]");
rule.SetStyle("red dim");
AnsiConsole.Render(rule);
```

View File

@@ -1,5 +1,6 @@
Title: Tables
Title: Table
Order: 3
RedirectFrom: tables
---
Tables are a perfect way of displaying tabular data in a terminal.
@@ -36,7 +37,7 @@ AnsiConsole.Render(table);
This will render the following output:
![Table](assets/images/table.png)
![Table](../assets/images/table.png)
# Table appearance

View File

@@ -1,3 +1,4 @@
using System.Diagnostics;
using Spectre.Console;
using Spectre.Console.Rendering;
@@ -8,26 +9,22 @@ namespace BordersExample
public static void Main()
{
// Render panel borders
AnsiConsole.WriteLine();
AnsiConsole.MarkupLine("[white bold underline]PANEL BORDERS[/]");
AnsiConsole.WriteLine();
RenderPanelBorders();
HorizontalRule("PANEL BORDERS");
PanelBorders();
// Render table borders
AnsiConsole.WriteLine();
AnsiConsole.MarkupLine("[white bold underline]TABLE BORDERS[/]");
AnsiConsole.WriteLine();
RenderTableBorders();
HorizontalRule("TABLE BORDERS");
TableBorders();
}
private static void RenderPanelBorders()
private static void PanelBorders()
{
static IRenderable CreatePanel(string name, BoxBorder border)
{
return new Panel($"This is a panel with\nthe [yellow]{name}[/] border.")
.SetHeader($" {name} ", Style.Parse("blue"), Justify.Center)
.SetBorderStyle(Style.Parse("grey"))
.SetBorder(border);
.Header($" {name} ", Style.Parse("blue"), Justify.Center)
.Border(border)
.BorderStyle(Style.Parse("grey"));
}
var items = new[]
@@ -46,19 +43,18 @@ namespace BordersExample
new Padding(2,0,0,0)));
}
private static void RenderTableBorders()
private static void TableBorders()
{
static IRenderable CreateTable(string name, TableBorder border)
{
var table = new Table().SetBorder(border);
table.AddColumn("[yellow]Header 1[/]");
table.AddColumn("[yellow]Header 2[/]", col => col.RightAligned());
var table = new Table().Border(border);
table.AddColumn("[yellow]Header 1[/]", c => c.Footer("[grey]Footer 1[/]"));
table.AddColumn("[yellow]Header 2[/]", col => col.Footer("[grey]Footer 2[/]").RightAligned());
table.AddRow("Cell", "Cell");
table.AddRow("Cell", "Cell");
return new Panel(table)
.SetHeader($" {name} ", Style.Parse("blue"), Justify.Center)
.SetBorderStyle(Style.Parse("grey"))
.Header($" {name} ", Style.Parse("blue"), Justify.Center)
.NoBorder();
}
@@ -85,5 +81,12 @@ namespace BordersExample
AnsiConsole.Render(new Columns(items).Collapse());
}
private static void HorizontalRule(string title)
{
AnsiConsole.WriteLine();
AnsiConsole.Render(new Rule($"[white bold]{title}[/]").RuleStyle("grey").LeftAligned());
AnsiConsole.WriteLine();
}
}
}

View File

@@ -1,6 +1,4 @@
using System.Collections.Generic;
using Spectre.Console;
using Spectre.Console.Rendering;
namespace Calendars
{
@@ -9,60 +7,13 @@ namespace Calendars
public static void Main(string[] args)
{
AnsiConsole.WriteLine();
AnsiConsole.Render(
new Columns(GetCalendars())
.Collapse());
}
private static IEnumerable<IRenderable> GetCalendars()
{
yield return EmbedInPanel(
"Invariant calendar",
new Calendar(2020, 10)
.SimpleHeavyBorder()
.SetHighlightStyle(Style.Parse("red"))
.AddCalendarEvent("An event", 2020, 9, 22)
.AddCalendarEvent("Another event", 2020, 10, 2)
.AddCalendarEvent("A third event", 2020, 10, 13));
yield return EmbedInPanel(
"Swedish calendar (sv-SE)",
new Calendar(2020, 10)
AnsiConsole.Render(new Calendar(2020, 10)
.RoundedBorder()
.SetHighlightStyle(Style.Parse("blue"))
.SetCulture("sv-SE")
.HighlightStyle(Style.Parse("red"))
.HeaderStyle(Style.Parse("yellow"))
.AddCalendarEvent("An event", 2020, 9, 22)
.AddCalendarEvent("Another event", 2020, 10, 2)
.AddCalendarEvent("A third event", 2020, 10, 13));
yield return EmbedInPanel(
"German calendar (de-DE)",
new Calendar(2020, 10)
.MarkdownBorder()
.SetHighlightStyle(Style.Parse("yellow"))
.SetCulture("de-DE")
.AddCalendarEvent("An event", 2020, 9, 22)
.AddCalendarEvent("Another event", 2020, 10, 2)
.AddCalendarEvent("A third event", 2020, 10, 13));
yield return EmbedInPanel(
"Italian calendar (de-DE)",
new Calendar(2020, 10)
.DoubleBorder()
.SetHighlightStyle(Style.Parse("green"))
.SetCulture("it-IT")
.AddCalendarEvent("An event", 2020, 9, 22)
.AddCalendarEvent("Another event", 2020, 10, 2)
.AddCalendarEvent("A third event", 2020, 10, 13));
}
private static IRenderable EmbedInPanel(string title, Calendar calendar)
{
return new Panel(calendar)
.Expand()
.RoundedBorder()
.SetBorderStyle(Style.Parse("grey"))
.SetHeader($" {title} ", Style.Parse("yellow"));
}
}
}

View File

@@ -24,7 +24,7 @@ namespace ColorExample
AnsiConsole.ResetColors();
AnsiConsole.WriteLine();
AnsiConsole.MarkupLine("[bold underline]3-bit Colors[/]");
AnsiConsole.Render(new Rule("[yellow bold underline]3-bit Colors[/]").RuleStyle("grey").LeftAligned());
AnsiConsole.WriteLine();
for (var i = 0; i < 8; i++)
@@ -47,7 +47,7 @@ namespace ColorExample
AnsiConsole.ResetColors();
AnsiConsole.WriteLine();
AnsiConsole.MarkupLine("[bold underline]4-bit Colors[/]");
AnsiConsole.Render(new Rule("[yellow bold underline]4-bit Colors[/]").RuleStyle("grey").LeftAligned());
AnsiConsole.WriteLine();
for (var i = 0; i < 16; i++)
@@ -70,7 +70,7 @@ namespace ColorExample
AnsiConsole.ResetColors();
AnsiConsole.WriteLine();
AnsiConsole.MarkupLine("[bold underline]8-bit Colors[/]");
AnsiConsole.Render(new Rule("[yellow bold underline]8-bit Colors[/]").RuleStyle("grey").LeftAligned());
AnsiConsole.WriteLine();
for (var i = 0; i < 16; i++)
@@ -97,7 +97,7 @@ namespace ColorExample
AnsiConsole.ResetColors();
AnsiConsole.WriteLine();
AnsiConsole.MarkupLine("[bold underline]24-bit Colors[/]");
AnsiConsole.Render(new Rule("[yellow bold underline]24-bit Colors[/]").RuleStyle("grey").LeftAligned());
AnsiConsole.WriteLine();
var index = 0;

View File

@@ -19,8 +19,8 @@ namespace ColumnsExample
var cards = new List<Panel>();
foreach(var user in users.results)
{
cards.Add(new Panel(GetCard(user))
.SetHeader($"{user.location.country}")
cards.Add(new Panel(GetCardContent(user))
.Header($"{user.location.country}")
.RoundedBorder().Expand());
}
@@ -28,7 +28,7 @@ namespace ColumnsExample
AnsiConsole.Render(new Columns(cards));
}
private static string GetCard(dynamic user)
private static string GetCardContent(dynamic user)
{
var name = $"{user.name.first} {user.name.last}";
var country = $"{user.location.city}";

View File

@@ -15,32 +15,32 @@ namespace Exceptions
catch (Exception ex)
{
AnsiConsole.WriteLine();
AnsiConsole.Render(new Panel("[u]Default[/]").Expand());
AnsiConsole.Render(new Rule("Default").LeftAligned());
AnsiConsole.WriteLine();
AnsiConsole.WriteException(ex);
AnsiConsole.WriteLine();
AnsiConsole.Render(new Panel("[u]Compact[/]").Expand());
AnsiConsole.Render(new Rule("Compact").LeftAligned());
AnsiConsole.WriteLine();
AnsiConsole.WriteException(ex, ExceptionFormats.ShortenEverything | ExceptionFormats.ShowLinks);
AnsiConsole.WriteLine();
AnsiConsole.Render(new Panel("[u]Custom colors[/]").Expand());
AnsiConsole.Render(new Rule("Compact + Custom colors").LeftAligned());
AnsiConsole.WriteLine();
AnsiConsole.WriteException(ex, new ExceptionSettings
{
Format = ExceptionFormats.ShortenEverything | ExceptionFormats.ShowLinks,
Style = new ExceptionStyle
{
Exception = Style.WithForeground(Color.Grey),
Message = Style.WithForeground(Color.White),
NonEmphasized = Style.WithForeground(Color.Cornsilk1),
Parenthesis = Style.WithForeground(Color.Cornsilk1),
Method = Style.WithForeground(Color.Red),
ParameterName = Style.WithForeground(Color.Cornsilk1),
ParameterType = Style.WithForeground(Color.Red),
Path = Style.WithForeground(Color.Red),
LineNumber = Style.WithForeground(Color.Cornsilk1),
Exception = new Style().Foreground(Color.Grey),
Message = new Style().Foreground(Color.White),
NonEmphasized = new Style().Foreground(Color.Cornsilk1),
Parenthesis = new Style().Foreground(Color.Cornsilk1),
Method = new Style().Foreground(Color.Red),
ParameterName = new Style().Foreground(Color.Cornsilk1),
ParameterType = new Style().Foreground(Color.Red),
Path = new Style().Foreground(Color.Red),
LineNumber = new Style().Foreground(Color.Cornsilk1),
}
});
}

View File

@@ -11,13 +11,12 @@ namespace GridExample
AnsiConsole.WriteLine();
var grid = new Grid();
grid.AddColumn(new GridColumn { NoWrap = true });
grid.AddColumn(new GridColumn { NoWrap = true, Width = 2 });
grid.AddColumn();
grid.AddRow("Options:", "", "");
grid.AddRow(" [blue]-h[/], [blue]--help[/]", "", "Show command line help.");
grid.AddRow(" [blue]-c[/], [blue]--configuration[/] <CONFIGURATION>", "", "The configuration to run for.");
grid.AddRow(" [blue]-v[/], [blue]--verbosity[/] <LEVEL>", "", "Set the [grey]MSBuild[/] verbosity level.");
grid.AddColumn(new GridColumn().NoWrap());
grid.AddColumn(new GridColumn().PadLeft(2));
grid.AddRow("Options:");
grid.AddRow(" [blue]-h[/], [blue]--help[/]", "Show command line help.");
grid.AddRow(" [blue]-c[/], [blue]--configuration[/] <CONFIGURATION>", "The configuration to run for.");
grid.AddRow(" [blue]-v[/], [blue]--verbosity[/] <LEVEL>", "Set the [grey]MSBuild[/] verbosity level.");
AnsiConsole.Render(grid);
}

View File

@@ -17,7 +17,7 @@ namespace InfoExample
AnsiConsole.Render(
new Panel(grid)
.SetHeader("Information"));
.Header("Information"));
}
private static string YesNo(bool value)

View File

@@ -13,28 +13,33 @@ namespace PanelExample
AnsiConsole.Render(
new Panel(
new Panel(content)
.SetBorder(BoxBorder.Rounded)));
.Border(BoxBorder.Rounded)));
// Left adjusted panel with text
AnsiConsole.Render(
new Panel(new Text("Left adjusted\nLeft").LeftAligned())
.Expand()
.SquareBorder()
.SetHeader("Left", Style.WithForeground(Color.Red)));
.Header("Left")
.HeaderStyle("red"));
// Centered ASCII panel with text
AnsiConsole.Render(
new Panel(new Text("Centered\nCenter").Centered())
.Expand()
.AsciiBorder()
.SetHeader("Center", Style.WithForeground(Color.Green), Justify.Center));
.Header("Center")
.HeaderStyle("green")
.HeaderAlignment(Justify.Center));
// Right adjusted, rounded panel with text
AnsiConsole.Render(
new Panel(new Text("Right adjusted\nRight").RightAligned())
.Expand()
.RoundedBorder()
.SetHeader("Right", Style.WithForeground(Color.Blue), Justify.Right));
.Header("Right")
.HeaderStyle("blue")
.HeaderAlignment(Justify.Right));
}
}
}

40
examples/Rules/Program.cs Normal file
View File

@@ -0,0 +1,40 @@
using Spectre.Console;
namespace EmojiExample
{
public static class Program
{
public static void Main(string[] args)
{
// No title
WrapInPanel(
new Rule()
.RuleStyle(Style.Parse("yellow"))
.LeftAligned());
// Left aligned title
WrapInPanel(
new Rule("[white]Left aligned[/]")
.RuleStyle(Style.Parse("red"))
.LeftAligned());
// Centered title
WrapInPanel(
new Rule("[silver]Centered[/]")
.RuleStyle(Style.Parse("green"))
.Centered());
// Right aligned title
WrapInPanel(
new Rule("[grey]Right aligned[/]")
.RuleStyle(Style.Parse("blue"))
.RightAligned());
}
private static void WrapInPanel(Rule rule)
{
AnsiConsole.Render(new Panel(rule).Expand().BorderStyle(Style.Parse("grey")));
AnsiConsole.WriteLine();
}
}
}

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Rules</Title>
<Description>Demonstrates how to render horizontal rules (lines).</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,4 +1,3 @@
using System.Diagnostics;
using Spectre.Console;
namespace TableExample
@@ -6,86 +5,47 @@ namespace TableExample
public static class Program
{
public static void Main()
{
// A simple table
RenderSimpleTable();
// A big table
RenderBigTable();
// A nested table
RenderNestedTable();
}
private static void RenderSimpleTable()
{
// Create the table.
var table = new Table();
table.AddColumn(new TableColumn("[u]Foo[/]"));
table.AddColumn(new TableColumn("[u]Bar[/]"));
table.AddColumn(new TableColumn("[u]Baz[/]"));
// Add some rows
table.AddRow("Hello", "[red]World![/]", "");
table.AddRow("[blue]Bonjour[/]", "[white]le[/]", "[red]monde![/]");
table.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", "");
var table = CreateTable();
// Render the table.
AnsiConsole.Render(table);
}
private static void RenderBigTable()
private static Table CreateTable()
{
// Create the table.
var table = new Table().SetBorder(TableBorder.Rounded);
table.AddColumn("[red underline]Foo[/]");
table.AddColumn(new TableColumn("[blue]Bar[/]") { Alignment = Justify.Right, NoWrap = true });
var simple = new Table()
.Border(TableBorder.Square)
.BorderColor(Color.Red)
.AddColumn(new TableColumn("[u]CDE[/]").Footer("EDC").Centered())
.AddColumn(new TableColumn("[u]FED[/]").Footer("DEF"))
.AddColumn(new TableColumn("[u]IHG[/]").Footer("GHI"))
.AddRow("Hello", "[red]World![/]", "")
.AddRow("[blue]Bonjour[/]", "[white]le[/]", "[red]monde![/]")
.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", "");
// Add some rows
table.AddRow("[blue][underline]Hell[/]o[/]", "World");
table.AddRow("[yellow]Patrik [green]\"Hello World\"[/] Svensson[/]", "Was [underline]here[/]!");
table.AddEmptyRow();
table.AddRow(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit,sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " +
"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " +
"dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat " +
"non proident, sunt in culpa qui officia deserunt mollit anim id est laborum", "<- Strange language");
table.AddEmptyRow();
table.AddRow("Hej", "[green]Världen[/]");
var second = new Table()
.Border(TableBorder.Rounded)
.BorderColor(Color.Green)
.AddColumn(new TableColumn("[u]Foo[/]"))
.AddColumn(new TableColumn("[u]Bar[/]"))
.AddColumn(new TableColumn("[u]Baz[/]"))
.AddRow("Hello", "[red]World![/]", "")
.AddRow(simple, new Text("Whaaat"), new Text("Lolz"))
.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", "");
AnsiConsole.Render(table);
}
private static void RenderNestedTable()
{
// Create simple table.
var simple = new Table().SetBorder(TableBorder.Rounded).SetBorderColor(Color.Red);
simple.AddColumn(new TableColumn("[u]Foo[/]").Centered());
simple.AddColumn(new TableColumn("[u]Bar[/]"));
simple.AddColumn(new TableColumn("[u]Baz[/]"));
simple.AddRow("Hello", "[red]World![/]", "");
simple.AddRow("[blue]Bonjour[/]", "[white]le[/]", "[red]monde![/]");
simple.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", "");
// Create other table.
var second = new Table().SetBorder(TableBorder.Square).SetBorderColor(Color.Green);
second.AddColumn(new TableColumn("[u]Foo[/]"));
second.AddColumn(new TableColumn("[u]Bar[/]"));
second.AddColumn(new TableColumn("[u]Baz[/]"));
second.AddRow("Hello", "[red]World![/]", "");
second.AddRow(simple, new Text("Whaaat"), new Text("Lolz"));
second.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", "");
var table = new Table().SetBorder(TableBorder.Rounded);
table.AddColumn(new TableColumn(new Panel("[u]Foo[/]").SetBorderColor(Color.Red)));
table.AddColumn(new TableColumn(new Panel("[u]Bar[/]").SetBorderColor(Color.Green)));
table.AddColumn(new TableColumn(new Panel("[u]Baz[/]").SetBorderColor(Color.Blue)));
// Add some rows
table.AddRow(new Text("Hello").Centered(), new Markup("[red]World![/]"), Text.Empty);
table.AddRow(second, new Text("Whaaat"), new Text("Lol"));
table.AddRow(new Markup("[blue]Hej[/]").Centered(), new Markup("[yellow]Världen![/]"), Text.Empty);
AnsiConsole.Render(table);
return new Table()
.Centered()
.Border(TableBorder.DoubleEdge)
.Title("TABLE [yellow]TITLE[/]")
.Caption("TABLE [yellow]CAPTION[/]")
.AddColumn(new TableColumn(new Panel("[u]ABC[/]").BorderColor(Color.Red)).Footer("[u]FOOTER 1[/]"))
.AddColumn(new TableColumn(new Panel("[u]DEF[/]").BorderColor(Color.Green)).Footer("[u]FOOTER 2[/]"))
.AddColumn(new TableColumn(new Panel("[u]GHI[/]").BorderColor(Color.Blue)).Footer("[u]FOOTER 3[/]"))
.AddRow(new Text("Hello").Centered(), new Markup("[red]World![/]"), Text.Empty)
.AddRow(second, new Text("Whaaat"), new Text("Lol"))
.AddRow(new Markup("[blue]Hej[/]").Centered(), new Markup("[yellow]Världen![/]"), Text.Empty);
}
}
}

View File

@@ -6,10 +6,10 @@ namespace Spectre.Console.Tests
{
if (foreground)
{
return style.WithForeground(color);
return style.Foreground(color);
}
return style.WithBackground(color);
return style.Background(color);
}
}
}

View File

@@ -21,7 +21,7 @@ namespace Spectre.Console.Tests.Unit
var console = new TestableAnsiConsole(ColorSystem.TrueColor);
// When
console.Write("Hello World", Style.WithDecoration(decoration));
console.Write("Hello World", new Style().Decoration(decoration));
// Then
console.Output.ShouldBe(expected);
@@ -36,7 +36,7 @@ namespace Spectre.Console.Tests.Unit
var console = new TestableAnsiConsole(ColorSystem.TrueColor);
// When
console.Write("Hello World", Style.WithDecoration(decoration));
console.Write("Hello World", new Style().Decoration(decoration));
// Then
console.Output.ShouldBe(expected);

View File

@@ -15,9 +15,10 @@ namespace Spectre.Console.Tests.Unit
// When
console.Write(
"Hello",
Style.WithForeground(Color.RoyalBlue1)
.WithBackground(Color.NavajoWhite1)
.WithDecoration(Decoration.Italic));
new Style()
.Foreground(Color.RoyalBlue1)
.Background(Color.NavajoWhite1)
.Decoration(Decoration.Italic));
// Then
console.Output.ShouldBe("\u001b[3;90;47mHello\u001b[0m");
@@ -32,9 +33,10 @@ namespace Spectre.Console.Tests.Unit
// When
console.Write(
"Hello",
Style.WithForeground(Color.Default)
.WithBackground(Color.NavajoWhite1)
.WithDecoration(Decoration.Italic));
new Style()
.Foreground(Color.Default)
.Background(Color.NavajoWhite1)
.Decoration(Decoration.Italic));
// Then
console.Output.ShouldBe("\u001b[3;47mHello\u001b[0m");
@@ -49,9 +51,10 @@ namespace Spectre.Console.Tests.Unit
// When
console.Write(
"Hello",
Style.WithForeground(Color.RoyalBlue1)
.WithBackground(Color.Default)
.WithDecoration(Decoration.Italic));
new Style()
.Foreground(Color.RoyalBlue1)
.Background(Color.Default)
.Decoration(Decoration.Italic));
// Then
console.Output.ShouldBe("\u001b[3;90mHello\u001b[0m");
@@ -66,9 +69,10 @@ namespace Spectre.Console.Tests.Unit
// When
console.Write(
"Hello",
Style.WithForeground(Color.RoyalBlue1)
.WithBackground(Color.NavajoWhite1)
.WithDecoration(Decoration.None));
new Style()
.Foreground(Color.RoyalBlue1)
.Background(Color.NavajoWhite1)
.Decoration(Decoration.None));
// Then
console.Output.ShouldBe("\u001b[90;47mHello\u001b[0m");
@@ -83,8 +87,8 @@ namespace Spectre.Console.Tests.Unit
var console = new TestableAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes);
// When
console.WriteLine("Hello", Style.WithBackground(ConsoleColor.Red));
console.WriteLine("World", Style.WithBackground(ConsoleColor.Green));
console.WriteLine("Hello", new Style().Background(ConsoleColor.Red));
console.WriteLine("World", new Style().Background(ConsoleColor.Green));
// Then
console.Output.NormalizeLineEndings()
@@ -98,7 +102,7 @@ namespace Spectre.Console.Tests.Unit
var console = new TestableAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes);
// When
console.WriteLine("Hello\nWorld", Style.WithBackground(ConsoleColor.Red));
console.WriteLine("Hello\nWorld", new Style().Background(ConsoleColor.Red));
// Then
console.Output.NormalizeLineEndings()

View File

@@ -14,7 +14,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.None.GetSafeBorder(safe: true);
var border = BoxExtensions.GetSafeBorder(BoxBorder.None, safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.None);
@@ -47,7 +47,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.Ascii.GetSafeBorder(safe: true);
var border = BoxExtensions.GetSafeBorder(BoxBorder.Ascii, safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Ascii);
@@ -80,7 +80,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.Double.GetSafeBorder(safe: true);
var border = BoxExtensions.GetSafeBorder(BoxBorder.Double, safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Double);
@@ -113,7 +113,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.Heavy.GetSafeBorder(safe: true);
var border = BoxExtensions.GetSafeBorder(BoxBorder.Heavy, safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Square);
@@ -144,7 +144,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.Rounded.GetSafeBorder(safe: true);
var border = BoxExtensions.GetSafeBorder(BoxBorder.Rounded, safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Square);
@@ -174,7 +174,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.Square.GetSafeBorder(safe: true);
var border = BoxExtensions.GetSafeBorder(BoxBorder.Square, safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Square);
@@ -203,7 +203,7 @@ namespace Spectre.Console.Tests.Unit
public static Panel GetPanel()
{
return new Panel("Hello World")
.SetHeader("Greeting");
.Header("Greeting");
}
}
}

View File

@@ -20,17 +20,105 @@ namespace Spectre.Console.Tests.Unit
console.Render(calendar);
// Then
console.Lines.Count.ShouldBe(10);
console.Lines[0].ShouldBe("┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐");
console.Lines[1].ShouldBe("│ Sun │ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │");
console.Lines[2].ShouldBe("├─────┼─────┼─────┼─────┼─────┼─────┼─────┤");
console.Lines[3].ShouldBe("│ │ │ │ │ 1 │ 2 │ 3* │");
console.Lines[4].ShouldBe("│ 45678910 │");
console.Lines[5].ShouldBe("│ 1112*13141516 │ 17 │");
console.Lines[6].ShouldBe("│ 18 │ 19 2021 │ 222324 │");
console.Lines[7].ShouldBe("│ 2526 │ 27 │ 28 │ 29 │ 3031 │");
console.Lines[8].ShouldBe("│ │");
console.Lines[9].ShouldBe("└─────┴─────┴─────┴─────┴─────┴─────┴─────┘");
console.Lines.Count.ShouldBe(11);
console.Lines[00].ShouldBe(" 2020 October ");
console.Lines[01].ShouldBe("┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐");
console.Lines[02].ShouldBe("│ Sun │ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │");
console.Lines[03].ShouldBe("├─────┼─────┼─────┼─────┼─────┼─────┼─────┤");
console.Lines[04].ShouldBe("│ 123* │");
console.Lines[05].ShouldBe("│ 4 5 6 7 8 9 │ 10 │");
console.Lines[06].ShouldBe("│ 11 │ 12*13 │ 14151617 │");
console.Lines[07].ShouldBe("│ 1819 │ 20 │ 21 │ 2223 │ 24 │");
console.Lines[08].ShouldBe("│ 25262728293031 │");
console.Lines[09].ShouldBe("│ │ │ │ │ │ │ │");
console.Lines[10].ShouldBe("└─────┴─────┴─────┴─────┴─────┴─────┴─────┘");
}
[Fact]
public void Should_Center_Calendar_Correctly()
{
// Given
var console = new PlainConsole(width: 80);
var calendar = new Calendar(2020, 10)
.Centered()
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
// When
console.Render(calendar);
// Then
console.Lines.Count.ShouldBe(11);
console.Lines[00].ShouldBe(" 2020 October ");
console.Lines[01].ShouldBe(" ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐ ");
console.Lines[02].ShouldBe(" │ Sun │ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ ");
console.Lines[03].ShouldBe(" ├─────┼─────┼─────┼─────┼─────┼─────┼─────┤ ");
console.Lines[04].ShouldBe(" │ │ │ │ │ 1 │ 2 │ 3* │ ");
console.Lines[05].ShouldBe(" │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ ");
console.Lines[06].ShouldBe(" │ 11 │ 12* │ 13 │ 14 │ 15 │ 16 │ 17 │ ");
console.Lines[07].ShouldBe(" │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ ");
console.Lines[08].ShouldBe(" │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │ ");
console.Lines[09].ShouldBe(" │ │ │ │ │ │ │ │ ");
console.Lines[10].ShouldBe(" └─────┴─────┴─────┴─────┴─────┴─────┴─────┘ ");
}
[Fact]
public void Should_Left_Align_Calendar_Correctly()
{
// Given
var console = new PlainConsole(width: 80);
var calendar = new Calendar(2020, 10)
.LeftAligned()
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
// When
console.Render(calendar);
// Then
console.Lines.Count.ShouldBe(11);
console.Lines[00].ShouldBe(" 2020 October ");
console.Lines[01].ShouldBe("┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐");
console.Lines[02].ShouldBe("│ Sun │ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │");
console.Lines[03].ShouldBe("├─────┼─────┼─────┼─────┼─────┼─────┼─────┤");
console.Lines[04].ShouldBe("│ │ │ │ │ 1 │ 2 │ 3* │");
console.Lines[05].ShouldBe("│ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │");
console.Lines[06].ShouldBe("│ 11 │ 12* │ 13 │ 14 │ 15 │ 16 │ 17 │");
console.Lines[07].ShouldBe("│ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │");
console.Lines[08].ShouldBe("│ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │");
console.Lines[09].ShouldBe("│ │ │ │ │ │ │ │");
console.Lines[10].ShouldBe("└─────┴─────┴─────┴─────┴─────┴─────┴─────┘");
}
[Fact]
public void Should_Right_Align_Calendar_Correctly()
{
// Given
var console = new PlainConsole(width: 80);
var calendar = new Calendar(2020, 10)
.RightAligned()
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
// When
console.Render(calendar);
// Then
console.Lines.Count.ShouldBe(11);
console.Lines[00].ShouldBe(" 2020 October ");
console.Lines[01].ShouldBe(" ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐");
console.Lines[02].ShouldBe(" │ Sun │ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │");
console.Lines[03].ShouldBe(" ├─────┼─────┼─────┼─────┼─────┼─────┼─────┤");
console.Lines[04].ShouldBe(" │ │ │ │ │ 1 │ 2 │ 3* │");
console.Lines[05].ShouldBe(" │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │");
console.Lines[06].ShouldBe(" │ 11 │ 12* │ 13 │ 14 │ 15 │ 16 │ 17 │");
console.Lines[07].ShouldBe(" │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │");
console.Lines[08].ShouldBe(" │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │");
console.Lines[09].ShouldBe(" │ │ │ │ │ │ │ │");
console.Lines[10].ShouldBe(" └─────┴─────┴─────┴─────┴─────┴─────┴─────┘");
}
[Fact]
@@ -39,7 +127,7 @@ namespace Spectre.Console.Tests.Unit
// Given
var console = new PlainConsole(width: 80);
var calendar = new Calendar(2020, 10, 15)
.SetCulture("de-DE")
.Culture("de-DE")
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
@@ -48,45 +136,18 @@ namespace Spectre.Console.Tests.Unit
console.Render(calendar);
// Then
console.Lines.Count.ShouldBe(10);
console.Lines[0].ShouldBe("┌─────┬────┬────┬────┬────┬────┬────┐");
console.Lines[1].ShouldBe("│ Mo │ Di │ Mi │ Do │ Fr │ Sa │ So │");
console.Lines[2].ShouldBe("├─────┼────┼────┼────┼────┼────┼────┤");
console.Lines[3].ShouldBe("│ │ │ │ 1 │ 2 │ 3* │ 4 │");
console.Lines[4].ShouldBe("│ 567891011 │");
console.Lines[5].ShouldBe("│ 12*13141516 │ 17 │ 18 │");
console.Lines[6].ShouldBe("│ 19 2021 │ 22232425 │");
console.Lines[7].ShouldBe("│ 26 │ 27 │ 28 │ 29 │ 3031 │");
console.Lines[8].ShouldBe("│ │ │");
console.Lines[9].ShouldBe("└─────┴────┴────┴────┴────┴────┴────┘");
}
[Fact]
public void Should_Render_List_Of_Events_If_Enabled()
{
// Given
var console = new PlainConsole(width: 80);
var calendar = new Calendar(2020, 10, 15)
.SetCulture("de-DE")
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
// When
console.Render(calendar);
// Then
console.Lines.Count.ShouldBe(10);
console.Lines[0].ShouldBe("┌─────┬────┬────┬────┬────┬────┬────┐");
console.Lines[1].ShouldBe("│ Mo │ Di │ Mi │ Do │ Fr │ Sa │ So │");
console.Lines[2].ShouldBe("├─────┼────┼────┼────┼────┼────┼────┤");
console.Lines[3].ShouldBe("│ │ │ │ 1 │ 2 │ 3* │ 4 │");
console.Lines[4].ShouldBe("│ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │");
console.Lines[5].ShouldBe("│ 12* │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │");
console.Lines[6].ShouldBe("│ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │");
console.Lines[7].ShouldBe("│ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │ │");
console.Lines[8].ShouldBe("│ │ │ │ │ │ │ │");
console.Lines[9].ShouldBe("└─────┴────┴────┴────┴────┴────┴────┘");
console.Lines.Count.ShouldBe(11);
console.Lines[00].ShouldBe(" Oktober 2020 ");
console.Lines[01].ShouldBe("┌─────┬────┬────┬────┬────┬────┬────┐");
console.Lines[02].ShouldBe("│ Mo │ Di │ Mi │ Do │ Fr │ Sa │ So │");
console.Lines[03].ShouldBe("├─────┼────┼────┼────┼────┼────┼────┤");
console.Lines[04].ShouldBe("│ 123*4 │");
console.Lines[05].ShouldBe("│ 5 6 7 8 9 │ 10 │ 11 │");
console.Lines[06].ShouldBe("│ 12*13 │ 1415161718 │");
console.Lines[07].ShouldBe("│ 19 │ 20 │ 21 │ 2223 │ 2425 │");
console.Lines[08].ShouldBe("│ 262728293031 │ │");
console.Lines[09].ShouldBe("│ │ │ │ │ │ │ │");
console.Lines[10].ShouldBe("└─────┴────┴────┴────┴────┴────┴────┘");
}
}
}

View File

@@ -6,6 +6,24 @@ namespace Spectre.Console.Tests.Unit
{
public sealed class MarkupTests
{
public sealed class TheEscapeMethod
{
[Theory]
[InlineData("Hello World", "Hello World")]
[InlineData("Hello World [", "Hello World [[")]
[InlineData("Hello World ]", "Hello World ]]")]
[InlineData("Hello [World]", "Hello [[World]]")]
[InlineData("Hello [[World]]", "Hello [[[[World]]]]")]
public void Should_Escape_Markup_As_Expected(string input, string expected)
{
// Given, When
var result = Markup.Escape(input);
// Then
result.ShouldBe(expected);
}
}
[Theory]
[InlineData("Hello [[ World ]")]
[InlineData("Hello [[ World ] !")]

View File

@@ -17,7 +17,7 @@ namespace Spectre.Console.Tests.Unit
table.AddRow("Corgi", "Waldo");
// When
console.Render(new Padder(table).SetPadding(1, 2, 3, 4));
console.Render(new Padder(table).Padding(1, 2, 3, 4));
// Then
console.Lines.Count.ShouldBe(12);
@@ -48,7 +48,7 @@ namespace Spectre.Console.Tests.Unit
// When
console.Render(new Padder(table)
.SetPadding(1, 2, 3, 4)
.Padding(1, 2, 3, 4)
.Expand());
// Then
@@ -77,11 +77,11 @@ namespace Spectre.Console.Tests.Unit
table.AddColumn("Bar", c => c.PadLeft(0).PadRight(0));
table.AddRow("Baz", "Qux");
table.AddRow(new Text("Corgi"), new Padder(new Panel("Waldo"))
.SetPadding(2, 1, 2, 1));
.Padding(2, 1, 2, 1));
// When
console.Render(new Padder(table)
.SetPadding(1, 2, 3, 4)
.Padding(1, 2, 3, 4)
.Expand());
// Then

View File

@@ -244,7 +244,7 @@ namespace Spectre.Console.Tests.Unit
}
[Fact]
public void Should_Justify_Child_To_Right()
public void Should_Justify_Child_To_Right_Correctly()
{
// Given
var console = new PlainConsole(width: 25);
@@ -264,7 +264,7 @@ namespace Spectre.Console.Tests.Unit
}
[Fact]
public void Should_Justify_Child_To_Center()
public void Should_Center_Child_Correctly()
{
// Given
var console = new PlainConsole(width: 25);
@@ -315,8 +315,8 @@ namespace Spectre.Console.Tests.Unit
var panel = new Panel(grid)
.Expand().RoundedBorder()
.SetBorderStyle(Style.WithForeground(Color.Grey))
.SetHeader("Short paths ", Style.WithForeground(Color.Grey));
.BorderStyle(new Style().Foreground(Color.Grey))
.Header("Short paths ", new Style().Foreground(Color.Grey));
// When
console.Render(panel);

View File

@@ -43,7 +43,7 @@ namespace Spectre.Console.Tests.Unit
.AddColumns("[red on black]Foo[/]", "[green bold]Bar[/]", "[blue italic]Qux[/]")
.AddRow("[invert underline]Corgi[/]", "[bold strikethrough]Waldo[/]", "[dim]Zap[/]")
.AddRow(new Panel("[blue]Hello World[/]")
.SetBorderColor(Color.Red).RoundedBorder()));
.BorderColor(Color.Red).RoundedBorder()));
// When
var html = recorder.ExportHtml();

View File

@@ -0,0 +1,125 @@
using Shouldly;
using Xunit;
namespace Spectre.Console.Tests.Unit
{
public sealed class RuleTests
{
[Fact]
public void Should_Render_Default_Rule_Without_Title()
{
// Given
var console = new PlainConsole(width: 40);
// When
console.Render(new Rule());
// Then
console.Lines.Count.ShouldBe(1);
console.Lines[0].ShouldBe("────────────────────────────────────────");
}
[Fact]
public void Should_Render_Default_Rule_With_Title_Centered_By_Default()
{
// Given
var console = new PlainConsole(width: 40);
// When
console.Render(new Rule("Hello World"));
// Then
console.Lines.Count.ShouldBe(1);
console.Lines[0].ShouldBe("───────────── Hello World ──────────────");
}
[Fact]
public void Should_Render_Default_Rule_With_Title_Left_Aligned()
{
// Given
var console = new PlainConsole(width: 40);
// When
console.Render(new Rule("Hello World")
{
Alignment = Justify.Left,
});
// Then
console.Lines.Count.ShouldBe(1);
console.Lines[0].ShouldBe("── Hello World ─────────────────────────");
}
[Fact]
public void Should_Render_Default_Rule_With_Title_Right_Aligned()
{
// Given
var console = new PlainConsole(width: 40);
// When
console.Render(new Rule("Hello World")
{
Alignment = Justify.Right,
});
// Then
console.Lines.Count.ShouldBe(1);
console.Lines[0].ShouldBe("───────────────────────── Hello World ──");
}
[Fact]
public void Should_Convert_Line_Breaks_In_Title_To_Spaces()
{
// Given
var console = new PlainConsole(width: 40);
// When
console.Render(new Rule("Hello\nWorld\r\n!"));
// Then
console.Lines.Count.ShouldBe(1);
console.Lines[0].ShouldBe("──────────── Hello World ! ─────────────");
}
[Fact]
public void Should_Truncate_Title()
{
// Given
var console = new PlainConsole(width: 40);
// When
console.Render(new Rule(" Hello World "));
// Then
console.Lines.Count.ShouldBe(1);
console.Lines[0].ShouldBe("───────────── Hello World ──────────────");
}
[Theory]
[InlineData(0, "Hello World Hello World Hello World Hello World Hello World", "")]
[InlineData(1, "Hello World Hello World Hello World Hello World Hello World", "─")]
[InlineData(2, "Hello World Hello World Hello World Hello World Hello World", "──")]
[InlineData(3, "Hello World Hello World Hello World Hello World Hello World", "───")]
[InlineData(4, "Hello World Hello World Hello World Hello World Hello World", "────")]
[InlineData(5, "Hello World Hello World Hello World Hello World Hello World", "─────")]
[InlineData(6, "Hello World Hello World Hello World Hello World Hello World", "──────")]
[InlineData(7, "Hello World Hello World Hello World Hello World Hello World", "───────")]
[InlineData(8, "Hello World Hello World Hello World Hello World Hello World", "── H… ──")]
[InlineData(8, "A", "── A ───")]
[InlineData(8, "AB", "── AB ──")]
[InlineData(8, "ABC", "── A… ──")]
[InlineData(40, "Hello World Hello World Hello World Hello World Hello World", "──── Hello World Hello World Hello… ────")]
public void Should_Truncate_Too_Long_Title(int width, string input, string expected)
{
// Given
var console = new PlainConsole(width);
// When
console.Render(new Rule(input));
// Then
console.Lines.Count.ShouldBe(1);
console.Lines[0].ShouldBe(expected);
}
}
}

View File

@@ -1,3 +1,4 @@
using System.Text;
using Shouldly;
using Spectre.Console.Rendering;
using Xunit;
@@ -6,6 +7,16 @@ namespace Spectre.Console.Tests.Unit
{
public sealed class SegmentTests
{
[Fact]
public void Lol()
{
var context = new RenderContext(Encoding.UTF8, false);
var result = new Segment(" ").CellCount(context);
result.ShouldBe(4);
}
public sealed class TheSplitMethod
{
[Fact]
@@ -31,7 +42,10 @@ namespace Spectre.Console.Tests.Unit
[Fact]
public void Should_Split_Segment()
{
var context = new RenderContext(Encoding.UTF8, false);
var lines = Segment.SplitLines(
context,
new[]
{
new Segment("Foo"),
@@ -61,7 +75,9 @@ namespace Spectre.Console.Tests.Unit
[Fact]
public void Should_Split_Segments_With_Linebreak_In_Text()
{
var context = new RenderContext(Encoding.UTF8, false);
var lines = Segment.SplitLines(
context,
new[]
{
new Segment("Foo\n"),

View File

@@ -42,10 +42,11 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(3);
console.Lines.Count.ShouldBe(4);
console.Lines[0].ShouldBe("Header 1 Header 2");
console.Lines[1].ShouldBe("Cell Cell ");
console.Lines[2].ShouldBe("Cell Cell ");
console.Lines[3].ShouldBe("Footer 1 Footer 2");
}
}
@@ -85,13 +86,15 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe("+---------------------+");
console.Lines[1].ShouldBe("| Header 1 | Header 2 |");
console.Lines[2].ShouldBe("|----------+----------|");
console.Lines[3].ShouldBe("| Cell | Cell |");
console.Lines[4].ShouldBe("| Cell | Cell |");
console.Lines[5].ShouldBe("+---------------------+");
console.Lines[5].ShouldBe("|----------+----------|");
console.Lines[6].ShouldBe("| Footer 1 | Footer 2 |");
console.Lines[7].ShouldBe("+---------------------+");
}
}
@@ -131,13 +134,15 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe("+----------+----------+");
console.Lines[1].ShouldBe("| Header 1 | Header 2 |");
console.Lines[2].ShouldBe("|----------+----------|");
console.Lines[3].ShouldBe("| Cell | Cell |");
console.Lines[4].ShouldBe("| Cell | Cell |");
console.Lines[5].ShouldBe("+----------+----------+");
console.Lines[5].ShouldBe("|----------+----------|");
console.Lines[6].ShouldBe("| Footer 1 | Footer 2 |");
console.Lines[7].ShouldBe("+----------+----------+");
}
}
@@ -177,13 +182,15 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe("+----------+----------+");
console.Lines[1].ShouldBe("| Header 1 | Header 2 |");
console.Lines[2].ShouldBe("|==========+==========|");
console.Lines[3].ShouldBe("| Cell | Cell |");
console.Lines[4].ShouldBe("| Cell | Cell |");
console.Lines[5].ShouldBe("+----------+----------+");
console.Lines[6].ShouldBe("| Footer 1 | Footer 2 |");
console.Lines[7].ShouldBe("+----------+----------+");
}
}
@@ -223,13 +230,15 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe("┌──────────┬──────────┐");
console.Lines[1].ShouldBe("│ Header 1 │ Header 2 │");
console.Lines[2].ShouldBe("├──────────┼──────────┤");
console.Lines[3].ShouldBe("│ Cell │ Cell │");
console.Lines[4].ShouldBe("│ Cell │ Cell │");
console.Lines[5].ShouldBe("────────────────────");
console.Lines[5].ShouldBe("────────────────────");
console.Lines[6].ShouldBe("│ Footer 1 │ Footer 2 │");
console.Lines[7].ShouldBe("└──────────┴──────────┘");
}
}
@@ -269,13 +278,15 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe("╭──────────┬──────────╮");
console.Lines[1].ShouldBe("│ Header 1 │ Header 2 │");
console.Lines[2].ShouldBe("├──────────┼──────────┤");
console.Lines[3].ShouldBe("│ Cell │ Cell │");
console.Lines[4].ShouldBe("│ Cell │ Cell │");
console.Lines[5].ShouldBe("────────────────────");
console.Lines[5].ShouldBe("────────────────────");
console.Lines[6].ShouldBe("│ Footer 1 │ Footer 2 │");
console.Lines[7].ShouldBe("╰──────────┴──────────╯");
}
}
@@ -315,13 +326,15 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe(" Header 1 │ Header 2 ");
console.Lines[2].ShouldBe(" ──────────┼────────── ");
console.Lines[3].ShouldBe(" Cell │ Cell ");
console.Lines[4].ShouldBe(" Cell │ Cell ");
console.Lines[5].ShouldBe(" ");
console.Lines[5].ShouldBe(" ──────────┼────────── ");
console.Lines[6].ShouldBe(" Footer 1 │ Footer 2 ");
console.Lines[7].ShouldBe(" ");
}
}
@@ -361,13 +374,15 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe(" Header 1 │ Header 2 ");
console.Lines[2].ShouldBe(" ━━━━━━━━━━┿━━━━━━━━━━ ");
console.Lines[3].ShouldBe(" Cell │ Cell ");
console.Lines[4].ShouldBe(" Cell │ Cell ");
console.Lines[5].ShouldBe(" ");
console.Lines[5].ShouldBe(" ━━━━━━━━━━┿━━━━━━━━━━ ");
console.Lines[6].ShouldBe(" Footer 1 │ Footer 2 ");
console.Lines[7].ShouldBe(" ");
}
}
@@ -407,13 +422,15 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe(" Header 1 │ Header 2 ");
console.Lines[2].ShouldBe(" ══════════╪══════════ ");
console.Lines[3].ShouldBe(" Cell │ Cell ");
console.Lines[4].ShouldBe(" Cell │ Cell ");
console.Lines[5].ShouldBe(" ");
console.Lines[5].ShouldBe(" ══════════╪══════════ ");
console.Lines[6].ShouldBe(" Footer 1 │ Footer 2 ");
console.Lines[7].ShouldBe(" ");
}
}
@@ -453,13 +470,15 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe(" Header 1 Header 2 ");
console.Lines[2].ShouldBe("───────────────────────");
console.Lines[3].ShouldBe(" Cell Cell ");
console.Lines[4].ShouldBe(" Cell Cell ");
console.Lines[5].ShouldBe(" ");
console.Lines[5].ShouldBe("───────────────────────");
console.Lines[6].ShouldBe(" Footer 1 Footer 2 ");
console.Lines[7].ShouldBe(" ");
}
}
@@ -499,13 +518,15 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe("───────────────────────");
console.Lines[1].ShouldBe(" Header 1 Header 2 ");
console.Lines[2].ShouldBe("───────────────────────");
console.Lines[3].ShouldBe(" Cell Cell ");
console.Lines[4].ShouldBe(" Cell Cell ");
console.Lines[5].ShouldBe("───────────────────────");
console.Lines[6].ShouldBe(" Footer 1 Footer 2 ");
console.Lines[7].ShouldBe("───────────────────────");
}
}
@@ -545,13 +566,15 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe(" Header 1 Header 2 ");
console.Lines[2].ShouldBe("━━━━━━━━━━━━━━━━━━━━━━━");
console.Lines[3].ShouldBe(" Cell Cell ");
console.Lines[4].ShouldBe(" Cell Cell ");
console.Lines[5].ShouldBe(" ");
console.Lines[5].ShouldBe("━━━━━━━━━━━━━━━━━━━━━━━");
console.Lines[6].ShouldBe(" Footer 1 Footer 2 ");
console.Lines[7].ShouldBe(" ");
}
}
@@ -591,13 +614,15 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe("┏━━━━━━━━━━┳━━━━━━━━━━┓");
console.Lines[1].ShouldBe("┃ Header 1 ┃ Header 2 ┃");
console.Lines[2].ShouldBe("┣━━━━━━━━━━╋━━━━━━━━━━┫");
console.Lines[3].ShouldBe("┃ Cell ┃ Cell ┃");
console.Lines[4].ShouldBe("┃ Cell ┃ Cell ┃");
console.Lines[5].ShouldBe("━━━━━━━━━━━━━━━━━━━━");
console.Lines[5].ShouldBe("━━━━━━━━━━━━━━━━━━━━");
console.Lines[6].ShouldBe("┃ Footer 1 ┃ Footer 2 ┃");
console.Lines[7].ShouldBe("┗━━━━━━━━━━┻━━━━━━━━━━┛");
}
}
@@ -637,13 +662,15 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe("┏━━━━━━━━━━┯━━━━━━━━━━┓");
console.Lines[1].ShouldBe("┃ Header 1 │ Header 2 ┃");
console.Lines[2].ShouldBe("┠──────────┼──────────┨");
console.Lines[3].ShouldBe("┃ Cell │ Cell ┃");
console.Lines[4].ShouldBe("┃ Cell │ Cell ┃");
console.Lines[5].ShouldBe("┗━━━━━━━━━━┷━━━━━━━━━━┛");
console.Lines[5].ShouldBe("┠──────────┼──────────┨");
console.Lines[6].ShouldBe("┃ Footer 1 │ Footer 2 ┃");
console.Lines[7].ShouldBe("┗━━━━━━━━━━┷━━━━━━━━━━┛");
}
}
@@ -683,13 +710,15 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe("┏━━━━━━━━━━┳━━━━━━━━━━┓");
console.Lines[1].ShouldBe("┃ Header 1 ┃ Header 2 ┃");
console.Lines[2].ShouldBe("┡━━━━━━━━━━╇━━━━━━━━━━┩");
console.Lines[3].ShouldBe("│ Cell │ Cell │");
console.Lines[4].ShouldBe("│ Cell │ Cell │");
console.Lines[5].ShouldBe("────────────────────");
console.Lines[5].ShouldBe("────────────────────");
console.Lines[6].ShouldBe("│ Footer 1 │ Footer 2 │");
console.Lines[7].ShouldBe("└──────────┴──────────┘");
}
}
@@ -729,13 +758,15 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe("╔══════════╦══════════╗");
console.Lines[1].ShouldBe("║ Header 1 ║ Header 2 ║");
console.Lines[2].ShouldBe("╠══════════╬══════════╣");
console.Lines[3].ShouldBe("║ Cell ║ Cell ║");
console.Lines[4].ShouldBe("║ Cell ║ Cell ║");
console.Lines[5].ShouldBe("════════════════════");
console.Lines[5].ShouldBe("════════════════════");
console.Lines[6].ShouldBe("║ Footer 1 ║ Footer 2 ║");
console.Lines[7].ShouldBe("╚══════════╩══════════╝");
}
}
@@ -775,13 +806,15 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe("╔══════════╤══════════╗");
console.Lines[1].ShouldBe("║ Header 1 │ Header 2 ║");
console.Lines[2].ShouldBe("╟──────────┼──────────╢");
console.Lines[3].ShouldBe("║ Cell │ Cell ║");
console.Lines[4].ShouldBe("║ Cell │ Cell ║");
console.Lines[5].ShouldBe("╚══════════╧══════════╝");
console.Lines[5].ShouldBe("╟──────────┼──────────╢");
console.Lines[6].ShouldBe("║ Footer 1 │ Footer 2 ║");
console.Lines[7].ShouldBe("╚══════════╧══════════╝");
}
}
@@ -821,13 +854,14 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(7);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe("| Header 1 | Header 2 |");
console.Lines[2].ShouldBe("| -------- | -------- |");
console.Lines[3].ShouldBe("| Cell | Cell |");
console.Lines[4].ShouldBe("| Cell | Cell |");
console.Lines[5].ShouldBe(" ");
console.Lines[5].ShouldBe("| Footer 1 | Footer 2 |");
console.Lines[6].ShouldBe(" ");
}
[Fact]
@@ -841,13 +875,14 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(7);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe("| Header 1 | Header 2 |");
console.Lines[2].ShouldBe("| -------- | :------- |");
console.Lines[3].ShouldBe("| Cell | Cell |");
console.Lines[4].ShouldBe("| Cell | Cell |");
console.Lines[5].ShouldBe(" ");
console.Lines[5].ShouldBe("| Footer 1 | Footer 2 |");
console.Lines[6].ShouldBe(" ");
}
[Fact]
@@ -861,13 +896,14 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(7);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe("| Header 1 | Header 2 |");
console.Lines[2].ShouldBe("| -------- | :------: |");
console.Lines[3].ShouldBe("| Cell | Cell |");
console.Lines[4].ShouldBe("| Cell | Cell |");
console.Lines[5].ShouldBe(" ");
console.Lines[5].ShouldBe("| Footer 1 | Footer 2 |");
console.Lines[6].ShouldBe(" ");
}
[Fact]
@@ -881,13 +917,14 @@ namespace Spectre.Console.Tests.Unit
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines.Count.ShouldBe(7);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe("| Header 1 | Header 2 |");
console.Lines[2].ShouldBe("| -------- | -------: |");
console.Lines[3].ShouldBe("| Cell | Cell |");
console.Lines[4].ShouldBe("| Cell | Cell |");
console.Lines[5].ShouldBe(" ");
console.Lines[5].ShouldBe("| Footer 1 | Footer 2 |");
console.Lines[6].ShouldBe(" ");
}
}
@@ -896,8 +933,8 @@ namespace Spectre.Console.Tests.Unit
public static Table GetTable(Justify? header1 = null, Justify? header2 = null)
{
var table = new Table();
table.AddColumn("Header 1", c => c.Alignment = header1);
table.AddColumn("Header 2", c => c.Alignment = header2);
table.AddColumn("Header 1", c => c.Alignment(header1).Footer("Footer 1"));
table.AddColumn("Header 2", c => c.Alignment(header2).Footer("Footer 2"));
table.AddRow("Cell", "Cell");
table.AddRow("Cell", "Cell");
return table;

View File

@@ -168,6 +168,105 @@ namespace Spectre.Console.Tests.Unit
console.Lines[5].ShouldBe("└────────┴────────┴───────┘");
}
[Fact]
public void Should_Render_Table_With_Footers_Correctly()
{
// Given
var console = new PlainConsole(width: 80);
var table = new Table();
table.AddColumn(new TableColumn("Foo").Footer("Oof").RightAligned());
table.AddColumn("Bar");
table.AddColumns(new TableColumn("Baz").Footer("Zab"));
table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred");
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(8);
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 │ Fred │");
console.Lines[5].ShouldBe("├────────┼────────┼───────┤");
console.Lines[6].ShouldBe("│ Oof │ │ Zab │");
console.Lines[7].ShouldBe("└────────┴────────┴───────┘");
}
[Fact]
public void Should_Left_Align_Table_Correctly()
{
// Given
var console = new PlainConsole(width: 80);
var table = new Table();
table.Alignment = Justify.Left;
table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred");
// 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 │ Fred │");
console.Lines[5].ShouldBe("└────────┴────────┴───────┘");
}
[Fact]
public void Should_Center_Table_Correctly()
{
// Given
var console = new PlainConsole(width: 80);
var table = new Table();
table.Alignment = Justify.Center;
table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred");
// 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 │ Fred │ ");
console.Lines[5].ShouldBe(" └────────┴────────┴───────┘ ");
}
[Fact]
public void Should_Right_Align_Table_Correctly()
{
// Given
var console = new PlainConsole(width: 80);
var table = new Table();
table.Alignment = Justify.Right;
table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred");
// 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 │ Fred │");
console.Lines[5].ShouldBe(" └────────┴────────┴───────┘");
}
[Fact]
public void Should_Render_Table_Nested_In_Panels_Correctly()
{
@@ -250,52 +349,6 @@ namespace Spectre.Console.Tests.Unit
console.Lines[5].ShouldBe("└───────────────────────────┴───────────────────────────┴──────────────────────┘");
}
[Fact]
public void Should_Render_Table_With_Ascii_Border_Correctly()
{
// Given
var console = new PlainConsole(width: 80);
var table = new Table { Border = TableBorder.Ascii };
table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred");
// 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 | Fred |");
console.Lines[5].ShouldBe("+-------------------------+");
}
[Fact]
public void Should_Render_Table_With_Rounded_Border_Correctly()
{
// Given
var console = new PlainConsole(width: 80);
var table = new Table { Border = TableBorder.Rounded };
table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred");
// 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 │ Fred │");
console.Lines[5].ShouldBe("╰────────┴────────┴───────╯");
}
[Fact]
public void Should_Render_Table_With_No_Border_Correctly()
{
@@ -383,5 +436,165 @@ namespace Spectre.Console.Tests.Unit
console.Lines[1].ShouldBe("│ Foo │ Bar │ Baz │");
console.Lines[2].ShouldBe("└─────┴─────┴────────┘");
}
[Fact]
public void Should_Not_Draw_Tables_That_Are_Impossible_To_Draw()
{
// Given
var console = new PlainConsole(width: 25);
var first = new Table().Border(TableBorder.Rounded).BorderColor(Color.Red);
first.AddColumn(new TableColumn("[u]PS1[/]").Centered());
first.AddColumn(new TableColumn("[u]PS2[/]"));
first.AddColumn(new TableColumn("[u]PS3[/]"));
first.AddRow("Hello", "[red]World[/]", string.Empty);
first.AddRow("[blue]Bonjour[/]", "[white]le[/]", "[red]monde![/]");
first.AddRow("[blue]Hej[/]", "[yellow]Världen[/]", string.Empty);
var second = new Table().Border(TableBorder.Square).BorderColor(Color.Green);
second.AddColumn(new TableColumn("[u]Foo[/]"));
second.AddColumn(new TableColumn("[u]Bar[/]"));
second.AddColumn(new TableColumn("[u]Baz[/]"));
second.AddRow("Hello", "[red]World[/]", string.Empty);
second.AddRow(first, new Text("Whaaat"), new Text("Lolz"));
second.AddRow("[blue]Hej[/]", "[yellow]Världen[/]", string.Empty);
var table = new Table().Border(TableBorder.Rounded);
table.AddColumn(new TableColumn(new Panel("[u]ABC[/]").BorderColor(Color.Red)));
table.AddColumn(new TableColumn(new Panel("[u]DEF[/]").BorderColor(Color.Green)));
table.AddColumn(new TableColumn(new Panel("[u]GHI[/]").BorderColor(Color.Blue)));
table.AddRow(new Text("Hello").Centered(), new Markup("[red]World[/]"), Text.Empty);
table.AddRow(second, new Text("Whaat"), new Text("Lol").RightAligned());
table.AddRow(new Markup("[blue]Hej[/]"), new Markup("[yellow]Världen[/]"), Text.Empty);
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(12);
console.Lines[00].ShouldBe("╭───────┬───────┬───────╮");
console.Lines[01].ShouldBe("│ ┌───┐ │ ┌───┐ │ ┌───┐ │");
console.Lines[02].ShouldBe("│ │ A │ │ │ D │ │ │ G │ │");
console.Lines[03].ShouldBe("│ │ B │ │ │ E │ │ │ H │ │");
console.Lines[04].ShouldBe("│ │ C │ │ │ F │ │ │ I │ │");
console.Lines[05].ShouldBe("│ └───┘ │ └───┘ │ └───┘ │");
console.Lines[06].ShouldBe("├───────┼───────┼───────┤");
console.Lines[07].ShouldBe("│ Hello │ World │ │");
console.Lines[08].ShouldBe("│ … │ Whaat │ Lol │");
console.Lines[09].ShouldBe("│ Hej │ Värld │ │");
console.Lines[10].ShouldBe("│ │ en │ │");
console.Lines[11].ShouldBe("╰───────┴───────┴───────╯");
}
[Fact]
public void Should_Render_Table_With_Title_And_Caption_Correctly()
{
// Given
var console = new PlainConsole(width: 80);
var table = new Table { Border = TableBorder.Rounded };
table.Title = new TableTitle("Hello World");
table.Caption = new TableTitle("Goodbye World");
table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred");
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe(" Hello World ");
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(" Goodbye World ");
}
[Fact]
public void Should_Left_Align_Table_With_Title_And_Caption_Correctly()
{
// Given
var console = new PlainConsole(width: 80);
var table = new Table { Border = TableBorder.Rounded };
table.LeftAligned();
table.Title = new TableTitle("Hello World");
table.Caption = new TableTitle("Goodbye World");
table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred");
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe(" Hello World ");
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(" Goodbye World ");
}
[Fact]
public void Should_Center_Table_With_Title_And_Caption_Correctly()
{
// Given
var console = new PlainConsole(width: 80);
var table = new Table { Border = TableBorder.Rounded };
table.Centered();
table.Title = new TableTitle("Hello World");
table.Caption = new TableTitle("Goodbye World");
table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred");
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe(" Hello World ");
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(" Goodbye World ");
}
[Fact]
public void Should_Right_Align_Table_With_Title_And_Caption_Correctly()
{
// Given
var console = new PlainConsole(width: 80);
var table = new Table { Border = TableBorder.Rounded };
table.RightAligned();
table.Title = new TableTitle("Hello World");
table.Caption = new TableTitle("Goodbye World");
table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred");
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(8);
console.Lines[0].ShouldBe(" Hello World ");
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(" Goodbye World ");
}
}
}

View File

@@ -108,7 +108,7 @@ namespace Spectre.Console.Tests.Unit
// Given
var console = new PlainConsole(14);
var text = new Text("foo pneumonoultramicroscopicsilicovolcanoconiosis bar qux")
.SetOverflow(overflow);
.Overflow(overflow);
// When
console.Render(text);

View File

@@ -44,7 +44,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub", "GitHub", "{C3E2CB
..\.github\workflows\publish.yaml = ..\.github\workflows\publish.yaml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calendars", "..\examples\Calendars\Calendars.csproj", "{57691C7D-683D-46E6-AA4F-57A8C5F65D25}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Calendars", "..\examples\Calendars\Calendars.csproj", "{57691C7D-683D-46E6-AA4F-57A8C5F65D25}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rules", "..\examples\Rules\Rules.csproj", "{8622A261-02C6-40CA-9797-E3F01ED87D6B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -212,6 +214,18 @@ Global
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Release|x64.Build.0 = Release|Any CPU
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Release|x86.ActiveCfg = Release|Any CPU
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Release|x86.Build.0 = Release|Any CPU
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Debug|x64.ActiveCfg = Debug|Any CPU
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Debug|x64.Build.0 = Debug|Any CPU
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Debug|x86.ActiveCfg = Debug|Any CPU
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Debug|x86.Build.0 = Debug|Any CPU
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Release|Any CPU.Build.0 = Release|Any CPU
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Release|x64.ActiveCfg = Release|Any CPU
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Release|x64.Build.0 = Release|Any CPU
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Release|x86.ActiveCfg = Release|Any CPU
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -229,6 +243,7 @@ Global
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
{C3E2CB5C-1517-4C75-B59A-93D4E22BEC8D} = {20595AD4-8D75-4AF8-B6BC-9C38C160423F}
{57691C7D-683D-46E6-AA4F-57A8C5F65D25} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
{8622A261-02C6-40CA-9797-E3F01ED87D6B} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C}

View File

@@ -14,7 +14,7 @@ namespace Spectre.Console
public static Color Foreground
{
get => CurrentStyle.Foreground;
set => CurrentStyle = CurrentStyle.WithForeground(value);
set => CurrentStyle = CurrentStyle.Foreground(value);
}
/// <summary>
@@ -23,7 +23,7 @@ namespace Spectre.Console
public static Color Background
{
get => CurrentStyle.Background;
set => CurrentStyle = CurrentStyle.WithBackground(value);
set => CurrentStyle = CurrentStyle.Background(value);
}
/// <summary>
@@ -32,7 +32,7 @@ namespace Spectre.Console
public static Decoration Decoration
{
get => CurrentStyle.Decoration;
set => CurrentStyle = CurrentStyle.WithDecoration(value);
set => CurrentStyle = CurrentStyle.Decoration(value);
}
/// <summary>

View File

@@ -12,7 +12,7 @@ namespace Spectre.Console
/// <param name="obj">The alignable object.</param>
/// <param name="alignment">The alignment.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetAlignment<T>(this T obj, Justify alignment)
public static T Alignment<T>(this T obj, Justify? alignment)
where T : class, IAlignable
{
if (obj is null)

View File

@@ -69,7 +69,7 @@ namespace Spectre.Console
/// <param name="calendar">The calendar.</param>
/// <param name="style">The highlight style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Calendar SetHighlightStyle(this Calendar calendar, Style? style)
public static Calendar HighlightStyle(this Calendar calendar, Style? style)
{
if (calendar is null)
{
@@ -79,5 +79,22 @@ namespace Spectre.Console
calendar.HightlightStyle = style ?? Style.Plain;
return calendar;
}
/// <summary>
/// Sets the calendar's header <see cref="Style"/>.
/// </summary>
/// <param name="calendar">The calendar.</param>
/// <param name="style">The header style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Calendar HeaderStyle(this Calendar calendar, Style? style)
{
if (calendar is null)
{
throw new ArgumentNullException(nameof(calendar));
}
calendar.HeaderStyle = style ?? Style.Plain;
return calendar;
}
}
}

View File

@@ -17,7 +17,12 @@ namespace Spectre.Console
public static T Collapse<T>(this T obj)
where T : class, IExpandable
{
SetExpand<T>(obj, false);
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Expand = false;
return obj;
}
@@ -29,20 +34,14 @@ namespace Spectre.Console
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T Expand<T>(this T obj)
where T : class, IExpandable
{
SetExpand<T>(obj, true);
return obj;
}
private static void SetExpand<T>(T obj, bool value)
where T : class, IExpandable
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Expand = value;
obj.Expand = true;
return obj;
}
}
}

View File

@@ -7,11 +7,29 @@ namespace Spectre.Console
/// </summary>
public static class HasBorderExtensions
{
/// <summary>
/// Enables the safe border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to enable the safe border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SafeBorder<T>(this T obj)
where T : class, IHasBorder
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.UseSafeBorder = true;
return obj;
}
/// <summary>
/// Disables the safe border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <param name="obj">The object to disable the safe border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T NoSafeBorder<T>(this T obj)
where T : class, IHasBorder
@@ -29,10 +47,10 @@ namespace Spectre.Console
/// Sets the border style.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border color for.</param>
/// <param name="obj">The object to set the border style for.</param>
/// <param name="style">The border style to set.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetBorderStyle<T>(this T obj, Style style)
public static T BorderStyle<T>(this T obj, Style style)
where T : class, IHasBorder
{
if (obj is null)
@@ -51,7 +69,7 @@ namespace Spectre.Console
/// <param name="obj">The object to set the border color for.</param>
/// <param name="color">The border color to set.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetBorderColor<T>(this T obj, Color color)
public static T BorderColor<T>(this T obj, Color color)
where T : class, IHasBorder
{
if (obj is null)
@@ -59,7 +77,7 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(obj));
}
obj.BorderStyle = (obj.BorderStyle ?? Style.Plain).WithForeground(color);
obj.BorderStyle = (obj.BorderStyle ?? Style.Plain).Foreground(color);
return obj;
}
}

View File

@@ -7,6 +7,25 @@ namespace Spectre.Console
/// </summary>
public static class HasBoxBorderExtensions
{
/// <summary>
/// Sets the border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <param name="border">The border to use.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T Border<T>(this T obj, BoxBorder border)
where T : class, IHasBoxBorder
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Border = border;
return obj;
}
/// <summary>
/// Do not display a border.
/// </summary>
@@ -16,7 +35,7 @@ namespace Spectre.Console
public static T NoBorder<T>(this T obj)
where T : class, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.None);
return Border(obj, BoxBorder.None);
}
/// <summary>
@@ -28,7 +47,7 @@ namespace Spectre.Console
public static T SquareBorder<T>(this T obj)
where T : class, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.Square);
return Border(obj, BoxBorder.Square);
}
/// <summary>
@@ -40,7 +59,7 @@ namespace Spectre.Console
public static T AsciiBorder<T>(this T obj)
where T : class, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.Ascii);
return Border(obj, BoxBorder.Ascii);
}
/// <summary>
@@ -52,7 +71,7 @@ namespace Spectre.Console
public static T RoundedBorder<T>(this T obj)
where T : class, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.Rounded);
return Border(obj, BoxBorder.Rounded);
}
/// <summary>
@@ -64,7 +83,7 @@ namespace Spectre.Console
public static T HeavyBorder<T>(this T obj)
where T : class, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.Heavy);
return Border(obj, BoxBorder.Heavy);
}
/// <summary>
@@ -76,26 +95,7 @@ namespace Spectre.Console
public static T DoubleBorder<T>(this T obj)
where T : class, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.Double);
}
/// <summary>
/// Sets the border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <param name="border">The border to use.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetBorder<T>(this T obj, BoxBorder border)
where T : class, IHasBoxBorder
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Border = border;
return obj;
return Border(obj, BoxBorder.Double);
}
}
}

View File

@@ -15,7 +15,7 @@ namespace Spectre.Console
/// <param name="obj">The object to set the culture for.</param>
/// <param name="culture">The culture to set.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetCulture<T>(this T obj, CultureInfo culture)
public static T Culture<T>(this T obj, CultureInfo culture)
where T : class, IHasCulture
{
if (obj is null)
@@ -39,16 +39,15 @@ namespace Spectre.Console
/// <param name="obj">The object to set the culture for.</param>
/// <param name="name">The culture to set.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetCulture<T>(this T obj, string name)
public static T Culture<T>(this T obj, string name)
where T : class, IHasCulture
{
if (obj is null)
if (name is null)
{
throw new ArgumentNullException(nameof(obj));
throw new ArgumentNullException(nameof(name));
}
obj.Culture = CultureInfo.GetCultureInfo(name);
return obj;
return Culture(obj, CultureInfo.GetCultureInfo(name));
}
/// <summary>
@@ -56,18 +55,12 @@ namespace Spectre.Console
/// </summary>
/// <typeparam name="T">An object type with a culture.</typeparam>
/// <param name="obj">The object to set the culture for.</param>
/// <param name="name">The culture to set.</param>
/// <param name="culture">The culture to set.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetCulture<T>(this T obj, int name)
public static T Culture<T>(this T obj, int culture)
where T : class, IHasCulture
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Culture = CultureInfo.GetCultureInfo(name);
return obj;
return Culture(obj, CultureInfo.GetCultureInfo(culture));
}
}
}

View File

@@ -16,7 +16,7 @@ namespace Spectre.Console
public static T NoBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.None);
return Border(obj, TableBorder.None);
}
/// <summary>
@@ -28,7 +28,7 @@ namespace Spectre.Console
public static T SquareBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Square);
return Border(obj, TableBorder.Square);
}
/// <summary>
@@ -40,7 +40,7 @@ namespace Spectre.Console
public static T AsciiBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Ascii);
return Border(obj, TableBorder.Ascii);
}
/// <summary>
@@ -52,7 +52,7 @@ namespace Spectre.Console
public static T Ascii2Border<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Ascii2);
return Border(obj, TableBorder.Ascii2);
}
/// <summary>
@@ -64,7 +64,7 @@ namespace Spectre.Console
public static T AsciiDoubleHeadBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.AsciiDoubleHead);
return Border(obj, TableBorder.AsciiDoubleHead);
}
/// <summary>
@@ -76,7 +76,7 @@ namespace Spectre.Console
public static T RoundedBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Rounded);
return Border(obj, TableBorder.Rounded);
}
/// <summary>
@@ -88,7 +88,7 @@ namespace Spectre.Console
public static T MinimalBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Minimal);
return Border(obj, TableBorder.Minimal);
}
/// <summary>
@@ -100,7 +100,7 @@ namespace Spectre.Console
public static T MinimalHeavyHeadBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.MinimalHeavyHead);
return Border(obj, TableBorder.MinimalHeavyHead);
}
/// <summary>
@@ -112,7 +112,7 @@ namespace Spectre.Console
public static T MinimalDoubleHeadBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.MinimalDoubleHead);
return Border(obj, TableBorder.MinimalDoubleHead);
}
/// <summary>
@@ -124,7 +124,7 @@ namespace Spectre.Console
public static T SimpleBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Simple);
return Border(obj, TableBorder.Simple);
}
/// <summary>
@@ -136,7 +136,7 @@ namespace Spectre.Console
public static T SimpleHeavyBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.SimpleHeavy);
return Border(obj, TableBorder.SimpleHeavy);
}
/// <summary>
@@ -148,7 +148,7 @@ namespace Spectre.Console
public static T HorizontalBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Horizontal);
return Border(obj, TableBorder.Horizontal);
}
/// <summary>
@@ -160,7 +160,7 @@ namespace Spectre.Console
public static T HeavyBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Heavy);
return Border(obj, TableBorder.Heavy);
}
/// <summary>
@@ -172,7 +172,7 @@ namespace Spectre.Console
public static T HeavyEdgeBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.HeavyEdge);
return Border(obj, TableBorder.HeavyEdge);
}
/// <summary>
@@ -184,7 +184,7 @@ namespace Spectre.Console
public static T HeavyHeadBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.HeavyHead);
return Border(obj, TableBorder.HeavyHead);
}
/// <summary>
@@ -196,7 +196,7 @@ namespace Spectre.Console
public static T DoubleBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Double);
return Border(obj, TableBorder.Double);
}
/// <summary>
@@ -208,7 +208,7 @@ namespace Spectre.Console
public static T DoubleEdgeBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.DoubleEdge);
return Border(obj, TableBorder.DoubleEdge);
}
/// <summary>
@@ -220,7 +220,7 @@ namespace Spectre.Console
public static T MarkdownBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Markdown);
return Border(obj, TableBorder.Markdown);
}
/// <summary>
@@ -230,7 +230,7 @@ namespace Spectre.Console
/// <param name="obj">The object to set the border for.</param>
/// <param name="border">The border to use.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetBorder<T>(this T obj, TableBorder border)
public static T Border<T>(this T obj, TableBorder border)
where T : class, IHasTableBorder
{
if (obj is null)

View File

@@ -0,0 +1,32 @@
using System;
using System.ComponentModel;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IAlignable"/>.
/// </summary>
public static class ObsoleteAlignableExtensions
{
/// <summary>
/// Sets the alignment for an <see cref="IAlignable"/> object.
/// </summary>
/// <typeparam name="T">The alignable object type.</typeparam>
/// <param name="obj">The alignable object.</param>
/// <param name="alignment">The alignment.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Alignment(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static T SetAlignment<T>(this T obj, Justify alignment)
where T : class, IAlignable
{
if (obj is null)
{
throw new System.ArgumentNullException(nameof(obj));
}
obj.Alignment = alignment;
return obj;
}
}
}

View File

@@ -0,0 +1,49 @@
using System;
using System.ComponentModel;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="Calendar"/>.
/// </summary>
public static class ObsoleteCalendarExtensions
{
/// <summary>
/// Sets the calendar's highlight <see cref="Style"/>.
/// </summary>
/// <param name="calendar">The calendar.</param>
/// <param name="style">The highlight style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use HighlightStyle(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Calendar SetHighlightStyle(this Calendar calendar, Style? style)
{
if (calendar is null)
{
throw new ArgumentNullException(nameof(calendar));
}
calendar.HightlightStyle = style ?? Style.Plain;
return calendar;
}
/// <summary>
/// Sets the calendar's header <see cref="Style"/>.
/// </summary>
/// <param name="calendar">The calendar.</param>
/// <param name="style">The header style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use HeaderStyle(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Calendar SetHeaderStyle(this Calendar calendar, Style? style)
{
if (calendar is null)
{
throw new ArgumentNullException(nameof(calendar));
}
calendar.HeaderStyle = style ?? Style.Plain;
return calendar;
}
}
}

View File

@@ -0,0 +1,53 @@
using System;
using System.ComponentModel;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IHasBorder"/>.
/// </summary>
public static class ObsoleteHasBorderExtensions
{
/// <summary>
/// Sets the border style.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border color for.</param>
/// <param name="style">The border style to set.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use BorderStyle(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static T SetBorderStyle<T>(this T obj, Style style)
where T : class, IHasBorder
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.BorderStyle = style;
return obj;
}
/// <summary>
/// Sets the border color.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border color for.</param>
/// <param name="color">The border color to set.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use BorderColor(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static T SetBorderColor<T>(this T obj, Color color)
where T : class, IHasBorder
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.BorderStyle = (obj.BorderStyle ?? Style.Plain).Foreground(color);
return obj;
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.ComponentModel;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IHasBoxBorder"/>.
/// </summary>
public static class ObsoleteHasBoxBorderExtensions
{
/// <summary>
/// Sets the border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <param name="border">The border to use.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Border(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static T SetBorder<T>(this T obj, BoxBorder border)
where T : class, IHasBoxBorder
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Border = border;
return obj;
}
}
}

View File

@@ -0,0 +1,80 @@
using System;
using System.ComponentModel;
using System.Globalization;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IHasCulture"/>.
/// </summary>
public static class ObsoleteHasCultureExtensions
{
/// <summary>
/// Sets the culture.
/// </summary>
/// <typeparam name="T">An object type with a culture.</typeparam>
/// <param name="obj">The object to set the culture for.</param>
/// <param name="culture">The culture to set.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Culture(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static T SetCulture<T>(this T obj, CultureInfo culture)
where T : class, IHasCulture
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
if (culture is null)
{
throw new ArgumentNullException(nameof(culture));
}
obj.Culture = culture;
return obj;
}
/// <summary>
/// Sets the culture.
/// </summary>
/// <typeparam name="T">An object type with a culture.</typeparam>
/// <param name="obj">The object to set the culture for.</param>
/// <param name="name">The culture to set.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Culture(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static T SetCulture<T>(this T obj, string name)
where T : class, IHasCulture
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Culture = CultureInfo.GetCultureInfo(name);
return obj;
}
/// <summary>
/// Sets the culture.
/// </summary>
/// <typeparam name="T">An object type with a culture.</typeparam>
/// <param name="obj">The object to set the culture for.</param>
/// <param name="name">The culture to set.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Culture(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static T SetCulture<T>(this T obj, int name)
where T : class, IHasCulture
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Culture = CultureInfo.GetCultureInfo(name);
return obj;
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.ComponentModel;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IHasTableBorder"/>.
/// </summary>
public static class ObsoleteHasTableBorderExtensions
{
/// <summary>
/// Sets the border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <param name="border">The border to use.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Border(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static T SetBorder<T>(this T obj, TableBorder border)
where T : class, IHasTableBorder
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Border = border;
return obj;
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.ComponentModel;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IOverflowable"/>.
/// </summary>
public static class ObsoleteOverflowableExtensions
{
/// <summary>
/// Sets the overflow strategy.
/// </summary>
/// <typeparam name="T">An object implementing <see cref="IOverflowable"/>.</typeparam>
/// <param name="obj">The overflowable object instance.</param>
/// <param name="overflow">The overflow strategy to use.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Overflow(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static T SetOverflow<T>(this T obj, Overflow overflow)
where T : class, IOverflowable
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Overflow = overflow;
return obj;
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.ComponentModel;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IPaddable"/>.
/// </summary>
public static class ObsoletePaddableExtensions
{
/// <summary>
/// Sets the left and right padding.
/// </summary>
/// <typeparam name="T">An object implementing <see cref="IPaddable"/>.</typeparam>
/// <param name="obj">The paddable object instance.</param>
/// <param name="left">The left padding to apply.</param>
/// <param name="top">The top padding to apply.</param>
/// <param name="right">The right padding to apply.</param>
/// <param name="bottom">The bottom padding to apply.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Padding(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static T SetPadding<T>(this T obj, int left, int top, int right, int bottom)
where T : class, IPaddable
{
return SetPadding(obj, new Padding(left, top, right, bottom));
}
/// <summary>
/// Sets the padding.
/// </summary>
/// <typeparam name="T">An object implementing <see cref="IPaddable"/>.</typeparam>
/// <param name="obj">The paddable object instance.</param>
/// <param name="padding">The padding to apply.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Padding(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static T SetPadding<T>(this T obj, Padding padding)
where T : class, IPaddable
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Padding = padding;
return obj;
}
}
}

View File

@@ -0,0 +1,58 @@
using System;
using System.ComponentModel;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="Panel"/>.
/// </summary>
public static class ObsoletePanelExtensions
{
/// <summary>
/// Sets the panel header.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="text">The header text.</param>
/// <param name="style">The header style.</param>
/// <param name="alignment">The header alignment.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Header(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Panel SetHeader(this Panel panel, string text, Style? style = null, Justify? alignment = null)
{
if (panel is null)
{
throw new ArgumentNullException(nameof(panel));
}
if (text is null)
{
throw new ArgumentNullException(nameof(text));
}
style ??= panel.Header?.Style;
alignment ??= panel.Header?.Alignment;
return SetHeader(panel, new PanelHeader(text, style, alignment));
}
/// <summary>
/// Sets the panel header.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="header">The header to use.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Header(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Panel SetHeader(this Panel panel, PanelHeader header)
{
if (panel is null)
{
throw new ArgumentNullException(nameof(panel));
}
panel.Header = header;
return panel;
}
}
}

View File

@@ -0,0 +1,59 @@
using System;
using System.ComponentModel;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="RuleExtensions"/>.
/// </summary>
public static class ObsoleteRuleExtensions
{
/// <summary>
/// Sets the rule title.
/// </summary>
/// <param name="rule">The rule.</param>
/// <param name="title">The title.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Title(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Rule SetTitle(this Rule rule, string title)
{
if (rule is null)
{
throw new ArgumentNullException(nameof(rule));
}
if (title is null)
{
throw new ArgumentNullException(nameof(title));
}
rule.Title = title;
return rule;
}
/// <summary>
/// Sets the rule style.
/// </summary>
/// <param name="rule">The rule.</param>
/// <param name="style">The rule style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Style(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Rule SetStyle(this Rule rule, Style style)
{
if (rule is null)
{
throw new ArgumentNullException(nameof(rule));
}
if (style is null)
{
throw new ArgumentNullException(nameof(style));
}
rule.Style = style;
return rule;
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.ComponentModel;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="string"/>.
/// </summary>
public static class ObsoleteStringExtensions
{
/// <summary>
/// Escapes text so that it wont be interpreted as markup.
/// </summary>
/// <param name="text">The text to escape.</param>
/// <returns>A string that is safe to use in markup.</returns>
[Obsolete("Use EscapeMarkup(..) instead.", false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public static string SafeMarkup(this string text)
{
return text.EscapeMarkup();
}
}
}

View File

@@ -0,0 +1,100 @@
using System;
using System.ComponentModel;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="Style"/>.
/// </summary>
public static class ObsoleteStyleExtensions
{
/// <summary>
/// Creates a new style from the specified one with
/// the specified foreground color.
/// </summary>
/// <param name="style">The style.</param>
/// <param name="color">The foreground color.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Foreground(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Style WithForeground(this Style style, Color color)
{
if (style is null)
{
throw new ArgumentNullException(nameof(style));
}
return new Style(
foreground: color,
background: style.Background,
decoration: style.Decoration);
}
/// <summary>
/// Creates a new style from the specified one with
/// the specified background color.
/// </summary>
/// <param name="style">The style.</param>
/// <param name="color">The background color.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Background(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Style WithBackground(this Style style, Color color)
{
if (style is null)
{
throw new ArgumentNullException(nameof(style));
}
return new Style(
foreground: style.Foreground,
background: color,
decoration: style.Decoration);
}
/// <summary>
/// Creates a new style from the specified one with
/// the specified text decoration.
/// </summary>
/// <param name="style">The style.</param>
/// <param name="decoration">The text decoration.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Decoration(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Style WithDecoration(this Style style, Decoration decoration)
{
if (style is null)
{
throw new ArgumentNullException(nameof(style));
}
return new Style(
foreground: style.Foreground,
background: style.Background,
decoration: decoration);
}
/// <summary>
/// Creates a new style from the specified one with
/// the specified link.
/// </summary>
/// <param name="style">The style.</param>
/// <param name="link">The link.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Link(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Style WithLink(this Style style, string link)
{
if (style is null)
{
throw new ArgumentNullException(nameof(style));
}
return new Style(
foreground: style.Foreground,
background: style.Background,
decoration: style.Decoration,
link: link);
}
}
}

View File

@@ -0,0 +1,202 @@
using System;
using System.ComponentModel;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="Table"/>.
/// </summary>
public static class ObsoleteTableExtensions
{
/// <summary>
/// Sets the table width.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="width">The width.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Width(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table SetWidth(this Table table, int width)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
table.Width = width;
return table;
}
/// <summary>
/// Sets the table heading.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="text">The heading.</param>
/// <param name="style">The style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Heading(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table SetHeading(this Table table, string text, Style? style = null)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
if (text is null)
{
throw new ArgumentNullException(nameof(text));
}
return SetHeading(table, new TableTitle(text, style));
}
/// <summary>
/// Sets the table heading.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="heading">The heading.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Heading(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table SetHeading(this Table table, TableTitle heading)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
table.Title = heading;
return table;
}
/// <summary>
/// Sets the table footnote.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="text">The footnote.</param>
/// <param name="style">The style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Caption(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table SetFootnote(this Table table, string text, Style? style = null)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
if (text is null)
{
throw new ArgumentNullException(nameof(text));
}
return SetFootnote(table, new TableTitle(text, style));
}
/// <summary>
/// Sets the table footnote.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="footnote">The footnote.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Caption(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table SetFootnote(this Table table, TableTitle footnote)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
table.Caption = footnote;
return table;
}
/// <summary>
/// Sets the table footnote.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="text">The footnote.</param>
/// <param name="style">The style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Caption(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table Footnote(this Table table, string text, Style? style = null)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
if (text is null)
{
throw new ArgumentNullException(nameof(text));
}
return Footnote(table, new TableTitle(text, style));
}
/// <summary>
/// Sets the table footnote.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="footnote">The footnote.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Caption(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table Footnote(this Table table, TableTitle footnote)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
table.Caption = footnote;
return table;
}
/// <summary>
/// Sets the table heading.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="text">The heading.</param>
/// <param name="style">The style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Title(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table Heading(this Table table, string text, Style? style = null)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
if (text is null)
{
throw new ArgumentNullException(nameof(text));
}
return Heading(table, new TableTitle(text, style));
}
/// <summary>
/// Sets the table heading.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="heading">The heading.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Title(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table Heading(this Table table, TableTitle heading)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
table.Title = heading;
return table;
}
}
}

View File

@@ -21,7 +21,7 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(obj));
}
return SetOverflow(obj, Overflow.Fold);
return Overflow(obj, Console.Overflow.Fold);
}
/// <summary>
@@ -38,7 +38,7 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(obj));
}
return SetOverflow(obj, Overflow.Crop);
return Overflow(obj, Console.Overflow.Crop);
}
/// <summary>
@@ -55,7 +55,7 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(obj));
}
return SetOverflow(obj, Overflow.Ellipsis);
return Overflow(obj, Console.Overflow.Ellipsis);
}
/// <summary>
@@ -65,7 +65,7 @@ namespace Spectre.Console
/// <param name="obj">The overflowable object instance.</param>
/// <param name="overflow">The overflow strategy to use.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetOverflow<T>(this T obj, Overflow overflow)
public static T Overflow<T>(this T obj, Overflow overflow)
where T : class, IOverflowable
{
if (obj is null)

View File

@@ -22,7 +22,7 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(obj));
}
return SetPadding(obj, new Padding(left, obj.Padding.Top, obj.Padding.Right, obj.Padding.Bottom));
return Padding(obj, new Padding(left, obj.Padding.Top, obj.Padding.Right, obj.Padding.Bottom));
}
/// <summary>
@@ -40,7 +40,7 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(obj));
}
return SetPadding(obj, new Padding(obj.Padding.Left, top, obj.Padding.Right, obj.Padding.Bottom));
return Padding(obj, new Padding(obj.Padding.Left, top, obj.Padding.Right, obj.Padding.Bottom));
}
/// <summary>
@@ -58,7 +58,7 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(obj));
}
return SetPadding(obj, new Padding(obj.Padding.Left, obj.Padding.Top, right, obj.Padding.Bottom));
return Padding(obj, new Padding(obj.Padding.Left, obj.Padding.Top, right, obj.Padding.Bottom));
}
/// <summary>
@@ -76,7 +76,7 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(obj));
}
return SetPadding(obj, new Padding(obj.Padding.Left, obj.Padding.Top, obj.Padding.Right, bottom));
return Padding(obj, new Padding(obj.Padding.Left, obj.Padding.Top, obj.Padding.Right, bottom));
}
/// <summary>
@@ -89,10 +89,10 @@ namespace Spectre.Console
/// <param name="right">The right padding to apply.</param>
/// <param name="bottom">The bottom padding to apply.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetPadding<T>(this T obj, int left, int top, int right, int bottom)
public static T Padding<T>(this T obj, int left, int top, int right, int bottom)
where T : class, IPaddable
{
return SetPadding(obj, new Padding(left, top, right, bottom));
return Padding(obj, new Padding(left, top, right, bottom));
}
/// <summary>
@@ -102,7 +102,7 @@ namespace Spectre.Console
/// <param name="obj">The paddable object instance.</param>
/// <param name="padding">The padding to apply.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetPadding<T>(this T obj, Padding padding)
public static T Padding<T>(this T obj, Padding padding)
where T : class, IPaddable
{
if (obj is null)

View File

@@ -15,7 +15,7 @@ namespace Spectre.Console
/// <param name="style">The header style.</param>
/// <param name="alignment">The header alignment.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Panel SetHeader(this Panel panel, string text, Style? style = null, Justify? alignment = null)
public static Panel Header(this Panel panel, string text, Style? style = null, Justify? alignment = null)
{
if (panel is null)
{
@@ -27,7 +27,69 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(text));
}
return SetHeader(panel, new PanelHeader(text, style, alignment));
style ??= panel.Header?.Style;
alignment ??= panel.Header?.Alignment;
return Header(panel, new PanelHeader(text, style, alignment));
}
/// <summary>
/// Sets the panel header style.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="style">The header style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Panel HeaderStyle(this Panel panel, Style style)
{
if (panel is null)
{
throw new ArgumentNullException(nameof(panel));
}
if (style is null)
{
throw new ArgumentNullException(nameof(style));
}
if (panel.Header != null)
{
// Update existing style
panel.Header.Style = style;
}
else
{
// Create header
Header(panel, string.Empty, style, null);
}
return panel;
}
/// <summary>
/// Sets the panel header alignment.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="alignment">The header alignment.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Panel HeaderAlignment(this Panel panel, Justify alignment)
{
if (panel is null)
{
throw new ArgumentNullException(nameof(panel));
}
if (panel.Header != null)
{
// Update existing style
panel.Header.Alignment = alignment;
}
else
{
// Create header
Header(panel, string.Empty, null, alignment);
}
return panel;
}
/// <summary>
@@ -36,7 +98,7 @@ namespace Spectre.Console
/// <param name="panel">The panel.</param>
/// <param name="header">The header to use.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Panel SetHeader(this Panel panel, PanelHeader header)
public static Panel Header(this Panel panel, PanelHeader header)
{
if (panel is null)
{

View File

@@ -0,0 +1,54 @@
using System;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="RuleExtensions"/>.
/// </summary>
public static class RuleExtensions
{
/// <summary>
/// Sets the rule title.
/// </summary>
/// <param name="rule">The rule.</param>
/// <param name="title">The title.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Rule RuleTitle(this Rule rule, string title)
{
if (rule is null)
{
throw new ArgumentNullException(nameof(rule));
}
if (title is null)
{
throw new ArgumentNullException(nameof(title));
}
rule.Title = title;
return rule;
}
/// <summary>
/// Sets the rule style.
/// </summary>
/// <param name="rule">The rule.</param>
/// <param name="style">The rule style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Rule RuleStyle(this Rule rule, Style style)
{
if (rule is null)
{
throw new ArgumentNullException(nameof(rule));
}
if (style is null)
{
throw new ArgumentNullException(nameof(style));
}
rule.Style = style;
return rule;
}
}
}

View File

@@ -6,12 +6,11 @@ namespace Spectre.Console
public static class StringExtensions
{
/// <summary>
/// Converts the string to something that is safe to
/// use in a markup string.
/// Escapes text so that it wont be interpreted as markup.
/// </summary>
/// <param name="text">The text to convert.</param>
/// <returns>A string that is safe to use in a markup string.</returns>
public static string SafeMarkup(this string text)
/// <param name="text">The text to escape.</param>
/// <returns>A string that is safe to use in markup.</returns>
public static string EscapeMarkup(this string text)
{
if (text == null)
{

View File

@@ -14,7 +14,7 @@ namespace Spectre.Console
/// <param name="style">The style.</param>
/// <param name="color">The foreground color.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Style WithForeground(this Style style, Color color)
public static Style Foreground(this Style style, Color color)
{
if (style is null)
{
@@ -34,7 +34,7 @@ namespace Spectre.Console
/// <param name="style">The style.</param>
/// <param name="color">The background color.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Style WithBackground(this Style style, Color color)
public static Style Background(this Style style, Color color)
{
if (style is null)
{
@@ -54,7 +54,7 @@ namespace Spectre.Console
/// <param name="style">The style.</param>
/// <param name="decoration">The text decoration.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Style WithDecoration(this Style style, Decoration decoration)
public static Style Decoration(this Style style, Decoration decoration)
{
if (style is null)
{
@@ -74,7 +74,7 @@ namespace Spectre.Console
/// <param name="style">The style.</param>
/// <param name="link">The link.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Style WithLink(this Style style, string link)
public static Style Link(this Style style, string link)
{
if (style is null)
{

View File

@@ -5,7 +5,7 @@ namespace Spectre.Console.Rendering
/// <summary>
/// Contains extension methods for <see cref="TableBorder"/>.
/// </summary>
public static class BorderExtensions
public static class TableBorderExtensions
{
/// <summary>
/// Gets the safe border for a border.

View File

@@ -0,0 +1,55 @@
using System;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="TableColumn"/>.
/// </summary>
public static class TableColumnExtensions
{
/// <summary>
/// Sets the table column footer.
/// </summary>
/// <param name="column">The table column.</param>
/// <param name="footer">The table column markup text.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static TableColumn Footer(this TableColumn column, string footer)
{
if (column is null)
{
throw new ArgumentNullException(nameof(column));
}
if (footer is null)
{
throw new ArgumentNullException(nameof(footer));
}
column.Footer = new Markup(footer);
return column;
}
/// <summary>
/// Sets the table column footer.
/// </summary>
/// <param name="column">The table column.</param>
/// <param name="footer">The table column footer.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static TableColumn Footer(this TableColumn column, IRenderable footer)
{
if (column is null)
{
throw new ArgumentNullException(nameof(column));
}
if (footer is null)
{
throw new ArgumentNullException(nameof(footer));
}
column.Footer = footer;
return column;
}
}
}

View File

@@ -134,7 +134,7 @@ namespace Spectre.Console
/// <param name="table">The table.</param>
/// <param name="width">The width.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table SetWidth(this Table table, int width)
public static Table Width(this Table table, int width)
{
if (table is null)
{
@@ -176,5 +176,115 @@ namespace Spectre.Console
table.ShowHeaders = false;
return table;
}
/// <summary>
/// Shows table footers.
/// </summary>
/// <param name="table">The table.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table ShowFooters(this Table table)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
table.ShowFooters = true;
return table;
}
/// <summary>
/// Hides table footers.
/// </summary>
/// <param name="table">The table.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table HideFooters(this Table table)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
table.ShowFooters = false;
return table;
}
/// <summary>
/// Sets the table title.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="text">The table title markup text.</param>
/// <param name="style">The table title style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table Title(this Table table, string text, Style? style = null)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
if (text is null)
{
throw new ArgumentNullException(nameof(text));
}
return Title(table, new TableTitle(text, style));
}
/// <summary>
/// Sets the table title.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="title">The table title.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table Title(this Table table, TableTitle title)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
table.Title = title;
return table;
}
/// <summary>
/// Sets the table caption.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="text">The caption markup text.</param>
/// <param name="style">The style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table Caption(this Table table, string text, Style? style = null)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
if (text is null)
{
throw new ArgumentNullException(nameof(text));
}
return Caption(table, new TableTitle(text, style));
}
/// <summary>
/// Sets the table caption.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="caption">The caption.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table Caption(this Table table, TableTitle caption)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
table.Caption = caption;
return table;
}
}
}

View File

@@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using Spectre.Console.Rendering;
namespace Spectre.Console.Internal
{
internal static class Aligner
{
public static string Align(RenderContext context, string text, Justify? alignment, int maxWidth)
{
if (alignment == null || alignment == Justify.Left)
{
return text;
}
var width = Cell.GetCellLength(context, text);
if (width >= maxWidth)
{
return text;
}
switch (alignment)
{
case Justify.Right:
{
var diff = maxWidth - width;
return new string(' ', diff) + text;
}
case Justify.Center:
{
var diff = (maxWidth - width) / 2;
var left = new string(' ', diff);
var right = new string(' ', diff);
// Right side
var remainder = (maxWidth - width) % 2;
if (remainder != 0)
{
right += new string(' ', remainder);
}
return left + text + right;
}
default:
throw new NotSupportedException("Unknown alignment");
}
}
public static void Align<T>(RenderContext context, T segments, Justify? alignment, int maxWidth)
where T : List<Segment>
{
if (alignment == null || alignment == Justify.Left)
{
return;
}
var width = Segment.CellCount(context, segments);
if (width >= maxWidth)
{
return;
}
switch (alignment)
{
case Justify.Right:
{
var diff = maxWidth - width;
segments.Insert(0, new Segment(new string(' ', diff)));
break;
}
case Justify.Center:
{
// Left side.
var diff = (maxWidth - width) / 2;
segments.Insert(0, new Segment(new string(' ', diff)));
// Right side
segments.Add(new Segment(new string(' ', diff)));
var remainder = (maxWidth - width) % 2;
if (remainder != 0)
{
segments.Add(new Segment(new string(' ', remainder)));
}
break;
}
default:
throw new NotSupportedException("Unknown alignment");
}
}
}
}

View File

@@ -42,7 +42,7 @@ namespace Spectre.Console
{
var shortenTypes = (settings.Format & ExceptionFormats.ShortenTypes) != 0;
var type = Emphasize(ex.Type, new[] { '.' }, settings.Style.Exception, shortenTypes, settings);
var message = $"[{settings.Style.Message.ToMarkup()}]{ex.Message.SafeMarkup()}[/]";
var message = $"[{settings.Style.Message.ToMarkup()}]{ex.Message.EscapeMarkup()}[/]";
return new Markup(string.Concat(type, ": ", message));
}
@@ -99,7 +99,7 @@ namespace Spectre.Console
{
var typeColor = settings.Style.ParameterType.ToMarkup();
var nameColor = settings.Style.ParameterName.ToMarkup();
var parameters = frame.Parameters.Select(x => $"[{typeColor}]{x.Type.SafeMarkup()}[/] [{nameColor}]{x.Name}[/]");
var parameters = frame.Parameters.Select(x => $"[{typeColor}]{x.Type.EscapeMarkup()}[/] [{nameColor}]{x.Name}[/]");
builder.Append(string.Join(", ", parameters));
}

View File

@@ -8,14 +8,9 @@ namespace Spectre.Console.Internal
public static string GetAbbreviatedDayName(this DayOfWeek day, CultureInfo culture)
{
culture ??= CultureInfo.InvariantCulture;
var name = culture.DateTimeFormat.GetAbbreviatedDayName(day);
if (name.Length > 0 && char.IsLower(name[0]))
{
name = string.Format(culture, "{0}{1}", char.ToUpper(name[0], culture), name.Substring(1));
}
return name;
return culture.DateTimeFormat
.GetAbbreviatedDayName(day)
.Capitalize(culture);
}
public static DayOfWeek GetNextWeekDay(this DayOfWeek day)

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Spectre.Console.Rendering;
@@ -15,9 +16,31 @@ namespace Spectre.Console.Internal
public static int CellLength(this string text, RenderContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
return Cell.GetCellLength(context, text);
}
public static string Capitalize(this string text, CultureInfo? culture = null)
{
if (text == null)
{
return string.Empty;
}
culture ??= CultureInfo.InvariantCulture;
if (text.Length > 0 && char.IsLower(text[0]))
{
text = string.Format(culture, "{0}{1}", char.ToUpper(text[0], culture), text.Substring(1));
}
return text;
}
public static string NormalizeLineEndings(this string text, bool native = false)
{
text ??= string.Empty;

View File

@@ -21,6 +21,17 @@ namespace Spectre.Console.Internal
}
}
// TODO: We need to figure out why Segment.SplitLines fails
// if we let wcwidth (which returns -1 instead of 1)
// calculate the size for new line characters.
// That is correct from a Unicode perspective, but the
// algorithm was written before wcwidth was added and used
// to work with string length and not cell length.
if (rune == '\n')
{
return 1;
}
return UnicodeCalculator.GetWidth(rune);
});
}

View File

@@ -60,6 +60,26 @@ namespace Spectre.Console.Rendering
/// </summary>
HeaderBottomRight,
/// <summary>
/// The top left part of a footer.
/// </summary>
FooterTopLeft,
/// <summary>
/// The top part of a footer.
/// </summary>
FooterTop,
/// <summary>
/// The top separator part of a footer.
/// </summary>
FooterTopSeparator,
/// <summary>
/// The top right part of a footer.
/// </summary>
FooterTopRight,
/// <summary>
/// The left part of a cell.
/// </summary>

View File

@@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "|",
TableBorderPart.CellSeparator => "|",
TableBorderPart.CellRight => "|",
TableBorderPart.FooterTopLeft => "|",
TableBorderPart.FooterTop => "-",
TableBorderPart.FooterTopSeparator => "+",
TableBorderPart.FooterTopRight => "|",
TableBorderPart.FooterBottomLeft => "+",
TableBorderPart.FooterBottom => "-",
TableBorderPart.FooterBottomSeparator => "+",

View File

@@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "|",
TableBorderPart.CellSeparator => "|",
TableBorderPart.CellRight => "|",
TableBorderPart.FooterTopLeft => "+",
TableBorderPart.FooterTop => "-",
TableBorderPart.FooterTopSeparator => "+",
TableBorderPart.FooterTopRight => "+",
TableBorderPart.FooterBottomLeft => "+",
TableBorderPart.FooterBottom => "-",
TableBorderPart.FooterBottomSeparator => "+",

View File

@@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "|",
TableBorderPart.CellSeparator => "|",
TableBorderPart.CellRight => "|",
TableBorderPart.FooterTopLeft => "|",
TableBorderPart.FooterTop => "-",
TableBorderPart.FooterTopSeparator => "+",
TableBorderPart.FooterTopRight => "|",
TableBorderPart.FooterBottomLeft => "+",
TableBorderPart.FooterBottom => "-",
TableBorderPart.FooterBottomSeparator => "-",

View File

@@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "║",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => "║",
TableBorderPart.FooterTopLeft => "╟",
TableBorderPart.FooterTop => "─",
TableBorderPart.FooterTopSeparator => "┼",
TableBorderPart.FooterTopRight => "╢",
TableBorderPart.FooterBottomLeft => "╚",
TableBorderPart.FooterBottom => "═",
TableBorderPart.FooterBottomSeparator => "╧",

View File

@@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "║",
TableBorderPart.CellSeparator => "║",
TableBorderPart.CellRight => "║",
TableBorderPart.FooterTopLeft => "╠",
TableBorderPart.FooterTop => "═",
TableBorderPart.FooterTopSeparator => "╬",
TableBorderPart.FooterTopRight => "╣",
TableBorderPart.FooterBottomLeft => "╚",
TableBorderPart.FooterBottom => "═",
TableBorderPart.FooterBottomSeparator => "╩",

View File

@@ -29,6 +29,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "┃",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => "┃",
TableBorderPart.FooterTopLeft => "┠",
TableBorderPart.FooterTop => "─",
TableBorderPart.FooterTopSeparator => "┼",
TableBorderPart.FooterTopRight => "┨",
TableBorderPart.FooterBottomLeft => "┗",
TableBorderPart.FooterBottom => "━",
TableBorderPart.FooterBottomSeparator => "┷",

View File

@@ -29,6 +29,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "│",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => "│",
TableBorderPart.FooterTopLeft => "├",
TableBorderPart.FooterTop => "─",
TableBorderPart.FooterTopSeparator => "┼",
TableBorderPart.FooterTopRight => "┤",
TableBorderPart.FooterBottomLeft => "└",
TableBorderPart.FooterBottom => "─",
TableBorderPart.FooterBottomSeparator => "┴",

View File

@@ -29,6 +29,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "┃",
TableBorderPart.CellSeparator => "┃",
TableBorderPart.CellRight => "┃",
TableBorderPart.FooterTopLeft => "┣",
TableBorderPart.FooterTop => "━",
TableBorderPart.FooterTopSeparator => "╋",
TableBorderPart.FooterTopRight => "┫",
TableBorderPart.FooterBottomLeft => "┗",
TableBorderPart.FooterBottom => "━",
TableBorderPart.FooterBottomSeparator => "┻",

View File

@@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => " ",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterTopLeft => "─",
TableBorderPart.FooterTop => "─",
TableBorderPart.FooterTopSeparator => "─",
TableBorderPart.FooterTopRight => "─",
TableBorderPart.FooterBottomLeft => "─",
TableBorderPart.FooterBottom => "─",
TableBorderPart.FooterBottomSeparator => "─",

View File

@@ -29,6 +29,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "|",
TableBorderPart.CellSeparator => "|",
TableBorderPart.CellRight => "|",
TableBorderPart.FooterTopLeft => " ",
TableBorderPart.FooterTop => " ",
TableBorderPart.FooterTopSeparator => " ",
TableBorderPart.FooterTopRight => " ",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",
@@ -40,7 +44,12 @@ namespace Spectre.Console.Rendering
/// <inheritdoc/>
public override string GetColumnRow(TablePart part, IReadOnlyList<int> widths, IReadOnlyList<IColumn> columns)
{
if (part != TablePart.Separator)
if (part == TablePart.FooterSeparator)
{
return string.Empty;
}
if (part != TablePart.HeaderSeparator)
{
return base.GetColumnRow(part, widths, columns);
}

View File

@@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterTopLeft => " ",
TableBorderPart.FooterTop => "═",
TableBorderPart.FooterTopSeparator => "╪",
TableBorderPart.FooterTopRight => " ",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",

View File

@@ -29,6 +29,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterTopLeft => " ",
TableBorderPart.FooterTop => "━",
TableBorderPart.FooterTopSeparator => "┿",
TableBorderPart.FooterTopRight => " ",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",

View File

@@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterTopLeft => " ",
TableBorderPart.FooterTop => "─",
TableBorderPart.FooterTopSeparator => "┼",
TableBorderPart.FooterTopRight => " ",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",

View File

@@ -29,6 +29,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "│",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => "│",
TableBorderPart.FooterTopLeft => "├",
TableBorderPart.FooterTop => "─",
TableBorderPart.FooterTopSeparator => "┼",
TableBorderPart.FooterTopRight => "┤",
TableBorderPart.FooterBottomLeft => "╰",
TableBorderPart.FooterBottom => "─",
TableBorderPart.FooterBottomSeparator => "┴",

View File

@@ -29,6 +29,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => " ",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterTopLeft => "━",
TableBorderPart.FooterTop => "━",
TableBorderPart.FooterTopSeparator => "━",
TableBorderPart.FooterTopRight => "━",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",

View File

@@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => " ",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterTopLeft => "─",
TableBorderPart.FooterTop => "─",
TableBorderPart.FooterTopSeparator => "─",
TableBorderPart.FooterTopRight => "─",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",

View File

@@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "│",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => "│",
TableBorderPart.FooterTopLeft => "├",
TableBorderPart.FooterTop => "─",
TableBorderPart.FooterTopSeparator => "┼",
TableBorderPart.FooterTopRight => "┤",
TableBorderPart.FooterBottomLeft => "└",
TableBorderPart.FooterBottom => "─",
TableBorderPart.FooterBottomSeparator => "┴",

View File

@@ -27,6 +27,12 @@ namespace Spectre.Console.Rendering
/// </summary>
public Justify? Justification { get; }
/// <summary>
/// Gets a value indicating whether the context want items to render without
/// line breaks and return a single line where applicable.
/// </summary>
internal bool SingleLine { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RenderContext"/> class.
/// </summary>
@@ -34,21 +40,42 @@ namespace Spectre.Console.Rendering
/// <param name="legacyConsole">A value indicating whether or not this a legacy console (i.e. cmd.exe).</param>
/// <param name="justification">The justification to use when rendering.</param>
public RenderContext(Encoding encoding, bool legacyConsole, Justify? justification = null)
: this(encoding, legacyConsole, justification, false)
{
}
private RenderContext(Encoding encoding, bool legacyConsole, Justify? justification = null, bool singleLine = false)
{
Encoding = encoding ?? throw new System.ArgumentNullException(nameof(encoding));
LegacyConsole = legacyConsole;
Justification = justification;
Unicode = Encoding == Encoding.UTF8 || Encoding == Encoding.Unicode;
SingleLine = singleLine;
}
/// <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>
/// <returns>A new <see cref="RenderContext"/> instance.</returns>
public RenderContext WithJustification(Justify? justification)
{
return new RenderContext(Encoding, LegacyConsole, justification);
}
/// <summary>
/// Creates a new context that tell <see cref="IRenderable"/> instances
/// to not care about splitting things in new lines. Whether or not to
/// comply to the request is up to the item being rendered.
/// </summary>
/// <remarks>
/// Use with care since this has the potential to mess things up.
/// Only use this kind of context with items that you know about.
/// </remarks>
/// <returns>A new <see cref="RenderContext"/> instance.</returns>
internal RenderContext WithSingleLine()
{
return new RenderContext(Encoding, LegacyConsole, Justification, true);
}
}
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Spectre.Console.Internal;
namespace Spectre.Console.Rendering
@@ -66,12 +67,7 @@ namespace Spectre.Console.Rendering
private Segment(string text, Style style, bool lineBreak)
{
if (text is null)
{
throw new ArgumentNullException(nameof(text));
}
Text = text.NormalizeLineEndings();
Text = text?.NormalizeLineEndings() ?? throw new ArgumentNullException(nameof(text));
Style = style ?? throw new ArgumentNullException(nameof(style));
IsLineBreak = lineBreak;
IsWhiteSpace = string.IsNullOrWhiteSpace(text);
@@ -83,11 +79,37 @@ namespace Spectre.Console.Rendering
/// </summary>
/// <param name="context">The render context.</param>
/// <returns>The number of cells that this segment occupies in the console.</returns>
public int CellLength(RenderContext context)
public int CellCount(RenderContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
return Text.CellLength(context);
}
/// <summary>
/// Gets the number of cells that the segments occupies in the console.
/// </summary>
/// <param name="context">The render context.</param>
/// <param name="segments">The segments to measure.</param>
/// <returns>The number of cells that the segments occupies in the console.</returns>
public static int CellCount(RenderContext context, IEnumerable<Segment> segments)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
if (segments is null)
{
throw new ArgumentNullException(nameof(segments));
}
return segments.Sum(segment => segment.CellCount(context));
}
/// <summary>
/// Returns a new segment without any trailing line endings.
/// </summary>
@@ -119,24 +141,50 @@ namespace Spectre.Console.Rendering
new Segment(Text.Substring(offset, Text.Length - offset), Style));
}
/// <summary>
/// Clones the segment.
/// </summary>
/// <returns>A new segment that's identical to this one.</returns>
public Segment Clone()
{
return new Segment(Text, Style);
}
/// <summary>
/// Splits the provided segments into lines.
/// </summary>
/// <param name="context">The render context.</param>
/// <param name="segments">The segments to split.</param>
/// <returns>A collection of lines.</returns>
public static List<SegmentLine> SplitLines(IEnumerable<Segment> segments)
public static List<SegmentLine> SplitLines(RenderContext context, IEnumerable<Segment> segments)
{
return SplitLines(segments, int.MaxValue);
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
if (segments is null)
{
throw new ArgumentNullException(nameof(segments));
}
return SplitLines(context, segments, int.MaxValue);
}
/// <summary>
/// Splits the provided segments into lines with a maximum width.
/// </summary>
/// <param name="context">The render context.</param>
/// <param name="segments">The segments to split into lines.</param>
/// <param name="maxWidth">The maximum width.</param>
/// <returns>A list of lines.</returns>
public static List<SegmentLine> SplitLines(IEnumerable<Segment> segments, int maxWidth)
public static List<SegmentLine> SplitLines(RenderContext context, IEnumerable<Segment> segments, int maxWidth)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
if (segments is null)
{
throw new ArgumentNullException(nameof(segments));
@@ -151,9 +199,10 @@ namespace Spectre.Console.Rendering
{
var segment = stack.Pop();
if (line.Width + segment.Text.Length > maxWidth)
// Does this segment make the line exceed the max width?
if (line.CellCount(context) + segment.CellCount(context) > maxWidth)
{
var diff = -(maxWidth - (line.Width + segment.Text.Length));
var diff = -(maxWidth - (line.Length + segment.Text.Length));
var offset = segment.Text.Length - diff;
var (first, second) = segment.Split(offset);
@@ -170,11 +219,13 @@ namespace Spectre.Console.Rendering
continue;
}
// Does the segment contain a newline?
if (segment.Text.Contains("\n"))
{
// Is it a new line?
if (segment.Text == "\n")
{
if (line.Width > 0 || segment.IsLineBreak)
if (line.Length != 0 || segment.IsLineBreak)
{
lines.Add(line);
line = new SegmentLine();
@@ -197,7 +248,7 @@ namespace Spectre.Console.Rendering
if (parts.Length > 1)
{
if (line.Width > 0)
if (line.Length > 0)
{
lines.Add(line);
line = new SegmentLine();
@@ -225,8 +276,180 @@ namespace Spectre.Console.Rendering
return lines;
}
/// <summary>
/// Splits an overflowing segment into several new segments.
/// </summary>
/// <param name="segment">The segment to split.</param>
/// <param name="overflow">The overflow strategy to use.</param>
/// <param name="context">The render context.</param>
/// <param name="maxWidth">The maximum width.</param>
/// <returns>A list of segments that has been split.</returns>
public static List<Segment> SplitOverflow(Segment segment, Overflow? overflow, RenderContext context, int maxWidth)
{
if (segment is null)
{
throw new ArgumentNullException(nameof(segment));
}
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
if (segment.CellCount(context) <= maxWidth)
{
return new List<Segment>(1) { segment };
}
// Default to folding
overflow ??= Overflow.Fold;
var result = new List<Segment>();
if (overflow == Overflow.Fold)
{
var totalLength = segment.Text.CellLength(context);
var lengthLeft = totalLength;
while (lengthLeft > 0)
{
var index = totalLength - lengthLeft;
// How many characters should we take?
var take = Math.Min(maxWidth, totalLength - index);
if (take <= 0)
{
// This shouldn't really occur, but I don't like
// never ending loops if it does...
return new List<Segment>();
}
result.Add(new Segment(segment.Text.Substring(index, take), segment.Style));
lengthLeft -= take;
}
}
else if (overflow == Overflow.Crop)
{
if (Math.Max(0, maxWidth - 1) == 0)
{
result.Add(new Segment(string.Empty, segment.Style));
}
else
{
result.Add(new Segment(segment.Text.Substring(0, maxWidth), segment.Style));
}
}
else if (overflow == Overflow.Ellipsis)
{
if (Math.Max(0, maxWidth - 1) == 0)
{
result.Add(new Segment("…", segment.Style));
}
else
{
result.Add(new Segment(segment.Text.Substring(0, maxWidth - 1) + "…", segment.Style));
}
}
return result;
}
/// <summary>
/// Truncates the segments to the specified width.
/// </summary>
/// <param name="context">The render context.</param>
/// <param name="segments">The segments to truncate.</param>
/// <param name="maxWidth">The maximum width that the segments may occupy.</param>
/// <returns>A list of segments that has been truncated.</returns>
public static List<Segment> Truncate(RenderContext context, IEnumerable<Segment> segments, int maxWidth)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
if (segments is null)
{
throw new ArgumentNullException(nameof(segments));
}
var result = new List<Segment>();
var totalWidth = 0;
foreach (var segment in segments)
{
var segmentCellWidth = segment.CellCount(context);
if (totalWidth + segmentCellWidth > maxWidth)
{
break;
}
result.Add(segment);
totalWidth += segmentCellWidth;
}
if (result.Count == 0 && segments.Any())
{
var segment = Truncate(context, segments.First(), maxWidth);
if (segment != null)
{
result.Add(segment);
}
}
return result;
}
/// <summary>
/// Truncates the segment to the specified width.
/// </summary>
/// <param name="context">The render context.</param>
/// <param name="segment">The segment to truncate.</param>
/// <param name="maxWidth">The maximum width that the segment may occupy.</param>
/// <returns>A new truncated segment, or <c>null</c>.</returns>
public static Segment? Truncate(RenderContext context, Segment segment, int maxWidth)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
if (segment is null)
{
return null;
}
if (segment.CellCount(context) <= maxWidth)
{
return segment;
}
var builder = new StringBuilder();
foreach (var character in segment.Text)
{
var accumulatedCellWidth = builder.ToString().CellLength(context);
if (accumulatedCellWidth >= maxWidth)
{
break;
}
builder.Append(character);
}
if (builder.Length == 0)
{
return null;
}
return new Segment(builder.ToString(), segment.Style);
}
internal static IEnumerable<Segment> Merge(IEnumerable<Segment> segments)
{
if (segments is null)
{
throw new ArgumentNullException(nameof(segments));
}
var result = new List<Segment>();
var previous = (Segment?)null;
@@ -258,75 +481,35 @@ namespace Spectre.Console.Rendering
return result;
}
/// <summary>
/// Clones the segment.
/// </summary>
/// <returns>A new segment that's identical to this one.</returns>
public Segment Clone()
{
return new Segment(Text, Style);
}
/// <summary>
/// Splits an overflowing segment into several new segments.
/// </summary>
/// <param name="segment">The segment to split.</param>
/// <param name="overflow">The overflow strategy to use.</param>
/// <param name="context">The render context.</param>
/// <param name="width">The maximum width.</param>
/// <returns>A list of segments that has been split.</returns>
public static List<Segment> SplitOverflow(Segment segment, Overflow? overflow, RenderContext context, int width)
{
if (segment is null)
{
throw new ArgumentNullException(nameof(segment));
}
if (segment.CellLength(context) <= width)
{
return new List<Segment>(1) { segment };
}
// Default to folding
overflow ??= Overflow.Fold;
var result = new List<Segment>();
if (overflow == Overflow.Fold)
{
var totalLength = segment.Text.CellLength(context);
var lengthLeft = totalLength;
while (lengthLeft > 0)
{
var index = totalLength - lengthLeft;
// How many characters should we take?
var take = Math.Min(width, totalLength - index);
if (take <= 0)
{
// This shouldn't really occur, but I don't like
// never ending loops if it does...
return new List<Segment>();
}
result.Add(new Segment(segment.Text.Substring(index, take), segment.Style));
lengthLeft -= take;
}
}
else if (overflow == Overflow.Crop)
{
result.Add(new Segment(segment.Text.Substring(0, width), segment.Style));
}
else if (overflow == Overflow.Ellipsis)
{
result.Add(new Segment(segment.Text.Substring(0, width - 1) + "…", segment.Style));
}
return result;
}
internal static Segment TruncateWithEllipsis(string text, Style style, RenderContext context, int maxWidth)
{
if (text is null)
{
throw new ArgumentNullException(nameof(text));
}
if (style is null)
{
throw new ArgumentNullException(nameof(style));
}
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
var overflow = SplitOverflow(new Segment(text, style), Overflow.Ellipsis, context, maxWidth);
if (overflow.Count == 0)
{
if (maxWidth > 0)
{
return new Segment(text, style);
}
// We got space for an ellipsis
return new Segment("…", style);
}
return SplitOverflow(
new Segment(text, style),
Overflow.Ellipsis,
@@ -334,8 +517,68 @@ namespace Spectre.Console.Rendering
maxWidth)[0];
}
internal static List<Segment> TruncateWithEllipsis(IEnumerable<Segment> segments, RenderContext context, int maxWidth)
{
if (segments is null)
{
throw new ArgumentNullException(nameof(segments));
}
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
if (CellCount(context, segments) <= maxWidth)
{
return new List<Segment>(segments);
}
segments = TrimEnd(Truncate(context, segments, maxWidth - 1));
if (!segments.Any())
{
return new List<Segment>(1);
}
var result = new List<Segment>(segments);
result.Add(new Segment("…", result.Last().Style));
return result;
}
internal static List<Segment> TrimEnd(IEnumerable<Segment> segments)
{
if (segments is null)
{
throw new ArgumentNullException(nameof(segments));
}
var stack = new Stack<Segment>();
var checkForWhitespace = true;
foreach (var segment in segments.Reverse())
{
if (checkForWhitespace)
{
if (segment.IsWhiteSpace)
{
continue;
}
checkForWhitespace = false;
}
stack.Push(segment);
}
return stack.ToList();
}
internal static List<List<SegmentLine>> MakeSameHeight(int cellHeight, List<List<SegmentLine>> cells)
{
if (cells is null)
{
throw new ArgumentNullException(nameof(cells));
}
foreach (var cell in cells)
{
if (cell.Count < cellHeight)

View File

@@ -13,16 +13,21 @@ namespace Spectre.Console.Rendering
/// <summary>
/// Gets the width of the line.
/// </summary>
public int Width => this.Sum(line => line.Text.Length);
public int Length => this.Sum(line => line.Text.Length);
/// <summary>
/// Gets the cell width of the segment line.
/// Gets the number of cells the segment line occupies.
/// </summary>
/// <param name="context">The render context.</param>
/// <returns>The cell width of the segment line.</returns>
public int CellWidth(RenderContext context)
public int CellCount(RenderContext context)
{
return this.Sum(line => line.CellLength(context));
if (context is null)
{
throw new System.ArgumentNullException(nameof(context));
}
return Segment.CellCount(context, this);
}
/// <summary>
@@ -31,6 +36,11 @@ namespace Spectre.Console.Rendering
/// <param name="segment">The segment to prepend.</param>
public void Prepend(Segment segment)
{
if (segment is null)
{
throw new System.ArgumentNullException(nameof(segment));
}
Insert(0, segment);
}
}

View File

@@ -13,7 +13,12 @@ namespace Spectre.Console.Rendering
/// <summary>
/// The separator between the header and the cells.
/// </summary>
Separator,
HeaderSeparator,
/// <summary>
/// The separator between the footer and the cells.
/// </summary>
FooterSeparator,
/// <summary>
/// The bottom of a table.

View File

@@ -61,6 +61,7 @@ namespace Spectre.Console
/// </summary>
/// <param name="color">The foreground color.</param>
/// <returns>A new <see cref="Style"/> with the specified foreground color.</returns>
[Obsolete("Use ctor(..) instead")]
public static Style WithForeground(Color color)
{
return new Style(foreground: color);
@@ -71,6 +72,7 @@ namespace Spectre.Console
/// </summary>
/// <param name="color">The background color.</param>
/// <returns>A new <see cref="Style"/> with the specified background color.</returns>
[Obsolete("Use ctor(..) instead")]
public static Style WithBackground(Color color)
{
return new Style(background: color);
@@ -81,6 +83,7 @@ namespace Spectre.Console
/// </summary>
/// <param name="decoration">The text decoration.</param>
/// <returns>A new <see cref="Style"/> with the specified text decoration.</returns>
[Obsolete("Use ctor(..) instead")]
public static Style WithDecoration(Decoration decoration)
{
return new Style(decoration: decoration);
@@ -91,6 +94,7 @@ namespace Spectre.Console
/// </summary>
/// <param name="link">The link.</param>
/// <returns>A new <see cref="Style"/> with the specified link.</returns>
[Obsolete("Use ctor(..) instead")]
public static Style WithLink(string link)
{
return new Style(link: link);
@@ -143,7 +147,7 @@ namespace Spectre.Console
/// </summary>
/// <param name="style">The style string.</param>
[SuppressMessage("Usage", "CA2225:Operator overloads have named alternates")]
public static explicit operator Style(string style)
public static implicit operator Style(string style)
{
return Parse(style);
}

View File

@@ -83,10 +83,15 @@ namespace Spectre.Console
GetPart(TableBorderPart.HeaderTopSeparator), GetPart(TableBorderPart.HeaderTopRight)),
// Separator between header and cells
TablePart.Separator =>
TablePart.HeaderSeparator =>
(GetPart(TableBorderPart.HeaderBottomLeft), GetPart(TableBorderPart.HeaderBottom),
GetPart(TableBorderPart.HeaderBottomSeparator), GetPart(TableBorderPart.HeaderBottomRight)),
// Separator between footer and cells
TablePart.FooterSeparator =>
(GetPart(TableBorderPart.FooterTopLeft), GetPart(TableBorderPart.FooterTop),
GetPart(TableBorderPart.FooterTopSeparator), GetPart(TableBorderPart.FooterTopRight)),
// Bottom part
TablePart.Bottom =>
(GetPart(TableBorderPart.FooterBottomLeft), GetPart(TableBorderPart.FooterBottom),

View File

@@ -11,7 +11,7 @@ namespace Spectre.Console
/// <summary>
/// A renderable calendar.
/// </summary>
public sealed class Calendar : Renderable, IHasCulture, IHasTableBorder
public sealed class Calendar : Renderable, IHasCulture, IHasTableBorder, IAlignable
{
private const int NumberOfWeekDays = 7;
private const int ExpectedRowCount = 6;
@@ -28,6 +28,9 @@ namespace Spectre.Console
private bool _dirty;
private CultureInfo _culture;
private Style _highlightStyle;
private bool _showHeader;
private Style? _headerStyle;
private Justify? _alignment;
/// <summary>
/// Gets or sets the calendar year.
@@ -95,6 +98,31 @@ namespace Spectre.Console
set => MarkAsDirty(() => _highlightStyle = value);
}
/// <summary>
/// Gets or sets a value indicating whether or not the calendar header should be shown.
/// </summary>
public bool ShowHeader
{
get => _showHeader;
set => MarkAsDirty(() => _showHeader = value);
}
/// <summary>
/// Gets or sets the header style.
/// </summary>
public Style? HeaderStyle
{
get => _headerStyle;
set => MarkAsDirty(() => _headerStyle = value);
}
/// <inheritdoc/>
public Justify? Alignment
{
get => _alignment;
set => MarkAsDirty(() => _alignment = value);
}
/// <summary>
/// Gets a list containing all calendar events.
/// </summary>
@@ -137,6 +165,7 @@ namespace Spectre.Console
_dirty = true;
_culture = CultureInfo.InvariantCulture;
_highlightStyle = new Style(foreground: Color.Blue);
_showHeader = true;
_calendarEvents = new ListWithCallback<CalendarEvent>(() => _dirty = true);
}
@@ -174,8 +203,15 @@ namespace Spectre.Console
Border = _border,
UseSafeBorder = _useSafeBorder,
BorderStyle = _borderStyle,
Alignment = _alignment,
};
if (ShowHeader)
{
var heading = new DateTime(Year, Month, Day).ToString("Y", culture).EscapeMarkup();
table.Title = new TableTitle(heading, HeaderStyle);
}
// Add columns
foreach (var order in GetWeekdays())
{

View File

@@ -25,7 +25,16 @@ namespace Spectre.Console
/// <summary>
/// Initializes a new instance of the <see cref="Columns"/> class.
/// </summary>
/// <param name="items">The items to render.</param>
/// <param name="items">The items to render as columns.</param>
public Columns(params IRenderable[] items)
: this((IEnumerable<IRenderable>)items)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Columns"/> class.
/// </summary>
/// <param name="items">The items to render as columns.</param>
public Columns(IEnumerable<IRenderable> items)
{
if (items is null)
@@ -57,10 +66,15 @@ namespace Spectre.Console
var itemWidths = _items.Select(item => item.Measure(context, maxWidth).Max).ToArray();
var columnCount = CalculateColumnCount(maxWidth, itemWidths, _items.Count, maxPadding);
if (columnCount == 0)
{
// Temporary work around for extremely small consoles
return new Measurement(maxWidth, maxWidth);
}
var rows = _items.Count / columnCount;
var rows = _items.Count / Math.Max(columnCount, 1);
var greatestWidth = 0;
for (var row = 0; row < rows; row += columnCount)
for (var row = 0; row < rows; row += Math.Max(1, columnCount))
{
var widths = itemWidths.Skip(row * columnCount).Take(columnCount).ToList();
var totalWidth = widths.Sum() + (maxPadding * (widths.Count - 1));
@@ -80,6 +94,11 @@ namespace Spectre.Console
var itemWidths = _items.Select(item => item.Measure(context, maxWidth).Max).ToArray();
var columnCount = CalculateColumnCount(maxWidth, itemWidths, _items.Count, maxPadding);
if (columnCount == 0)
{
// Temporary work around for extremely small consoles
columnCount = 1;
}
var table = new Table();
table.NoBorder();

View File

@@ -7,7 +7,7 @@ namespace Spectre.Console
/// <summary>
/// A renderable grid.
/// </summary>
public sealed class Grid : Renderable, IExpandable
public sealed class Grid : Renderable, IExpandable, IAlignable
{
private readonly Table _table;
@@ -28,6 +28,13 @@ namespace Spectre.Console
set => _table.Expand = value;
}
/// <inheritdoc/>
public Justify? Alignment
{
get => _table.Alignment;
set => _table.Alignment = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="Grid"/> class.
/// </summary>

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using Spectre.Console.Internal;
using Spectre.Console.Rendering;
@@ -46,5 +47,20 @@ namespace Spectre.Console
{
return ((IRenderable)_paragraph).Render(context, maxWidth);
}
/// <summary>
/// Escapes text so that it wont be interpreted as markup.
/// </summary>
/// <param name="text">The text to escape.</param>
/// <returns>A string that is safe to use in markup.</returns>
public static string Escape(string text)
{
if (text is null)
{
throw new ArgumentNullException(nameof(text));
}
return text.EscapeMarkup();
}
}
}

View File

@@ -66,7 +66,7 @@ namespace Spectre.Console
}
var child = _child.Render(context, maxWidth - paddingWidth);
foreach (var (_, _, _, line) in Segment.SplitLines(child).Enumerate())
foreach (var (_, _, _, line) in Segment.SplitLines(context, child).Enumerate())
{
// Left padding
if (Padding.Left != 0)
@@ -83,7 +83,7 @@ namespace Spectre.Console
}
// Missing space on right side?
var lineWidth = line.CellWidth(context);
var lineWidth = line.CellCount(context);
var diff = width - lineWidth - Padding.Left - Padding.Right;
if (diff > 0)
{

View File

@@ -72,7 +72,7 @@ namespace Spectre.Console
/// <inheritdoc/>
protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth)
{
var border = Border.GetSafeBorder((context.LegacyConsole || !context.Unicode) && UseSafeBorder);
var border = BoxExtensions.GetSafeBorder(Border, (context.LegacyConsole || !context.Unicode) && UseSafeBorder);
var borderStyle = BorderStyle ?? Style.Plain;
var child = new Padder(_child, Padding);
@@ -94,7 +94,7 @@ namespace Spectre.Console
// Split the child segments into lines.
var childSegments = ((IRenderable)child).Render(context, childWidth);
foreach (var line in Segment.SplitLines(childSegments, childWidth))
foreach (var line in Segment.SplitLines(context, childSegments, childWidth))
{
if (line.Count == 1 && line[0].IsWhiteSpace)
{
@@ -109,7 +109,7 @@ namespace Spectre.Console
content.AddRange(line);
// Do we need to pad the panel?
var length = line.Sum(segment => segment.CellLength(context));
var length = line.Sum(segment => segment.CellCount(context));
if (length < childWidth)
{
var diff = childWidth - length;
@@ -148,7 +148,7 @@ namespace Spectre.Console
var headerWidth = panelWidth - (EdgeWidth * 2);
var header = Segment.TruncateWithEllipsis(Header.Text, Header.Style ?? borderStyle, context, headerWidth);
var excessWidth = headerWidth - header.CellLength(context);
var excessWidth = headerWidth - header.CellCount(context);
if (excessWidth > 0)
{
switch (Header.Alignment ?? Justify.Left)

Some files were not shown because too many files have changed in this diff Show More