Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.7" />
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageVersion Include="Spectre.Console" Version="0.55.2" />
<PackageVersion Include="TestableIO.System.IO.Abstractions.TestingHelpers" Version="22.1.1" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="xunit.v3" Version="3.2.2" />
Expand Down
192 changes: 192 additions & 0 deletions Examples/UICatalog/Scenarios/SpectreViewScenario.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#nullable enable

using global::Spectre.Console;
using global::Spectre.Console.Rendering;
using Terminal.Gui.Interop.Spectre;
using SpectreColor = global::Spectre.Console.Color;

namespace UICatalog.Scenarios;

[ScenarioMetadata ("Spectre", "Demonstrates Spectre.Console integration.")]
[ScenarioCategory ("Controls")]
[ScenarioCategory ("Text and Formatting")]
public sealed class SpectreViewScenario : Scenario
{
public override void Main ()
{
ConfigurationManager.Enable (ConfigLocations.All);

using IApplication app = Application.Create ();
app.Init ();

using Window appWindow = new ();
appWindow.Title = GetQuitKeyAndName ();
appWindow.BorderStyle = LineStyle.None;

string [] labels = ["_Table", "_Panel", "_Rule", "_Tree", "_BarChart", "_Calendar", "_Figlet", "_Markup"];
OptionSelector selector = new ()
{
X = 0,
Y = 0,
Width = 36,
Height = Dim.Auto (),
BorderStyle = LineStyle.Rounded,
Labels = labels,
Value = 0,
Title = "_Spectre Widget"
};

TextField userInput = new ()
{
X = Pos.Right (selector) + 1,
Y = 0,
Width = 22,
Height = 1,
Text = "Alice"
};

Label userLabel = new ()
{
X = Pos.Left (userInput),
Y = Pos.Bottom (userInput),
Text = "_Name for sample data:"
};

CheckBox autoSizeCheckBox = new ()
{
X = Pos.Left (userInput),
Y = Pos.Bottom (userLabel),
Text = "_AutoSize content",
Value = CheckState.Checked
};

Button refreshButton = new ()
{
X = Pos.Left (userInput),
Y = Pos.Bottom (autoSizeCheckBox) + 1,
Text = "_Refresh"
};

FrameView previewFrame = new ()
{
X = 0,
Y = Pos.Bottom (selector) + 1,
Width = Dim.Fill (),
Height = Dim.Fill (),
Title = "Preview",
BorderStyle = LineStyle.Rounded
};

SpectreView spectreView = new ()
{
X = 0,
Y = 0,
Width = Dim.Fill (),
Height = Dim.Fill (),
AutoSize = true,
Renderable = CreateRenderable (labels [0], userInput.Text),
ViewportSettings = ViewportSettingsFlags.HasVerticalScrollBar | ViewportSettingsFlags.HasHorizontalScrollBar
};

previewFrame.Add (spectreView);
appWindow.Add (selector, userInput, userLabel, autoSizeCheckBox, refreshButton, previewFrame);

selector.ValueChanged += (_, args) =>
{
if (args.NewValue is null)
{
return;
}

string selectedLabel = labels [(int)args.NewValue];
spectreView.Renderable = CreateRenderable (selectedLabel, userInput.Text);
};

refreshButton.Accepted += (_, _) =>
{
string selectedLabel = labels [(int)(selector.Value ?? 0)];
spectreView.Renderable = CreateRenderable (selectedLabel, userInput.Text);
};

autoSizeCheckBox.ValueChanged += (_, args) =>
{
spectreView.AutoSize = args.NewValue == CheckState.Checked;
};

app.Run (appWindow);
}

private static IRenderable CreateRenderable (string selectedLabel, string userName)
{
string safeName = string.IsNullOrWhiteSpace (userName) ? "User" : userName;

switch (selectedLabel)
{
case "_Panel":
{
return new Panel ($"Welcome, {safeName}!\nThis is Spectre rendered in a Terminal.Gui view.")
{
Header = new PanelHeader ("Spectre Panel")
};
}

case "_Rule":
{
return new Rule ($"Spectre Rule for {safeName}");
}

case "_Tree":
{
Tree tree = new ("Project");
global::Spectre.Console.TreeNode src = tree.AddNode ("src");
src.AddNode ("App.cs");
src.AddNode ("SpectreView.cs");
tree.AddNode ("README.md");

return tree;
}

case "_BarChart":
{
BarChart chart = new ();
chart.Width (60);
chart.AddItem (safeName, 81, new SpectreColor (100, 149, 237));
chart.AddItem ("Average", 73, new SpectreColor (0, 250, 154));
chart.AddItem ("Target", 90, new SpectreColor (255, 165, 0));

return chart;
}

case "_Calendar":
{
DateTime now = DateTime.Now;
Calendar calendar = new (now.Year, now.Month);
calendar.HighlightStyle (new Style (SpectreColor.Black, SpectreColor.Yellow));

return calendar;
}

case "_Figlet":
{
return new FigletText (safeName).Centered ();
}

case "_Markup":
{
return new Markup ($"[bold aqua]{safeName}[/] uses [yellow]SpectreView[/] inside [green]Terminal.Gui[/].");
}

default:
{
Table table = new ();
table.Border = TableBorder.Rounded;
table.AddColumn ("Name");
table.AddColumn ("Score");
table.AddRow (safeName, "81");
table.AddRow ("Average", "73");

return table;
}
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions Examples/UICatalog/UICatalog.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<PackageReference Include="Terminal.Gui.Editor" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Terminal.Gui.Interop.Spectre\Terminal.Gui.Interop.Spectre.csproj" />
<ProjectReference Include="..\..\Terminal.Gui\Terminal.Gui.csproj" />
</ItemGroup>
<ItemGroup>
Expand Down
158 changes: 158 additions & 0 deletions Terminal.Gui.Interop.Spectre/SpectreMarkupBridge.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
using global::Spectre.Console;
using Terminal.Gui.Drawing;
using TgAttribute = Terminal.Gui.Drawing.Attribute;
using TgColor = Terminal.Gui.Drawing.Color;
using SpectreColor = global::Spectre.Console.Color;

namespace Terminal.Gui.Interop.Spectre;

/// <summary>
/// Converts between Spectre.Console styling and Terminal.Gui drawing attributes.
/// </summary>
public static class SpectreMarkupBridge
{
/// <summary>
/// Converts a Spectre <see cref="Style"/> to a Terminal.Gui <see cref="TgAttribute"/>.
/// </summary>
/// <param name="style">The Spectre style to convert.</param>
/// <returns>The converted Terminal.Gui attribute.</returns>
public static TgAttribute ToAttribute (this Style style)
{
TgColor foreground = SpectreColorToTg (style.Foreground);
TgColor background = SpectreColorToTg (style.Background);
TextStyle textStyle = DecorationToTextStyle (style.Decoration);

return new (foreground, background, textStyle);
}

/// <summary>
/// Converts a Terminal.Gui <see cref="TgAttribute"/> to a Spectre <see cref="Style"/>.
/// </summary>
/// <param name="attribute">The Terminal.Gui attribute to convert.</param>
/// <returns>The converted Spectre style.</returns>
public static Style ToSpectreStyle (this TgAttribute attribute)
{
SpectreColor foreground = TgColorToSpectre (attribute.Foreground);
SpectreColor background = TgColorToSpectre (attribute.Background);
Decoration decoration = TextStyleToDecoration (attribute.Style);

return new (foreground, background, decoration);
}

private static TgColor SpectreColorToTg (SpectreColor? color)
{
if (color is null)
{
return TgColor.None;
}

SpectreColor value = color.Value;

if (value == SpectreColor.Default)
{
return TgColor.None;
}

return new TgColor (value.R, value.G, value.B);
}

private static SpectreColor TgColorToSpectre (TgColor color)
{
if (color == TgColor.None)
{
return SpectreColor.Default;
}

return new SpectreColor ((byte)color.R, (byte)color.G, (byte)color.B);
}

private static TextStyle DecorationToTextStyle (Decoration? decoration)
{
if (decoration is null)
{
return TextStyle.None;
}

Decoration value = decoration.Value;
TextStyle style = TextStyle.None;

if ((value & Decoration.Bold) != 0)
{
style |= TextStyle.Bold;
}

if ((value & Decoration.Dim) != 0)
{
style |= TextStyle.Faint;
}

if ((value & Decoration.Italic) != 0)
{
style |= TextStyle.Italic;
}

if ((value & Decoration.Underline) != 0)
{
style |= TextStyle.Underline;
}

if ((value & Decoration.Invert) != 0)
{
style |= TextStyle.Reverse;
}

if ((value & (Decoration.SlowBlink | Decoration.RapidBlink)) != 0)
{
style |= TextStyle.Blink;
}

if ((value & Decoration.Strikethrough) != 0)
{
style |= TextStyle.Strikethrough;
}

return style;
}

private static Decoration TextStyleToDecoration (TextStyle style)
{
Decoration decoration = Decoration.None;

if ((style & TextStyle.Bold) != 0)
{
decoration |= Decoration.Bold;
}

if ((style & TextStyle.Faint) != 0)
{
decoration |= Decoration.Dim;
}

if ((style & TextStyle.Italic) != 0)
{
decoration |= Decoration.Italic;
}

if ((style & TextStyle.Underline) != 0)
{
decoration |= Decoration.Underline;
}

if ((style & TextStyle.Reverse) != 0)
{
decoration |= Decoration.Invert;
}

if ((style & TextStyle.Blink) != 0)
{
decoration |= Decoration.SlowBlink;
}

if ((style & TextStyle.Strikethrough) != 0)
{
decoration |= Decoration.Strikethrough;
}

return decoration;
}
}
Loading
Loading