Zum Inhalt
Kuonen.UI

Terminal

Ein Terminal wie in PuTTY oder Windows Terminal: Zellraster mit Farben (ANSI, 256, RGB), Merkmalen, breiten Zeichen und Rückblick, ein blinkender Cursor, Auswahl (Doppelklick: Wort, Dreifachklick: Zeile, Alt+Ziehen: Rechteck), Kopieren und Einfügen. Die Steuerfolgen deutet die Anwendung; hier antwortet eine kleine Kommandozeile: „hilfe“, „farben“, „breit“, „leer“.

Vorführung: TerminalVorführung: Terminal

KXAML

TerminalSeite.kxaml
<StackPanel xmlns="https://ui.kuonen.org/kxaml"
            xmlns:x="https://ui.kuonen.org/kxaml/x"
            x:Class="Kuonen.UI.Galerie.Seiten.TerminalVorfuehrung"
            Spacing="8">
  <StackPanel Orientation="Horizontal" Spacing="8">
    <ComboBox x:Name="Bedienung" MinWidth="120" />
    <CheckBox x:Name="Hell" Content="Hell" />
  </StackPanel>
  <TerminalView x:Name="Terminal" Height="320" />
</StackPanel>
TerminalSeite.kxaml.cs
using Kuonen.UI;

namespace Kuonen.UI.Galerie.Seiten;

public sealed partial class TerminalSeite
{
    protected override Func<UIElement>? KxamlFabrik => () => new TerminalVorfuehrung();
}

/// <summary>KXAML-Fassung der Seite Terminal: der Aufbau steht in TerminalSeite.kxaml, hier das Verhalten.</summary>
public sealed partial class TerminalVorfuehrung
{
    public TerminalVorfuehrung()
    {
        InitializeComponent();
        TerminalSeite.Verdrahte(Terminal, Bedienung, Hell);
    }
}

C#

TerminalSeite.cs
using Kuonen.UI;
using Kuonen.UI.Controls;
using Kuonen.UI.Layout;

namespace Kuonen.UI.Galerie.Seiten;

/// <summary>Galerie-Seite: das Terminal (<see cref="TerminalView"/>) mit einer kleinen eingebauten Kommandozeile statt einer echten Gegenstelle.</summary>
public sealed partial class TerminalSeite : GalerieSeite
{
    public override string Titel => "Terminal";

    public override string Beschreibung =>
        "Ein Terminal wie in PuTTY oder Windows Terminal: Zellraster mit Farben (ANSI, 256, RGB), Merkmalen, breiten Zeichen und Rückblick, ein blinkender "
        + "Cursor, Auswahl (Doppelklick: Wort, Dreifachklick: Zeile, Alt+Ziehen: Rechteck), Kopieren und Einfügen. Die Steuerfolgen deutet die Anwendung; "
        + "hier antwortet eine kleine Kommandozeile: „hilfe“, „farben“, „breit“, „leer“.";

    public override string Stichworte =>
        "TerminalView Terminal Konsole Console Shell PuTTY xterm VT100 ANSI SSH Telnet Emulator Rückblick Scrollback Cursor Auswahl Kopieren Einfügen";

    public override string Datei => nameof(TerminalSeite);

    /// <summary>Die Eingabeaufforderung der kleinen Kommandozeile.</summary>
    public const string Aufforderung = "kuonen> ";

    internal static readonly string[] Bedienungen = ["Modern", "PuTTY", "xterm"];

    /// <summary>Das Verhalten beider Fassungen: Bedienung und Schema wählen, die Kommandozeile antwortet auf die Eingaben.</summary>
    internal static void Verdrahte(TerminalView terminal, ComboBox bedienung, CheckBox hell)
    {
        var puffer = terminal.Puffer;
        var zeile = new System.Text.StringBuilder();
        Begruesse(puffer);
        bedienung.ItemsSource = Bedienungen;
        bedienung.SelectedIndex = 0;
        bedienung.SelectionChanged += (_, _) => terminal.Bedienung = (TerminalBedienung)Math.Max(0, bedienung.SelectedIndex);
        hell.Checked += (_, _) => terminal.Farbschema = TerminalFarbschema.Hell;
        hell.Unchecked += (_, _) => terminal.Farbschema = TerminalFarbschema.Dunkel;
        terminal.Senden += eingabe =>
        {
            using var _ = puffer.Aendern();
            foreach (var rune in eingabe.EnumerateRunes())
            {
                if (rune.Value == '\r')
                {
                    puffer.Schreibe("\r\n");
                    Fuehre(puffer, zeile.ToString().Trim());
                    zeile.Clear();
                    puffer.Schreibe(Aufforderung);
                }
                else if (rune.Value is 0x7F or '\b')
                {
                    if (zeile.Length == 0) continue;
                    zeile.Length -= char.IsLowSurrogate(zeile[^1]) ? 2 : 1;
                    puffer.Schreibe("\b \b");
                }
                else if (rune.Value >= ' ' && !System.Text.Rune.IsControl(rune))
                {
                    zeile.Append(rune.ToString());
                    puffer.SchreibeZeichen(rune.Value);
                }
            }
        };
    }

    private static void Begruesse(TerminalPuffer puffer)
    {
        puffer.Vordergrund = TerminalFarbe.Palette(10);
        puffer.Merkmale = TerminalMerkmal.Fett;
        puffer.Schreibe("Kuonen.UI Terminal\r\n");
        puffer.Merkmale = TerminalMerkmal.Keine;
        puffer.Vordergrund = TerminalFarbe.Vorgabe;
        puffer.Schreibe("Tippen Sie „hilfe“ und Enter.\r\n" + Aufforderung);
    }

    internal static void Fuehre(TerminalPuffer puffer, string befehl)
    {
        switch (befehl)
        {
            case "":
                break;
            case "hilfe":
                puffer.Schreibe("farben  die 16 ANSI-Farben, Würfel und Graustufen\r\nbreit   breite und kombinierende Zeichen\r\nleer    Bildschirm leeren\r\n");
                break;
            case "farben":
                for (int i = 0; i < 16; i++)
                {
                    puffer.Hintergrund = TerminalFarbe.Palette(i);
                    puffer.Schreibe("  ");
                }
                puffer.Hintergrund = TerminalFarbe.Vorgabe;
                puffer.Schreibe("\r\n");
                for (int i = 16; i < 256; i += 6)
                {
                    puffer.Hintergrund = TerminalFarbe.Palette(i);
                    puffer.Schreibe(" ");
                }
                puffer.Hintergrund = TerminalFarbe.Vorgabe;
                puffer.Schreibe("\r\n");
                foreach (var (merkmal, name) in new[] { (TerminalMerkmal.Fett, "fett"), (TerminalMerkmal.Kursiv, "kursiv"), (TerminalMerkmal.Unterstrichen, "unterstrichen"),
                             (TerminalMerkmal.Durchgestrichen, "durchgestrichen"), (TerminalMerkmal.Umgekehrt, "umgekehrt"), (TerminalMerkmal.Blinkend, "blinkend") })
                {
                    puffer.Merkmale = merkmal;
                    puffer.Schreibe(name);
                    puffer.Merkmale = TerminalMerkmal.Keine;
                    puffer.Schreibe(" ");
                }
                puffer.Schreibe("\r\n");
                break;
            case "breit":
                puffer.Schreibe("中文 日本語 한국어 😀 é ü\r\n");
                break;
            case "leer":
                puffer.LoescheZeilenBereich();
                puffer.SetzeCursor(0, 0);
                break;
            default:
                puffer.Vordergrund = TerminalFarbe.Palette(9);
                puffer.Schreibe($"Unbekannt: {befehl}\r\n");
                puffer.Vordergrund = TerminalFarbe.Vorgabe;
                break;
        }
    }

    public override UIElement Baue()
    {
        var terminal = new TerminalView { Height = 320 };
        var bedienung = new ComboBox { MinWidth = 120 };
        var hell = new CheckBox { Content = "Hell" };
        Verdrahte(terminal, bedienung, hell);
        var leiste = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 8, Children = { bedienung, hell } };
        return new StackPanel { Spacing = 8, Children = { leiste, terminal } };
    }
}