GithubHelp home page GithubHelp logo

goblinfactory / konsole Goto Github PK

View Code? Open in Web Editor NEW
703.0 17.0 61.0 15.98 MB

Home of the simple console library consisting of ProgressBar, Window, Form, Draw & MockConsole (C# console progress bar with support for single or multithreaded progress updates) Window is a 100%-ish console compatible window, supporting all normal console writing to a windowed section of the screen, supporting scrolling and clipping of console output.

PowerShell 0.41% C# 99.58% Batchfile 0.01%
konsole progressbar window draw mockconsole csharp dotnet

konsole's Introduction

Konsole V6

Goblinfactory.Konsole NuGet Status nuget License Join the chat at https://gitter.im/goblinfactory-konsole/community

Low ceremony, Fluent DSL for writing console apps, utilities and spike projects. Providing thread safe progress bars, windows and forms and drawing for console applications. Build UX's as shown below in very few lines of code. Konsole provides simple threadsafe ways to write to the C# console window. See my notes on threading. The project is growing quickly with fast responses to issues raised.

If you have any questions on how to use Konsole, please join us on Gitter (https://gitter.im/goblinfactory-konsole/community?source=orgpage) and I'll be happy to help you.

Version 7 alpha release progress (see release notes for whats new)

date alpha release
11/2/21 (7.0.0.5-alpha)[https://www.nuget.org/packages/Goblinfactory.Konsole/7.0.0.5-alpha]
13/2/21 (7.0.0.7-alpha)[https://www.nuget.org/packages/Goblinfactory.Konsole/7.0.0.7-alpha]

enjoy, cheers,

Alan

sample demo using HighSpeedWriter

Contents : V6

Nuget Packages

Installing and Getting started

  1. start a new console application
dotnet new console -n myutility
  1. add Konsole package
dotnet add package Goblinfactory.Konsole
  1. add the code in the same shown below to your void main(string[] args) method
  2. run your program
dotnet run

Will give you the screenshot on the right. If not, please join our gitter chat and get some help.

have fun!

Alan

using Konsole.Internal;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using static System.ConsoleColor;

class Program
{
    static void Main(string[] args)
    {

        // quick dive in example 

        void Wait() => Console.ReadKey(true);

        // show how you can mix and match System.Console with Konsole
        Console.WriteLine("line one");

        // create an inline Box window at the current cursor position 
        // 20 characters wide, by 12 tall.
        // returns a Window that implements IConsole 
        // that you can use to write to the window 
        // and create new windows inside that window.
        
        var nyse = Window.OpenBox("NYSE", 20, 12, new BoxStyle() { 
            ThickNess = LineThickNess.Single, 
            Title = new Colors(White, Red) 
        });
        
        Console.WriteLine("line two");

        // create another inline Box window at the current cursor position
        var ftse100 = Window.OpenBox("FTSE 100", 20, 12, new BoxStyle() { 
            ThickNess = LineThickNess.Double, 
            Title = new Colors(White, Blue) 
        });
        Console.Write("line three");


        while(true) {
            Tick(nyse, "AMZ", amazon -= 0.04M, Red, '-', 4.1M);
            Tick(ftse100, "BP", bp += 0.05M, Green, '+', 7.2M);
            Wait();
        }

        decimal amazon = 84;
        decimal bp = 146;

        // simple method that takes a window and prints a stock price 
        // to that window in color
        void Tick(IConsole con, string sym, decimal newPrice, 
           ConsoleColor color, char sign, decimal perc) 
        {
            con.Write(White, $"{sym,-10}");
            con.WriteLine(color, $"{newPrice:0.00}");
            con.WriteLine(color, $"  ({sign}{newPrice}, {perc}%)");
            con.WriteLine("");
        }
    }
}

using static Console.ConsoleColor

If you will be using a lot of different colors throughout your application I recommend making use of the new C# static using language feature to make the code a bit easier to read.

before

Console.WriteLine(ConsoleColor.Red, "I am red"); 
var box = Window.OpenBox("warnings", new BoxStyle() { Title = new Colors(ConsoleColor.White, ConsoleColor.Red) })

becomes

using static System.Console;
...
Console.WriteLine(Red, "I am red"); 
var box = Window.OpenBox("warnings", new BoxStyle() { Title = new Colors(White, Red) })

IConsole

This is the main interface that all windows, and objects that wrap a window, or that wrap the System.Console writer. It implements the almost everything that System.Console does with some extra magic. IConsole is a well thought out .NET System.Console abstractions. Use to remove a direct dependancy on System.Console and replace with a dependancy on a well used and well known console interface, IConsole, to allow for building rich 'testable', high quality interactive console applications and utilities.

For more information about the different interfaces please see the full documentation for the contracts, as well as details of each interface here

Progress bars

ProgressBar

Create a threadsafe one or two line progress bar.

    var pb = new ProgressBar(PbStyle.DoubleLine, 50);
    pb.Refresh(0, "connecting to server to download 5 files asychronously.");
    Console.ReadLine();

    pb.Refresh(25, "downloading file number 25");
    Console.ReadLine();
    pb.Refresh(50, "finished.");

You can create a SingleLine or a DoubleLine progress bar. If none is specified, the a single line progressbar is created.

var pb1 = new ProgressBar(max);

ProgressBar worked parallel example

using Konsole.Internal;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

static void Main(string[] args) {

    var dirCnt = 15;
    var filesPerDir = 30;

    var r = new Random();
    var dirs = TestData.MakeObjectNames(dirCnt);

    Console.WriteLine("Press enter to start");

    var tasks = new List<Task>();
    var bars = new ConcurrentBag<ProgressBar>(); 
    foreach (var d in dirs)
    {
        var files = TestData.MakeNames(r.Next(filesPerDir));
        var bar = new ProgressBar(files.Count());
        bars.Add(bar);
        bar.Refresh(0, d);
        tasks.Add(ProcessFakeFiles(d, files, bar));
    }
    Console.ReadLine();
    start = true;
    Task.WaitAll(tasks.ToArray());
    Console.WriteLine("finished.");
    Console.ReadLine();
}    

DoubleLine progress bar

Double line progress bar is useful if you want to roll up and display the overall progress of a parent group, while displaying the names of the items being processed seperately. For example, when processing a number of folders and files inside folders, then use a DoubleLine ProgressBar.

var pb2 = new ProgressBar(PbStyle.DoubleLine, files.Count());

Open a progressbar inside a window

To open a progress bar inside a new window, just pass the window (IConsole) as the first parameter.

public static void Main(string[] args)
{
    var w = Window.OpenBox("tasks", 60, 8);
    var left = w.SplitLeft("files");
    var right = w.SplitRight("users");
    
    var pb1 = new ProgressBar(right, 100);
    pb1.Refresh(10, "Clint Eastwood");
 
    var pb2 = new ProgressBar(left, 100);
    pb2.Refresh(50, "hotel-california.mp3");


}

snippet source | anchor

Gives you

Threading and threadsafe writing to the Console.

If you have a background thread that writes to the screen, then you have to make sure that the thread code is threadsafe, with regards to the console. System.Console by default is not threadsafe. Use new ConcurrentWriter() to create a simple threadsafe writer that will write to the current console window. New Window is not threadsafe. Call .Concurrent() on a new window to return a thread safe window.

All the static constructors return threadsafe windows by default; So

Threadsafe static constructors

  • Window.Open
  • Window.OpenBox
  • Window.OpenInline
  • new ConcurrentWriter()
  • new Window().Concurrent()

new Window is not threadsafe

var myWindow new Window(...);

You can make an existing window instance safe by either calling .Concurrent() on an instance, or by only using that window as a region that is then Split using one of the extension methods, SplitTop, SplitBottom, SplitLeftRight etc. Those are static extension methods, and like the static constructors, they all return threadsafe instances wrapped in a new ConcurrentWriter().

Make it threadsafe

// create an 80 by 20 inline window
var window = new Window(80, 20);

// split that window into boxes
var left = window.SplitLeft("left");
var right = window.SplitRight("right");

// right and left are threadsafe, window is not.

var safewin = window.Concurrent();

// safewin is threadsafe, window is still NOT threadsafe.

safewin.WriteLine(Green, "This is threadsafe");

Debugging inside VSCode

debugging inside visual studio code

ConcurrentWriter

Provides a threadsafe way to write to the current console. You need to switch to writing to the console using a concurrent writer any time you have a background thread that is updating any portion of a console screen or a Konsole window.

var console = new ConcurrentWriter();

console.WriteLine(Green, $"finished encrypting {bytes} bytes.");
console.Write(...)
console.PrintAt(...)

You wrap any instance of any class that implements IConsole in a ConcurrentWriter Make any code of yours that implements IConsole threadsafe when writing to the console.

var myThreadSafeWriter = new ConcurrentWriter(myObjectThatImplementsIConsole);

The Window object

Window

var window = new Window();

Create a windowed region of the console (with or without a boxed border and title) that you can write to as if it were a new Console. It will wrap text and scroll, and you can print to it using PrintAt, as well as nest child windows in windows for more advanced window layouts.

  • ( 100%-ish console compatible window, supporting all normal console writing to a windowed section of the screen)
  • Supports scrolling and clipping of console output.
  • typical uses, for showing a scrolling output, e.g. build output in a window, while showing higher level progress in another window.
  • automatic borders
  • full color support

Floating constructors

When you provide a startX and startY position, as well as height and width, then the window created will be a floating window. The following are all floating constructors. The default foreground and background colors when none are provided are white on black.

Inline constructors

When you do not provide a startX and startY position, and only provide a height and width, then the window created is an inline window. The window is created starting at the next line using the height and width provided. The parent console CursorTop is advanced to the next line after the newly created window, and the cursorLeft is set to 0.

  • public Window(int width, int height)
  • public Window(int width, int height, ConsoleColor foreground, ConsoleColor background)

fullscreen constructor

When no start position or height and width is provided, then the window is fullscreen. It fill the entire parent window, even if the cursor is halfway down the parent window at the time. The cursor position is reset to the parent window 0,0.

  • Window()
var myConsoleAppMainWindow = new Window();

window example

var con = Window.OpenBox("client server demo", 110, 30);

con.WriteLine("starting client server demo");
var client = new Window(1, 4, 20, 20, ConsoleColor.Gray, ConsoleColor.DarkBlue, con).Concurrent();
var server = new Window(25, 4, 20, 20, con).Concurrent();
client.WriteLine("CLIENT");
client.WriteLine("------");
server.WriteLine("SERVER");
server.WriteLine("------");
client.WriteLine("<-- PUT some long text to show wrapping");
server.WriteLine(ConsoleColor.DarkYellow, "--> PUT some long text to show wrapping");
server.WriteLine(ConsoleColor.Red, "<-- 404|Not Found|some long text to show wrapping|");
client.WriteLine(ConsoleColor.Red, "--> 404|Not Found|some long text to show wrapping|");

con.WriteLine("starting names demo");
// let's open a window with a box around it by using Window.Open
var names = Window.OpenBox("names", 50, 4, 40, 10);
TestData.MakeNames(40).OrderByDescending(n => n).ToList()
        .ForEach(n => names.WriteLine(n));

con.WriteLine("starting numbers demo");
var numbers = Window.OpenBox("{numbers", 50, 15, 40, 10, new BoxStyle() { ThickNess = LineThickNess.Double, Body = new Colors(White, Blue) });
Enumerable.Range(1, 200).ToList()
        .ForEach(i => numbers.WriteLine(i.ToString())); // shows scrolling

Console.ReadKey(true);

gives you

window simple demo

Static constructors

OpenBox

  • Window.OpenBox(string title)
  • Window.OpenBox(string title, BoxStyle style)
  • Window.OpenBox(string title, int sx, int sy, int width, int height)
  • Window.OpenBox(string title, int width, int height, BoxStyle style = null)
  • Window.OpenBox(string title, int sx, int sy, int width, int height, BoxStyle style)

Open a full screen styled window with a lined box border with a title. Styling allows for setting foreground and background color of the Line, Title, and body, as well as the line thickness, single or double using default styling, white on black, single thickness line.


  [Test]
        public void WhenNested_draw_a_box_around_the_scrollable_window_with_a_centered_title_and_return_a_live_window_at_the_correct_screen_location()
        {
            var con = new MockConsole(20, 9);
            Window.HostConsole = con;
            var parent = Window.OpenBox("parent", 0, 0, 20, 8, new BoxStyle() { ThickNess = LineThickNess.Double });
            var child = parent.OpenBox("c1", 7, 2, 8, 4);
            parent.WindowWidth.Should().Be(18);
            parent.WindowHeight.Should().Be(6);
            //var child = parent.OpenBox("c1", 7, 2, 8, 4);

            parent.WriteLine("line1");
            parent.WriteLine("line2");

            var expected = new[]
            {
                        "╔═════ parent ═════╗",
                        "║line1             ║",
                        "║line2             ║",
                        "║       ┌─ c1 ─┐   ║",
                        "║       │      │   ║",
                        "║       │      │   ║",
                        "║       └──────┘   ║",
                        "╚══════════════════╝",
                        "                    "
            };

            con.Buffer.Should().BeEquivalentTo(expected);

            child.WriteLine("cats");
            child.Write("dogs");
            
            expected = new[]
            {
                        "╔═════ parent ═════╗",
                        "║line1             ║",
                        "║line2             ║",
                        "║       ┌─ c1 ─┐   ║",
                        "║       │cats  │   ║",
                        "║       │dogs  │   ║",
                        "║       └──────┘   ║",
                        "╚══════════════════╝",
                        "                    "
            };

            con.Buffer.Should().BeEquivalentTo(expected);

            // should not interfere with original window cursor position so should still be able to continue writing as 
            // if no new child window had been created.

            parent.WriteLine("line3");
            parent.WriteLine("line4");

            expected = new[]
{
                        "╔═════ parent ═════╗",
                        "║line1             ║",
                        "║line2             ║",
                        "║line3  ┌─ c1 ─┐   ║",
                        "║line4  │cats  │   ║",
                        "║       │dogs  │   ║",
                        "║       └──────┘   ║",
                        "╚══════════════════╝",
                        "                    "
            };

            con.Buffer.Should().BeEquivalentTo(expected);
        }

Open

Window.Open()

Calling Window.Open() without any parameters will create a new window region consisting of the whole screen, and will clear the screen, and reset the cursor position. Returns a threadsafe Concurrent window.

var win = Window.Open();

this is equivalent to

var win = new Window().Concurrent();

Methods and Extension Methods

These methods require an existing instance of a window. (IConsole)

PrintAt

  • PrintAt(int x, int y, char c)
  • PrintAt(x, y, text)
  • PrintAt(int x, int y, string format, params object[] args)

PrintAt an area of a window

 var window = new Window();
 ...
 window.PrintAt(20, 20, "WARNING!");

PrintAtColor

  • PrintAtColor(ConsoleColor foreground, int x, int y, string text, ConsoleColor? background = null)

PrintAt an area of a window providing the color.

 var window = new Window();
 ...
 window.PrintAtColor(Red, 20, 20, "WARNING!", White);

Print the text, optionally wrapping and causing any scrolling in the current window, at cursor position X,Y in foreground and background color without impacting the current window's cursor position or colours. This method is only threadsafe if you have created a window by using .ToConcurrent() after creating a new Window(), or the window was created using Window.Open(...) which returns a threadsafe window.

Write

Write the text to the window in the color, withouting resetting the window's current foreground colour. Optionally causes text to wrap, and if text moves beyond the end of the window causes the window to scroll. The cursor of the window that did the writing remains at the last printed position, and no other window's cursor positions are changed.

  • Write(string text)
  • Write(string format, params object[] args)
  • Write(ConsoleColor color, string format, params object[] args)
 var window = new Window();
 window.Write(Red, "WARNING!");
 window.Write("this text is in the default colour and is not red.");

WriteLine

Same as Write but simulates a carriage return by moving the CursorTop to next line and resetting CursorLeft to 0.

  • WriteLine(string format, params object[] args)
  • WriteLine(ConsoleColor color, string format, params object[] args)

SplitRows

Split a console window screen into rows of screens. Returns an array of the rows. Specify the height for each split. Use a height of 0 to indicate that row will take the remainder of the rows. Similar to * in CSS.

  • IConsole[] _SplitRows(IConsole c, params Split[] splits)
 var con = new MockConsole(20, 11);
            var consoles = con.SplitRows(
                    new Split(3, "headline", LineThickNess.Single, ConsoleColor.Yellow),
                    new Split(0, "content", LineThickNess.Single),
                    new Split(3, "status", LineThickNess.Single, ConsoleColor.Yellow)
            );

            var headline = consoles[0];
            var content = consoles[1];
            var status = consoles[2];

            headline.Write("my headline that scrolls because of wrapping");
            content.Write("content goes here, and this content get's wrapped, and if long enough will cause a bit of scrolling.");
            status.Write("I get clipped & scroll off.");

            var expected = new[]
            {
                    "┌──── headline ────┐",
                    "│wrapping          │",
                    "└──────────────────┘",
                    "┌───── content ────┐",
                    "│ if long enough wi│",
                    "│ll cause a bit of │",
                    "│scrolling.        │",
                    "└──────────────────┘",
                    "┌───── status ─────┐",
                    "│roll off.         │",
                    "└──────────────────┘"
            };

            con.Buffer.Should().BeEquivalentTo(expected);

SplitColumns

Split a console window screen into columns of screens. Returns an array of the rows. Specify the height for each split. Use a height of 0 to indicate that row will take the remainder of the rows. Similar to * in CSS.

  • IConsole[] _SplitRows(IConsole c, params Split[] splits)
        [Test]
        public void split_the_window_into_windows_using_provided_splits()
        {
            var con = new MockConsole(19, 5);
            var cols = con.SplitColumns(
                new Split(9, "left"),
                new Split(0, "right")
                );
            var left = cols[0];
            var right = cols[1];

            left.WriteLine("one");
            left.WriteLine("two");
            left.Write("three");

            right.WriteLine("four");
            right.WriteLine("five");
            right.Write("six");

            var expected = new[]
            {    
                "┌ left ─┐┌─ right ┐",
                "│one    ││four    │",
                "│two    ││five    │",
                "│three  ││six     │",
                "└───────┘└────────┘"
            };
            con.Buffer.Should().BeEquivalentTo(expected);
        }

Advanced windows with SplitRows and SplitColumns

You can create advanced window layouts using SplitRows and SplitColumns passing in a collection of Splits. Pass in a size of 0 to indicate that row or column window must contain the remainder of the window space.

            var c = new Window();
            var consoles = c.SplitRows(
                    new Split(4, "heading", LineThickNess.Single),
                    new Split(0),
                    new Split(4, "status", LineThickNess.Single)
            );

            var headline = consoles[0];
            var status = consoles[2];

            var contents = consoles[1].SplitColumns(
                    new Split(20),
                    new Split(0, "content") { Foreground = ConsoleColor.White, Background = ConsoleColor.Cyan },
                    new Split(20)
            );
            var menu = contents[0];
            var content = contents[1];
            var sidebar = contents[2];

            headline.Write("my headline");
            content.WriteLine("content goes here");

            menu.WriteLine("Options A");
            menu.WriteLine("Options B");

            sidebar.WriteLine("20% off all items between 11am and midnight tomorrow!");

            status.Write("System offline!");
            Console.ReadLine();

Produces the following window. Each of the console(s) that you have a reference to can be written to like any normal console, and will scroll and clip correctly. You can create progress bar instances inside these windows like any console.

Configure the properties of each section of a window with the Split class.

new Split(size) 
{
    title,
    lineThickNess, 
    foregroundColor,
    backgroundColor
};

SplitLeft

Split an IConsole window and return the left half of a screen. Returns an IConsole consisting of the inner window representing the scrollable window region inside the lined border.

  • IConsole SplitLeft(this Window c)
  • IConsole SplitLeft(this Window c, ConsoleColor foreground)
  • IConsole SplitLeft(this Window c, string title)
  • IConsole SplitLeft(this Window c, string title, ConsoleColor foreground)
  • IConsole SplitLeft(this Window c, string title, LineThickNess thickness)
  • IConsole SplitLeft(this Window c, string title, LineThickNess thickness, ConsoleColor foreground)

SplitRight

Split an IConsole window and return the left half of a screen. Returns an IConsole consisting of the inner window representing the scrollable window region inside the lined border.

  • IConsole SplitRight(this Window c)
  • IConsole SplitRight(this Window c, ConsoleColor foreground)
  • IConsole SplitRight(this Window c, string title)
  • IConsole SplitRight(this Window c, string title, ConsoleColor foreground)
  • IConsole SplitRight(this Window c, string title, LineThickNess thickness)
  • IConsole SplitRight(this Window c, string title, LineThickNess thickness, ConsoleColor foreground)

SplitLeft, SplitRight example

    var w = new Window();

    // split left
    var left = w.SplitLeft("left");

    // split right
    var right = w.SplitRight("right");

    left.WriteLine("one");
    left.WriteLine("two");
    left.Write("three");

    right.WriteLine("four");
    right.WriteLine("five");
    right.Write("six");

gives you

    ┌ left ─┐┌─ right ┐
    │one    ││four    │
    │two    ││five    │
    │three  ││six     │
    └───────┘└────────┘

SplitLeftRight

Split an IConsole window instance into two equal halves in one command returning a tuple consisting of the two inner windows representing the scrollable window region inside the lined border. The lined border between the two windows are merged.

  • (IConsole left, IConsole right) SplitLeftRight(this Window c, BorderCollapse border = Collapse)
  • (IConsole left, IConsole right) SplitLeftRight(this Window c, ConsoleColor foreground, ConsoleColor background, BorderCollapse border = Collapse)
  • (IConsole left, IConsole right) SplitLeftRight(this Window c, string leftTitle, string rightTitle, BorderCollapse border = Collapse)
  • (IConsole left, IConsole right) SplitLeftRight(this Window c, string leftTitle, string rightTitle, ConsoleColor foreground, ConsoleColor background, BorderCollapse border = Collapse)
  • (IConsole left, IConsole right) SplitLeftRight(this Window c, string leftTitle, string rightTitle, LineThickNess thickness, BorderCollapse border = Collapse)
  • (IConsole left, IConsole right) SplitLeftRight(this Window c, string leftTitle, string rightTitle, LineThickNess thickness, ConsoleColor foreground, ConsoleColor background, BorderCollapse border = Collapse)
    void Fill(IConsole con) => { 
        con.WriteLine("one");
        con.WriteLine("two");
        con.WriteLine("three");
        con.WriteLine("four");
    }
    (var left, var right) = win.SplitLeftRight("left", "right");
    
    Fill(left);
    Fill(right);
    
    // gives you   ...left ─┬─ right ┐
    │two    │two     │
    │three  │three   │
    │four   │four    │
    └───────┴────────┘    

SplitTop

  • IConsole SplitTop(this IConsole c)
  • IConsole SplitTop(this IConsole c, ConsoleColor foreground)
  • IConsole SplitTop(this IConsole c, string title)
  • IConsole SplitTop(this IConsole c, string title, ConsoleColor foreground)
  • IConsole SplitTop(this IConsole c, string title, LineThickNess thickness)
  • IConsole SplitTop(this IConsole c, string title, LineThickNess thickness, ConsoleColor foreground)

SplitBottom

  • IConsole SplitBottom(this IConsole c)
  • IConsole SplitBottom(this IConsole c, ConsoleColor foreground)
  • IConsole SplitBottom(this IConsole c, string title)
  • IConsole SplitBottom(this IConsole c, string title, ConsoleColor foreground)
  • IConsole SplitBottom(this IConsole c, string title, LineThickNess thickness)
  • IConsole SplitBottom(this IConsole c, string title, LineThickNess thickness, ConsoleColor foreground)
    var w = new Window();
    var top = w.SplitTop("top");
    var bottom = w.SplitBottom("bot");

    top.WriteLine("one");
    top.WriteLine("two");
    top.Write("three");

    bottom.WriteLine("four");
    bottom.WriteLine("five");
    bottom.Write("six");

gives you

    ┌── top ─┐
    │one     │
    │two     │
    │three   │
    └────────┘
    ┌── bot ─┐
    │four    │
    │five    │
    │six     │
    └────────┘

SplitTopBottom

Nested Windows

combining SplitTop, SplitBottom with SplitLeft, SplitRight

    var win = new Window(30,10);

    var left = win.SplitLeft("left");
    var right = win.SplitRight("right");
    
    var top = left.SplitTop("top");
    var bottom = left.SplitBottom("bot");
    
    top.WriteLine("one");
    top.WriteLine("two");
    top.Write("three");

    bottom.WriteLine("four");
    bottom.WriteLine("five");
    bottom.Write("six");

Gives you the window shown below.

Note that top and bottom windows are only 2 lines high and therefore printing three lines has cause the windows to scroll the top item off the window.

┌─── left ────┐┌─── right ───┐
│┌─── top ───┐││             │
││two        │││             │
││three      │││             │
│└───────────┘││             │
│┌─── bot ───┐││             │
││five       │││             │
││six        │││             │
│└───────────┘││             │
└─────────────┘└─────────────┘

Window Properties

(TBD) - Link to new readme.

Input

To capture input, create an Inline Window, e.g. var myWindow = Window.Open(width, height, title) and the cursor will be placed immediately UNDERNEATH the newly created window, and you can use and normal Console.ReadLine() reads, Console.ReadLine() will run at the current cursor.

[Roadmap - version 6] : version 6 includes significant updates addressed at forms, input and listview For now, use Console.ReadLine.

Here's a worked example showing you how to read input using Konsole

        static void Main(string[] args)
        {
            static void Compress(IConsole status, string file)
            {
                status.WriteLine($"compressing {file}");
                Thread.Sleep(new Random().Next(10000));
                status.WriteLine(Green, $"{file} (OK)");
            }

            static void Index(IConsole status, string file)
            {
                status.WriteLine($"indexing {file}");
                Thread.Sleep(new Random().Next(10000));
                status.WriteLine(Green, " finished.");
            }

            var console = new ConcurrentWriter();  // < -- NOTE THE ConcurrentWriter to replace Console

            // open two new windows inline at the current cursor position
            // cursor will move to below the new windows for easy ReadLine input

            var compressWindow = Window.OpenBox("compress", 50, 10);
            
            console.WriteLine("I am below compress");

            var encryptWindow = Window.OpenBox("encrypt", 50, 10);

            var tasks = new List<Task>();

            while (true)
            {
                console.Write("Enter name of file to process (quit) to exit:");
                var file = Console.ReadLine();
                if (file == "quit") break;
                tasks.Add(Task.Run(() => Compress(compressWindow, file)));
                tasks.Add(Task.Run(() => Index(encryptWindow, file)));
                console.WriteLine($"processing {file}");
            }

            console.WriteLine("waiting for background tasks");
            Task.WaitAll(tasks.ToArray());
            Console.WriteLine("done.");
        }

Running the code above gives you

Clipping and Transparency

experimental Window constructors

These Window constructors are specifically to support writing controls and take optional K parameters. This is experimental for now and the constructor params will be changed in future release as the API stabilises as we get more users to give us feedback. There's a long story behind why I used the ugly K parameter instead of more traditional constructor injection, buy me a beer and I'll tell you. Some of the original problems that led to K are no longer a problem and I will be removing it going forward and will be moving this out of experimental add adding to BoxStyle.

  • public Window(int x, int y, int width, int height, IConsole echoConsole = null, params K[] options)
  • public Window(IConsole echoConsole, params K[] options)
  • public Window(IConsole console, int width, int height, params K[] options)
  • public Window(IConsole console, int width, int height, ConsoleColor foreground, ConsoleColor background, params K[] options)
  • public Window(int width, int height, ConsoleColor foreground, ConsoleColor background, params K[] options)
K effect
Transparent window background color is transparent until you start writing then will print using the configured fore and background color i.e. initial window will not clear the background
FullScreen window background color is transparent until you start writing then will print using the configured fore and background color i.e. initial window will not clear the background
Clipping printing off the screen is clipped, no scrolling. Clipping is the default behavior for a window.
Scrolling printing off the bottom of the window causes the window to scroll. (cannot be used in conjunction with Clipping) Scrolling is the default window behavior.
    // create a single line 40 characters wide floating window 
    // starting at 10,10
    // White on blue text
    // with any overflow will be clipped. 
    // No scrolling. 
    var singleLineWindow = new Window(10,10, 40, 1, White, Blue, K.Clipping);

Draw

Draw

Draw lines and boxes single or double line width on the console window and intelligently merge lines that are drawn.

Start by creating a Draw instance, that needs an IConsole. for example

var window = new Window();
var draw = new Draw(window);

Draw constructors

Draw lines, optionally merging them intelligently to draw console forms or borders or lines.

  • Draw(IConsole console)
  • Draw(IConsole console, LineThickNess thickness = LineThickNess.Single, MergeOrOverlap mergeOrOverlap = MergeOrOverlap.Merge)

[V6 roadmap]

  • new Draw() - draw without needing a window instance

Draw methods

Box

Draw a box from start to end, with an optional centered title. Returns the draw instance for fluent chaining.

  • Draw Box(int sx, int sy, int ex, int ey, LineThickNess? thickness = null)
  • Draw Box(int sx, int sy, int ex, int ey, string title, LineThickNess? thicknessOverride = null)
var window = new Window();
new Draw(window).Box(2, 2, 42, 8, "my test box", LineThickNess.Single);

gives you

 ┌───────────── my test box ─────────────┐  
 │                                       │ 
 │                                       │ 
 │                                       │ 
 │                                       │ 
 │                                       │ 
 └───────────────────────────────────────┘ 

boxes can overlap

Boxes can overlap and line chars are intelligently merged. This allows for creating very sophisticated designs.

The sample below uses MockConsole which is also part of the Konsole library, so that we can assert on what was drawn to the console.

            var console = new MockConsole(12, 10);
            var line = new Draw(console, LineThickNess.Single, Merge);
            line.Box(0, 0, 8, 6, LineThickNess.Single);
            line.Box(3, 3, 11, 9, LineThickNess.Double);

            var expected = new[]
            {
               "┌───────┐   ",
               "│       │   ",
               "│       │   ",
               "│  ╔════╪══╗",
               "│  ║    │  ║",
               "│  ║    │  ║",
               "└──╫────┘  ║",
               "   ║       ║",
               "   ║       ║",
               "   ╚═══════╝"
            };
            console.Buffer.Should().BeEquivalentTo(expected);

Line

Draw a line between two points, either horizontally or vertically.

  • Draw Line(int sx, int sy, int ex, int ey, LineThickNess? thickness = null)
var window = new Window();


int height = 18;
int sy = 2;
int sx = 2;
int width = 60;
int ex = sx + width;
int ey = sy + height;
int col1 = 20;

  var draw = new Draw(console, LineThickNess.Double);
            draw
                .Box(sx, sy, ex, ey, "my test box")
                .Line(sx, sy + 2, ex, sy + 2)
                .Line(sx + col1, sy, sx + col1, sy + 2, LineThickNess.Single)
                .Line(sx + 35, ey - 4, ex - 5, ey - 4, LineThickNess.Double)
                .Line(sx + 35, ey - 2, ex - 5, ey - 2, LineThickNess.Double)
                .Line(sx + 35, ey - 4, sx + 35, ey - 2, LineThickNess.Single) 
                .Line(ex - 5, ey - 4, ex - 5, ey - 2, LineThickNess.Single); 

window.PrintAt(sx + 2, sy + 1, "DEMO INVOICE");                

gives you. (there is a small bug when switching from single to double line at a corner, as seen in the sample below, this will be fixed in upcoming version 6.)

╔═══════════════════╤═══ my test box ═══════════════════════╗
║ DEMO INVOICE      │                                       ║
╠═══════════════════╧═══════════════════════════════════════╣
║                                                           ║
║                                                           ║
║                                                           ║
║                                                           ║
║                                                           ║
║                                                           ║
║                                                           ║
║                                                           ║
║                                                           ║
║                                                           ║
║                                                           ║
║                                  ╤═══════════════════╤    ║
║                                  │                   │    ║
║                                  ╧═══════════════════╧    ║
║                                                           ║
╚═══════════════════════════════════════════════════════════╝

Forms

Quickly and neatly render an object and it's properties in a window or to the console. Support for multiple border styles. Support C# objects or dynamic objects. Readonly forms are currently rendered. (Currently only text fields, readonly, simple objects.) On the backlog; [V6] add additional field types, complex objects, and editing.

constructors

  • public Form()
  • public Form(int width)
  • Form(IConsole console = null)

If no width is provided, the whole width of the parent window is used.

Form methods

Write

Form is written (inline) at current cursor position, and cursor is updated to next line below form, with left=0.

  • void Write<T>(T item, string title = null)

Rendering Null objects

var form = new Form();
Person p = null;
console.WriteLine("line1");
form.Write(p);
console.WriteLine("line2");

gives you

line1
    ┌────────────────────────────────── Person  ──────────────────────────────────┐
    │ Null                                                                        │
    └─────────────────────────────────────────────────────────────────────────────┘
line2

Rendering Nullable fields

var form = new Form(54, new ThinBoxStyle());
var numclass = new TestClasses.FormTests.MixedNumClass
{
    DoubleField = double.MaxValue,
    DoubleNull = null,
    IntMinValue = int.MaxValue,
    IntNull = null,
    FloatField = 10.1234F,
    FloatNull = null,
};
form.Write(numclass);

gives you

┌────────────────── MixedNumClass  ──────────────────┐
│ Double Field      : 1.7976931348623157E+308        │
│ Double Null       : Null                           │
│ Int Min Value     : 2147483647                     │
│ Int Null          : Null                           │
│ Float Field       : 10.1234                        │
│ Float Null        : Null                           │
└────────────────────────────────────────────────────┘

Numerics and Nullable types

examples showing auto rendering of simple objects.

using Konsole.Forms

        using Konsole.Form;
        ...
            var form1 = new Form(80,new ThickBoxStyle());
            var person = new Person()
            {
                FirstName = "Fred",
                LastName = "Astair",
                FieldWithLongerName = "22 apples",
                FavouriteMovie = "Night of the Day of the Dawn of the Son 
                of the Bride of the Return of the Revenge of the Terror 
                of the Attack of the Evil, Mutant, Hellbound, Flesh-Eating 
                Subhumanoid Zombified Living Dead, Part 2: In Shocking 2-D"
            };
            form1.Write(person);

sample output

           // works with anonymous types
            new Form().Write(new {Height = "40px", Width = "200px"}, "Demo Box");

sample output

            // change the box style, and width
            new Form(40, new ThickBoxStyle()).Show(new { AddUser= "true", CloseAccount = "false", OpenAccount = "true"}, "Permissions");

sample output

Goblinfactory.Konsole.Windows (seperate nuget package)

TODO: move to seperate readme.

HighSpeedWriter

If you want to write a console game, or serious console application that you've tested and it's too slow using normal Konsole writing, and you are OK with the app only running on windows, then you can use HighSpeedWriter to write to the console window via native windows Kernel32. (A highspeed writer for MAC has been succesfully spiked and is on the backlog and will allow you to use Konsole to write .NET Core console apps that run on OSX and Windows.)

If you are only going to be updating small portions of the screen, then it is less CPU intensive to simply use PrintAt without a highspeed writer.

The other trade off is you need to keep calling .Flush() on your writer to refresh the screen. For a game you could dedicate a timer thread to refresh on tick and allow you to control the refresh rate, and cpu usage. Higher refresh rates will use more cpu.

Getting started with HighSpeedWriter

You use Konsole in the same way as described in the docs further above, except that all output is buffered, and you need to call Flush() on the writer when you want to update the screen.

If you have multiple threads writing to the Console, then instead of calling flush all the time, another option is to create a background thread that will tick over and refresh the screen x times a second. (depending on what framerate you require).

Known issues

  • HighSpeedWriter currently is not optimised to only write to a dirty section of a screen, that is on the backlog to implement. The result is that it's the same amount of CPU work to refresh the entire screen, as it is to update just 1 character.

HighSpeedWriter end to end sample

below is code that should give you a clue as to how I'm using HighSpeedWriter for myself. This sample code produces the following screen and output.

sample demo using HighSpeedWriter

Below is the source code that produced these screenshots

using System;
using System.Threading;
using System.Threading.Tasks;
using Konsole;
using Konsole.Internal;
using static System.ConsoleColor;

namespace Konsole.Samples
{
    public static class RealtimeStockPriceMonitorWithHighSpeedWriter
    {
        //TODO: get a feed of stock symbols, and allow user to pick stock prices from column B, and add to monitor
        //      on left. use fake (and real) stock service
        static bool finished = false;
        static bool crazyFast = false;
        static Func<bool> rand = () => new Random().Next(100) > 49;
        public static void Main(string[] args)
        {

            using var writer = new HighSpeedWriter();
            var window = new Window(writer);

            window.CursorVisible = false;
            
            var left = window.SplitLeft();
            var leftConsoles = left.SplitRows(
                new Split(0),
                new Split(9, "status"),
                new Split(10)
                );

            var status = leftConsoles[1];           
            status.BackgroundColor = Yellow;
            status.ForegroundColor = Red;
            status.Clear();

            var stocksCon = leftConsoles[0];            
            var menuCon = leftConsoles[2];
            var namesCon = window.SplitRight("account audit log");

            var r = new Random();
            int speed = 200;
            int i = 0;

            // print random names in random colors 
            // and demonstrate scrolling and wrapping at high speed
            var t1 = Task.Run(() => {
                var names = TestData.MakeNames(500);
                while (!finished)
                {
                    if (crazyFast)
                    {
                        while (crazyFast && !finished)
                        {
                            // fill a screen full before flushing
                            // this is super quick because writer 
                            // simply writes to a buffer and no actual
                            // slow IO has happened yet
                            for(int x = 0; x < 100; x++)
                            {
                                var color = (ConsoleColor)(r.Next(100) % 16);
                                namesCon.Write(color, $" {names[i++ % names.Length]}");
                            }
                            // now lets flush this massive block of updates
                            writer.Flush();
                        }
                    }
                    else
                    {
                        var color = (ConsoleColor)(r.Next(100) % 16);
                        namesCon.Write(color, $" {names[i++ % names.Length]}");
                        if (finished) break;
                        Thread.Sleep(r.Next(speed));
                        writer.Flush();
                    }
                }
            });

            var t3 = Task.Run(() =>
            {
                // stock ticker simulation
                var stocksNYSE = new[] { "BRK.A", "MSFT", "AMZN", "BTG.L", "AAPL", "LWRF.L", "GBLN1", "GBLN2" };
                var stocksFTSE = new[] { "SDR", "WPP", "ABF", "BP", "AVV", "AAL", "KGF", "MNDI", "NG","RM", "NXT","PSON" };
                var FTSE100Con = stocksCon.SplitLeft("FTSE 100");
                var NYSECon = stocksCon.SplitRight("NYSE");

                while (!finished)
                {
                    decimal move = (decimal)r.Next(100) / 10;
                    decimal newPrice = (50 + r.Next(100) + move);
                    decimal perc = ((decimal)r.Next(2000) / 100);
                    var increase = rand() ? true : false;
                    var sign = increase ? '+' : '-';
                    var changeColor = perc < 10 ? Cyan : increase ? Green : Red;
                    IConsole con;
                    string stock;
                    if (rand())
                    {
                        con = FTSE100Con;
                        stock = stocksFTSE[r.Next(stocksFTSE.Length)];
                    }
                    else
                    {
                        con = NYSECon;
                        stock = stocksNYSE[r.Next(stocksNYSE.Length)];
                    }
                    con.Write(White, $"   {stock,-10}");
                    con.WriteLine(changeColor, $"{newPrice:0.00}");
                    con.WriteLine(changeColor, $"  ({sign}{newPrice}, {perc}%)");
                    con.WriteLine("");
                    Thread.Sleep(r.Next(2000));
                }
            });

            // create a menu inside the menu console window
            // the menu will write updates to the status console window

            var menu = new Menu(menuCon, "Menu", ConsoleKey.Escape, 30,
                new MenuItem('s', "slow", () =>
                {
                    speed = 200;
                    status.Write(White, $" : {DateTime.Now.ToString("HH:mm:ss - ")}");
                    status.WriteLine(Green, $" SLOW ");
                    crazyFast = false;
                }),
                new MenuItem('f', "fast", () =>
                {
                    speed = 10;
                    status.Write(White, $" : {DateTime.Now.ToString("HH:mm:ss - ")}");
                    status.WriteLine(White, $" FAST ");
                    crazyFast = false;
                }),
                new MenuItem('c', "crazy fast", () =>
                {
                    speed = 1;
                    crazyFast = true;
                    status.Write(White, $" : {DateTime.Now.ToString("HH:mm:SS - ")}");
                    status.WriteLine(Red, $" CRAZY FAST ");
                })

            );

            status.WriteLine("press up and down to select a menu item, and enter or highlighted letter to select. Press escape to quit.");

            // menu writes to the console automatically,
            // but because we're using a buffered screen writer
            // we need to flush the UI after any menu action.
            menu.OnAfterMenuItem = _ => writer.Flush();

            menu.Run();
            // menu will block until user presses the exit key.

            finished = true;
            Task.WaitAll(t1, t3);

            window.Clear();
            window.WriteLine("thank you for flying Konsole air.");
            writer.Flush();
        }
    }
}

snippet source | anchor

Copying code from the unit tests

Because the unit tests are run on an Azure build server that does not have access a open console fileHandle many of the tests use MockConsole. Whenever you see sample code, eg the test below from src/Konsole.Tests/WindowTests/SplitColumnsShould.cs

    var con = new MockConsole(30, 4);
    var window = new Window(con);
    var consoles = window.SplitColumns(
            new Split(8, "col1", LineThickNess.Single),
            new Split(10, "col2", LineThickNess.Single),
            new Split(12, "col3", LineThickNess.Single)

... then in order to use the code yourself in a project, simply leave out the MockConsole, and start with a new Window() as below. The rest of the unit test code will work the same in production as in testing.

    var window = new Window();
    var consoles = window.SplitColumns(
            new Split(8, "col1", LineThickNess.Single),
            new Split(10, "col2", LineThickNess.Single),
            new Split(12, "col3", LineThickNess.Single)
            ... rest of code

Other .NET console libraries

unsorted list of libraries (random links I saved for myself to refer to or investigate)

Why did I write Konsole?

I wrote Konsole to allow me to write simple test projects, or "reference architecture" projects when evaulating various libraries, (for example, Akka.net, memstate or eventstor ) It allows me to write simple console apps that are easy to understand and render in a visually simple way, especially when there are multiple threads, actors or servers that I need to visual represent without getting sidetracked building a WPF or windforms or web application.

A big benefit to me is being able to visually describe in text any complex screen layout and application without requiring images.

I'm now also using it for other serious applications besides learning material. I'm using Konsole in Gunner a .NET testing library similar to Gattling that I need to put code under stress when evaulating different enterprise messaging libraries.

Debugging problems with Konsole

This project may not be fully compatible with your project.

When building .NET standard or .NET core app, you may recieve the following warning. If you have treat warnings as errors turned on, then this will fail your build.

Warning NU1702

Warning NU1702 ProjectReference 'C:\src\git-alan-public\konsole-spike\Konsole.Platform.Windows\Konsole.Platform.Windows.csproj' was resolved using '.NETFramework,Version=v4.6.1' instead of the project target framework '.NETStandard,Version=v2.0'. This project may not be fully compatible with your project. Konsole C:\Program Files (x86)\Microsoft Visual Studio\2019\Preview\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets 1653

This is not a problem for Konsole and is a bug in .NET where .NET standard 2.0 apps should be able to reference up to .NET framework 4.6.1 without errors. To suppress this error, add the following to your project file.

    <MSBuildWarningsAsMessages>NU1702</MSBuildWarningsAsMessages>  

No visible output, blank screen

If you are using the HighSpeedWriter you must call Flush() to render the output.

Corrupt output

colors or text output appearing in the wrong place.

possible causes and fixes

  • some code somewhere is writing directly to the System.Console. Replace that code with calls to a new ConcurrentWriter(). See Threading docs for more information.

Writing Tests for your code that uses Konsole

Tests for code using HighSpeedWriter

TBD

All other tests

TBD

MockConsole

MockConsole (substitute), and IConsole (interface) usage

Use MockConsole as a real (fully functional) System.Console substitute, that renders with 100% fidelity to an internal buffer that can be used to assert correct console behavior of any object writing using IConsole. MockConsole can even render out a text representation of the current state of the the console, including representations for the foreground and background color of anything written.

All the test for this library have been written using MockConsole. For a fully detailed examples of MockConsole being stretched to the limits, see Konsole.Tests.WindowTests.

        
        using Konsole;
        ...
        public class Cat
        {
            private readonly IConsole _console;
            public Cat(IConsole console) { _console = console; }
            public void Greet()
            {
                _console.WriteLine("Prrr!");
                _console.WriteLine("Meow!");
            }
        }

        [Test]
        public void TestConsole_ConsoleWriter_and_IConsole_example_usage()
        {
            {
                // test the cat
                // ============
                var console = new TestConsole(80, 20);
                var cat = new Cat(console);
                cat.Greet();
                Assert.AreEqual(console.BufferWrittenTrimmed, new[] {"Prrr!", "Meow!"});
            }
            {
                // create an instance of a cat that will purr to the real Console
                // ==============================================================
                var cat = new Cat(new ConsoleWriter());
                cat.Greet(); // prints Prrr! aand Meow! to the console
            }
        }

MockConsole vs Mock<IConsole>

Below is a comparison of how someone might test an Invoice class using a traditional Mock<IConsole> and the same test, using a Konsole.MockConsole. To make it a fair comparison I'm comparing to NSubstitute which is quite terse and one of my favourite mocking frameworks.

        [Test]
        public void Test_Invoice_using_mocks()
        {
            // test the invoice
            // ============
            IConsole console = new Substitute.For<IConsole>();
            var invoice = new Invoice(console);
            invoice.AddLine(2, "Semi Skimmed Milk", "2 pints", "£",1.00);
            invoice.AddLine(3, "Warburtons Crumpets", "6 pack", "£",0.89);
            invoice.Print();
                
            // not really practical to test printed output using a mock console
            // ================================================================
            console.Received().SetCursorPosition(0,0);
            console.Received().WriteLine("ACME Wholesale Foody");
            console.Received().WriteLine("--------------------");
            console.Received().WriteLine("");
            console.Received().WriteLine("--------------------");
            console.Received().Write("qty ");
            console.Received().Write(2);
            console.Received().Write(" Semi Skimmed Milk");
            console.Received().Write(", ");
            console.Received().Write("{0} pints", 2);
            console.Received().Write("£ {0.00,-10}", 2.0m);
            .
            .
            . // and so on and so on ...for probably around another 12 or 13 lines.
            .
            .
             // having to mimick the exact formatted Write's and Writelines and SetCursor movements 
             // this is brittle, if the code is optimised to replace two Write's with a single formatted WriteLine for example
             // then this test fails even though the desired output is written to the console.
        }
        

using a Test Double like Konsole.MockConsole the test above becomes

        [Test]
        public void testing_Invoice_class_using_MockConsole()
            {
                var expected = @"
                 ACME WHoleSale Foody 
                 -------------------- 
                 qty 2 Semi Skimmed Milk   , 2 pints     £ 2.00
                 qty 3 Warburtons Crumpets , 6 pack      £ 5.34
                 --------------
                 total   £ 7.34 
                 --------------
            
                * some random message on the footer
";
        
                var console = new MockConsole();
                var invoice = new Invoice(console);
                invoice.AddLine(2, "Semi Skimmed Milk", "2 pints", "£",1.00);
                invoice.AddLine(3, "Warburtons Crumpets", "6 pack", "£",0.89);
                invoice.Print();
                Assert.AreEqual(console.BufferString,expected);
                });
            }
                
            // Now, if someone accidentally changes your currency formatter, this test will wail
            // when the rendered output to the Console suddenly changes, bwaaam! Instant Fail.
            // Score one for MockConsole, sweetness, life is good!
        }

Cross platform notes

TBD

Building the solution

git clone https://github.com/goblinfactory/konsole.git
cd konsole
dotnet test `or` build.bat

requirements

Any version of .net core. Update global.json to the version of .net core you have installed and run build.bat

Dev releases

  • dev branch will be available as pre-releases from time to time. To access the latest dev branch, use Nuget to include pre-releases. latest dev branch will be suffixed with dev-{nn} e.g. 6.0.1-dev-01

Alpha releases

  • Alpha releases will be suffixed with -alpha-{nn}
  • Alpha releases are any release that I want to make available for user feedback. Most commonly this will effectively be a private release for a user that has requested a specific feature, or reported a bug and I create a candidate release to fix that bug, or add that feature.
  • Alpha releases may contain public API api changes and would mean to include the fix it would mean that feature would be included in the next major release.

ChangeLog

  • here is the changelog. It is kept up to date.

The format is based on Keep a Changelog and this project adheres to Semantic Versioning.

Support, Feedback, Suggestions

If you find Konsole useful please consider sponsoring me.

keep chillin!

O__
_/`.\
    `=( 

Alan

p.s. join us at snowcode free dev conference at ski resort
www.snowcode.com
developers + party + snow + great learning February (private invite only powderfest) and April (open to public) end of season snowcode conf, slushfest, best for boarders! :D will try to be at a glacier for the skiers.

@snowcode

Alan Hemmings

konsole's People

Contributors

gitter-badger avatar goblinfactory avatar julianturner avatar luanroger avatar mateuszlewko avatar pazerop avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

konsole's Issues

Nested floating windows dont scroll properly

When nesting a floating window, inside another floating window, the new nested window does not scroll correctly.

var w = new Window();
var left = w.SplitLeft("left");
var top = left.SplitTop("top");
var bottom = left.SplitBottom("bottom");

In the example above, top and bottom are both nested floating windows. When printing to bottom or top, causing the window to scroll, then the scrolling does not happen correctly.

crash on Writeline of Json

This is a really odd one.

Using the same plumbing as before, the log emit is trying to output a log entry containing some well formatted JSON. The call to .WriteLine() throws a FormatException..... the kind of thing you'd see if you were passing a formatting string and some parameters... but all I'm doing is passing a string.

The Logging_Console variable below is held in a class injected as a singleton. It is a Konsole.IConsole initialized as logging_console = Window.Open(0, progress_window_height, main_window_width, main_window_height - progress_console.WindowHeight - command_console.WindowHeight - (window_padding * 2), "operation logging", Konsole.Drawing.LineThickNess.Single);

Log entry:
"SessionId: -1 receiving json "{\"code\":3837,\"sessionId\":-1,\"payload\":{\"text\":\"client requesting session id\"}}"

public void Emit(LogEvent logEvent)
{
   Dawn.Guard.Argument(() => logEvent).NotNull();
   var message = logEvent.RenderMessage(_formatProvider);
   this.consoleWrapper.Logging_Console.WriteLine(message); // <--- exception
}
Exception:  {"Input string was not in a correct format."} | System.FormatException

stack trace:
   at System.Text.StringBuilder.FormatError()
   at System.Text.StringBuilder.AppendFormatHelper(IFormatProvider provider, String format, ParamsArray args)
   at System.String.FormatHelper(IFormatProvider provider, String format, ParamsArray args)
   at System.String.Format(String format, Object[] args)
   at Konsole.Window.Write(String format, Object[] args)
   at Konsole.Window.WriteLine(String format, Object[] args)
   at Konsole.ConcurrentWriter.WriteLine(String format, Object[] args)
   at consoleTools.Implementations.Serilog_Sink.StealthConsoleSink.Emit(LogEvent logEvent) in C:\Users\ekramer\Documents\Visual Studio 2019\Projects\consoleTools\consoleTools\Implementations\Serilog_Sink\StealthConsoleSink.cs:line 33
   at Serilog.Core.Sinks.SafeAggregateSink.Emit(LogEvent logEvent)

Since a params object[] parameter accepts 'nothing' as an argument it is compiling. But when it gets down to Window.cs line 457-ish the call to string.format(format, args) is for some reason interpretting my json string as a formatted string with no arguments provided and is choking on it.

Edit: hrm.. this is odd too... a quick test of the Json string with string.Format()

            var bar = string.Format("SessionId: -1 receiving json {\"code\":3837,\"sessionId\":-1,\"payload\":{\"text\":\"client requesting session id\"}}");

the linter in Vs2019 is showing that line underlined green with a message of 'format string contains an invalid place holder'

wonder if i need to somehow sanitize it before passing it in?

ProgressBar does not expose current

A class ProgressBar creates a ProgressBarTwoLine or ProgressBarSlim under the hood. Both of them have a field _current, however, for the time being only ProgressBarSlim exposes the current value.
Could it be possible that the class ProgressBar exposes it as well? This property could be added a get-only property to IProgressBar. Does not look like a lot-to-do and it would be a handy property :)

Incorrect line character when merging a single and double line box

            var console = new MockConsole(12, 10);
            var line = new Draw(console, LineThickNess.Single, Merge);
            line.Box(0, 0, 8, 6, LineThickNess.Double);
            line.Box(3, 3, 11, 9, LineThickNess.Single);
           
         // produces the following output with invalid vertical line
       
            console.Buffer.Should().BeEquivalentTo(new[]
            {
               "╔═══════╗   ",
               "║       ║   ",
               "║       ║   ",
               "║  ┌────│──┐",
               "║  │    ║  │",
               "║  │    ║  │",
               "╚══╪════╝  │",
               "   │       │",
               "   │       │",
               "   └───────┘"
            });

Note the invalid vertical line at the intersection.

Include ETA in progressbar

It would be really great if the progress bar could be configured to show the estimated time of completion.
Such could be easily interpolated by averaging the time needed per iteration.
That would be highly informative!

Refresh writing progress bar to new line of console

I am having an issue where calling refresh on the progress bar is writing each increment to a new line. My code looks like below

foreach (var partition in partitions)
{
    var unitOfWork = new UnitOfWork(partition);
    var progressBar = new ProgressBar(100);
    unitOfWork.ReportProgress = progress => progressBar.Refresh(Convert.ToInt32(progress), String.Empty);
    await PerformWork(unitOfWork);
}

When there is a lot of work to do the progress bar behavior looks something like below.
progressbar

It seems to only do this after many iterations of the loop. I will work on creating a sample project to replicate the behavior that I can publish. In the meantime, any thoughts as to what might be happening?

Window Keyboard event handler - pass keyboard events to child windows

Any window must be able to start monitoring keystrokes, and keep track of active child windows that any keyboard commands are then passed to.

e.g.

var scores = window.OpenBox("scores", 50,20); 
scores.Run();

The default keystroke to move between active windows that have tab sequence number is the Tab key, but this can be overridden. When used in conjuction with multi-inputs (form input) then tab no longer moves between individual inputs, that will be left to the arrow keys or enter key.

  • Windows that can be tracked are windows open will be static Open or Split methods.
  • Pressing tab moves between active windows.
  • Active window is indicated by theme, default theme will be double line for active window, brightWhite on black, and inactive will be gray on black single Line.

Example

var parentWindow = new Window();
var teamA = parentWindow.SplitLeft("team A");
var teamB = parentWindow.splitRight("team B");
var getNames = GetNameInputs(); // returns an array of ReadLine();
teamA.Add(getNames);
teamB.Add(getNames);
parentWindow.Run();

Calling Run, will cause first window to have focus, and all keyboard events are sent to the controls inside that window. Pressing tab, will switch focus between the two window objects, teamA and teamB, and will cause each window to change their style indicating selection.

Crash after window resize

Been testing the Goblinfactory console classes and so far I'm pretty happy with them. I have a console app that needs progress bars and a section where a log will be displayed. I was able to do all that fairly easily. As long as I don't mess with the window size everything runs just fine.

But I'm having an issue wherein the console window gets resized and causes an exception. At first I did this accidentally when I grabbed the wrong console but it occurs most of the time when I resize the window whenI do so manually not by using minimize/maximize.

Edit: Also seems to be when I make the window smaller. Making the window larger just makes a mess of the screen.

I'm getting an argument out of range exception. This occurs when the log sink I am using attempts to update the log IConsole. I'm using dependency injection, though I don't know if that really matters from your prospective. The IConsoleWrapper is just a class I use to hold on to the individual parts of the 'window'

I instantiate the IConsoles like this:

var main_window_height = Console.WindowHeight;
var main_window_width = Console.WindowWidth;

var mainWindow = new Window(0, 0, main_window_width, main_window_height);

var progress_window_height = 4;
var command_window_height  = 3;
var window_padding         = 2;

var progress_console       = default(IConsole);
var logging_console        = default(IConsole);
var command_console        = default(IConsole);              

progress_console = Window.Open(0, 0, main_window_width, progress_window_height, "operation progress", Konsole.Drawing.LineThickNess.Single);
command_console = Window.Open(0, main_window_height - command_window_height, main_window_width, command_window_height, "system command", Konsole.Drawing.LineThickNess.Single);
logging_console = Window.Open(0, progress_window_height, main_window_width, main_window_height - progress_console.WindowHeight - command_console.WindowHeight - (window_padding * 2), "operation logging", Konsole.Drawing.LineThickNess.Single);

var console_wrapper = new ConsoleWrapper(progress_console, logging_console, command_console, main_window_height - window_padding);

serviceCollection.AddSingleton<IConsoleWrapper>(console_wrapper);

and the log update happens here....it's triggered by a Serilog log write.

public void Emit(LogEvent logEvent)
{
	Dawn.Guard.Argument(() => logEvent).NotNull();

	var message = logEvent.RenderMessage(_formatProvider);

	this.consoleWrapper.Logging_Console.WriteLine(message);
}

The exception details are the following:

 | $exception | {"The value must be greater than or equal to zero and less than the console's buffer size in that dimension. (Parameter 'sourceWidth')\r\nActual value was 104."} | System.ArgumentOutOfRangeException


   at System.ConsolePal.MoveBufferArea(Int32 sourceLeft, Int32 sourceTop, Int32 sourceWidth, Int32 sourceHeight, Int32 targetLeft, Int32 targetTop, Char sourceChar, ConsoleColor sourceForeColor, ConsoleColor sourceBackColor)
   at Konsole.Window.ScrollUp()
   at Konsole.Window.WriteLine(String format, Object[] args)
   at Konsole.ConcurrentWriter.WriteLine(String format, Object[] args)
   at Progress_Reporter_tests_cli.Implementations.Serilog_Sink.StealthConsoleSink.Emit(LogEvent logEvent) in C:\Users\ekramer\Documents\Visual Studio 2019\Projects\Progress_Reporter_tests_cli\Progress_Reporter_tests_cli\Implementations\Serilog_Sink\StealthConsoleSink.cs:line 34
   at Serilog.Core.Sinks.SafeAggregateSink.Emit(LogEvent logEvent)

demo project not working

demo project with all the samples is not working

  1. Do a fresh checkout or clone of the solution
  2. set sample project as start project
  3. press F5 and you get an exception, System.ArgumentOutOfRangeException: 'The value must be greater than or equal to zero and less than the console's buffer size in that dimension.

.NET Core support

Hi

Thank for you awesome library.
Is there any chance for .net core support ?

Input done right! :D

I want to be able to capture input safely at a region on the screen or at the current cursor position, and be able to specify the data type to limit to, and for the input to be constrained by the containing window.

For example

var window = Window.OpenBox("name", 40,3);
window.Write("Name:   ");
var name = window.ReadText(60, { Scroll = true }); // limits entry to 60 characters, allow scrolling
window.Write("Age:      ");
var age = window.ReadNumber(0, 120); 
window.Write("Discount: ");
var discount = window.ReadDecimal(0, 100, 2); // min, max, decimals 

Because the inputs are being entered inside a window, all text must be clipped by the containing window.

Scrolling content

If the text area left for the input is less than the available characters, then when the user reaches the end of the line, the textbox must scroll the contents.

for example in the code above the window is only 40 characters wide,and the caption takes up space, so the name which can be up to 60 characters, will cause the text to scroll when.

Support keystrokes

  • home, to move cursor to first character
  • end, to last
  • delete
  • insert (toggle between insert and overwrite)
  • control + arrow, highlight word
  • tab, complete entry from previous entries
    (to be completed)

Input at X,Y

var name = window.ReadAt(10,10, Input.Number, width:30);

Move between inputs using tab and arrow keys

The arrow keys will move you to the next active input based on the inputs location on the screen. Pressing up will move you to the nearest input above you, etc.

// pseudo code
var person = Window.Read<Person>(CaptionWidth:14, 
  p => 
  new Input(p.Name, 10,10, Text, width:30, caption:"Name"),
   new Input(p.Surname, 0,11, Text, width:30, caption:"Surname"), 
   new Input(p.X, 0, 1, Integer(0,50), width:4, caption:"X:"), 
   new Input(p.Y, 6,1, Integer(0,50), width:4, caption:"Y:"), 
);

Pressing tab moves you between inputs, inputs are highlighted with a default theme, typically black on white for active input, and white and black for inactive.

Themeable inputs

be able to set the foreground and background color of active, current, inactive items.

  • active : means that the item is part of an input.
  • current: user has tabbed into the entry
  • inactive : user has finished entering text, and the value is now in readonly display mode.

cascading theme to reduce code required to render

Inputs to read their theme from a window.
if a parent window has no theme, then the check for theme bubbles up to the parents parent, so that themes cascade.
Windows to come with decent default themes.

Newline / carriage return?

Thank you for writing Konsole, I really dig it!
Do you have a newline-character, which I can put into e. g. a Write or Writeline statement? \r\n does not work correctly.
Thx

Implement MoveBufferArea in Unix to enable using more cross platform support

Implement MoveBufferArea in Unix to enable using more cross platform support.

Currently .NET console (of all things) is not properly implemented in .NET Standard 2.1.

If you look here : https://github.com/dotnet/corefx/blob/master/src/System.Console/src/System/ConsolePal.Unix.cs
and search for PlatformNotSupportedException you'll see there are 17 console methods and props that throw this exception. i.e. are simply not support. The most critical of these that we need is MoveBufferArea which is required in order to provide scrolling support needed in all the windowing functionality.

        public static void MoveBufferArea(int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight, int targetLeft, int targetTop, char sourceChar, ConsoleColor sourceForeColor, ConsoleColor sourceBackColor)
        {
            throw new PlatformNotSupportedException();
        }

Scroll Up - does not technically scroll up, it (if anything) scrolls down. Will need to be renamed in next big update & will be a breaking change.

Scroll Up is used internally but exposed as a public method on IConsole.
As new functionality is being added to Konsole, allowing for ViewPorts and scrolling in all directions, this needs to be reviewed. In the next big release, this change will probably be rolled up into a set up viewport and "true" scrolling functionalities, where you will be able to scroll Up (to see previously written text, i.e. the History that has since scrolled off the top of the screen.)

This probably will not impact any users of Konsole, but in case someone is using it, please note that after the next big release, you'll need to use ScrollDown, not scrollUp.

For the definition of Scroll Up, or Scroll down, please see this explanation here:
https://www.webopedia.com/TERM/S/scroll.html

Konsole will be using the term ScrollUp in future to mean, you want the display to shown one or more lines towards to the top of the Document you are viewing. So the text moves down, and "older text" that was not visible should become visible at the top.

Scrolling Down will mean the reverse.

And lastly, when you're writing to the end of a screen and it causes the text to overflow the existing screen, then the screen (or buffer) will be expanded by 1 line, if you are already at the bottom of the screen or window, and the UI will then Scroll Down 1 line. (All the text moves UP 1 line), the new line will be filled with the default fill character, and window (console) colors.

write and write-line when scrolling results in first Write's text being lost

the following code demonstrates the bug (and I will use this in the unit test for the fix)

            var con = new Window(Console.WindowWidth, 20);
            var green = con.SplitLeft("left", ConsoleColor.Green);
            var yellow = con.SplitRight("right", ConsoleColor.Yellow);

            // When Konsole starts scrolling, first WRITE is ignored! 
            for (int i = 0; i < 30; i++)
            {
                green.Write("PART ONE - ");
                green.WriteLine(" - PART TWO");
            }

results in the following output

┌──────────────────── left ─────────────────────┐┌───────────────────── right ────────────────────┐
│PART ONE -  - PART TWO                         ││                                                │
│PART ONE -  - PART TWO                         ││                                                │
│PART ONE -  - PART TWO                         ││                                                │
│PART ONE -  - PART TWO                         ││                                                │
│PART ONE -  - PART TWO                         ││                                                │
│PART ONE -  - PART TWO                         ││                                                │
│ - PART TWO                                    ││                                                │
│ - PART TWO                                    ││                                                │
│ - PART TWO                                    ││                                                │
│ - PART TWO                                    ││                                                │
│ - PART TWO                                    ││                                                │
│ - PART TWO                                    ││                                                │
│ - PART TWO                                    ││                                                │
│ - PART TWO                                    ││                                                │
│ - PART TWO                                    ││                                                │
│ - PART TWO                                    ││                                                │
│ - PART TWO                                    ││                                                │
│ - PART TWO                                    ││                                                │
└───────────────────────────────────────────────┘└────────────────────────────────────────────────┘

10X (ten x) performance improvement : High speed line draw writer

currently the line draw routines are quite slow, involving a lot of cursor setting and calling out to existing console cursor movements, per line character. Create a version of the line draw that only writes to the buffer in conjunction with the highspeed writer, for a serious improvement in performance.

Add BBCode support

I use this extention to do colourfull output. I think it will be not bad to add full BBCode support (if it's possible) to console output.

    public static void WriteLineBBCode(this IConsole console, string line)
    {
        var exp = new Regex(@"\[color\=([^\]]+)\]([^\]]+)\[\/color\]");
        var matches = exp.Matches(line);
        foreach (Match match in matches)
        {
            Enum.TryParse(match.Groups[1].Value, true, out ConsoleColor consoleColor);
            console.Write(consoleColor, match.Groups[2].Value);
        }
        console.WriteLine("");
    }

List View and Form View

List view to allow you to render a list of entities in window X.
Form view to allow you to specify that a selected item on a list, is rendered inside window Y.

example

var window = new Window();
var listWin = window.SplitLeft("accounts");
var formWin = window.SplitRight("Form View");
var list = new ListView(listWin, accountService.Accounts); // convention based automatic select fields
list.OnSelect(item  => { formWin.Clear(); new Form(formWin).Write(item));
view.Run();

If a `Write` ends exactly on the last column, then the cursor is not advanced to the next line.

this fails.

            var con = new MockConsole(6, 3);
            con.Write("123");
            con.CursorLeft.Should().Be(3);
            con.CursorTop.Should().Be(0);
            con.Write("456");
            con.CursorLeft.Should().Be(0);
            con.CursorTop.Should().Be(1);
            con.Write("ABCDE");
            con.Write("1234");
            var expected = new[]
            {
                "123456",
                "ABCDEF",
                "XY    "
            };
            con.Buffer.Should().BeEquivalentTo(expected);

// actual output (which is faulty) is

 "12345A"
 "BCDEFX"
 "Y     "

the cursor remains at cursorLeft = 5, so that when the next Write or WriteLine happens, it overwrites the last character written.

Invalid Handle

I receive Invald Handle Exception when running

ProgressBar bar = new ProgressBar(PbStyle.DoubleLine, m);

in a Windows Application.
In Console Application this works very well.
Is there a workaround to use library in Windows Applications Output Window ?

window area borders disappeared

I'm not sure exactly what's going on but I have a very long process that I've been working to parallelize. It does take quite a bit of resource so that might be part of it. Anyway the borders between the various areas on the console window have disappeared. Everything appears to be working. Like the progress bar is still updating and the scrolling log window is still scrolling. So it doesn't appear to be hurting anything.

I don't know if it matters or not but I'm currently using a second progress bar in the first panel as a stationary label to display the current run time. It's being updated in the second level of a paralellized nested loop

borders_present
missing_borders

Refresh throw ArgumentOutOfRangeException

When ProgressBar defines with text with > console window size.
call to Refresh or Next will throw an exception: count' must be non-negative.\r\nParameter name: count

Concole Progress Bar is not thread safe

The refresh method has lock but constructor doesn't. So when threads creating ProgressBar inside or the one bar is already start reporting in other thread, the output overrides each other.

Konsole is slow to render large screens. Speed, Speed, Speed.

When rendering large screens with lots of layout Konsole can take up to 1 or 2 seconds to render a whole layout. While ok for the original use case of being able to help visualise output from scripts with parts running in parallel, its not OK to use to create a real application or "for production" utility.

REMOTE KONSOLE - SignalR/server send events React "Konsole"

SignalR/server send events React "Konsole"

The idea is to be able to drop in a text console "widget" into a webpage, e.g. a React control, or even in a static CDN website, that loads a console with a unique session ID, app ID and string[] channels. Then be able to treate writing to a remote console as simple as writing to a konsole window, with some extra support for broadcasting to channels, as well as processing user input.

for example, in a game; where the statusbar channel, is defined client side using normal console commands, window.SplitLeft(), splitColumns for example

 // below code would be converted to javascript / react so shown only as pseduo code for converting to react.
  var rows = layout.SplitRows(
                    new Split(3, "headline", LineThickNess.Single, ConsoleColor.Yellow),
                    new Split(0, "content", LineThickNess.Single),
                    new Split(3, "status", LineThickNess.Single, ConsoleColor.Yellow)
            );
  var rc = new remoteConsole(appId, appUrl, authtoken);
 var header = new remoteConsole(rc, rows[0], "header");
var content = new remoteConsole(rc, rows[1],"content");
 var status = new remoteConsole(rc, rows[2], "statusBar"); 
  rc.Render();
  rc.HandleEvents(myCancellationToken);

then server side you can write

var (deadUser, restOfUsers) = GetNextVictim(); // returns sessionIDs used to communicate with remote users.

var rc = new RemoteConsole(appId, appUrl, token);

var contentAll = rc.BroadCast.ToChannel("content").ToAllExcluding(deadUser.SessionId);
contentAll.WriteLine(Yellow, $"{deadUser.Name} was killed by the wearwolf.");

var contentDead = rc.SingleCast(deadUser.SessionId).ToChannel("content"):
contentDead.WriteLine(Red, $"You have been killed by the wearwolf.");

var statusAll = rc.BroadCast.ToChannel("status");
statusAll.WriteLine($"Only {cnt} players left alive!");

Window.Open with Single line thickness opens with double

    // bug - Window.Open with Single line thickness opens with double!
    public static void Run(IConsole con)
    {
        var w1 = Window.Open(1, 1, 40, 28, "TestData : Filenames", LineThickNess.Single, ConsoleColor.White, ConsoleColor.DarkBlue);
        var fileNames = TestData.MakeFileNames(100, ".txt", ".com", ".exe", ".png");
        foreach (var file in fileNames)
            w1.WriteLine(file);

        var w2 = Window.Open(43, 1, 40, 28, "TestData : Filenames", LineThickNess.Single, ConsoleColor.White, ConsoleColor.DarkYellow);
        var objNames = TestData.MakeObjectNames(100);
        foreach (var oname in objNames)
            w2.WriteLine(oname);
    }

generates the following

screen shot 2017-03-20 at 20 21 07

border should be a single line, not double line.

Full Color Theming support

Hi, I just wanted to leave the comment that C# supports 24Bit colors for the Windows console.
You can find an example in one of my libraries:

Color Setter Functions

public static class ConsoleExtensions
{
    public static RGBAColor ForegroundColor
    {
        set => Console.Write(value.ToVT100ForegroundString());
    }

    public static RGBAColor BackgroundColor
    {
        set => Console.Write(value.ToVT100BackgroundString());
    }


    // this static constructor is needed on non-unix machines, as the VT100 color codes are not always enabled by default.
    // see https://github.com/Unknown6656/Unknown6656.Core/blob/master/Unknown6656.Core/Controls/Console/ConsoleExtensions.cs
    static ConsoleExtensions()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            STDINConsoleMode |= ConsoleMode.ENABLE_VIRTUAL_TERMINAL_PROCESSING;
            STDOUTConsoleMode |= ConsoleMode.ENABLE_VIRTUAL_TERMINAL_PROCESSING;
        }
    }
}

https://github.com/Unknown6656/Unknown6656.Core/blob/master/Unknown6656.Core/Controls/Console/ConsoleExtensions.cs#L37..L57

Color Data Structure

public struct RGBAColor
{
    public byte B,G,B,A;


    public RGBAColor(uint argb)
        : this()
    {
        if (argb <= 0xffff)
            argb = ((argb & 0xf000) << 16)
                 | ((argb & 0xff00) << 12)
                 | ((argb & 0x0ff0) << 8)
                 | ((argb & 0x00ff) << 4)
                 | (argb & 0x000f);

        A = (byte)((argb >> 24) & 0xff);
        R = (byte)((argb >> 16) & 0xff);
        G = (byte)((argb >> 8) & 0xff);
        B = (byte)(argb & 0xff);
    }

    
    public string ToVT100ForegroundString() => $"\x1b[38;2;{R};{G};{B}m";

    public string ToVT100BackgroundString() => $"\x1b[48;2;{R};{G};{B}m";


    public static implicit operator RGBAColor(uint argb) => new RGBAColor(argb);

    public static implicit operator RGBAColor(ConsoleColor color) => color_scheme[color];

    private static readonly Dictionary<ConsoleColor, RGBAColor> color_scheme = new()
    {
        [ConsoleColor.Black      ] = 0xff000000,
        [ConsoleColor.DarkBlue   ] = 0xff000080,
        [ConsoleColor.DarkGreen  ] = 0xff008000,
        [ConsoleColor.DarkCyan   ] = 0xff008080,
        [ConsoleColor.DarkRed    ] = 0xff800000,
        [ConsoleColor.DarkMagenta] = 0xff800080,
        [ConsoleColor.DarkYellow ] = 0xff808000,
        [ConsoleColor.Gray       ] = 0xffc0c0c0,
        [ConsoleColor.DarkGray   ] = 0xff808080,
        [ConsoleColor.Blue       ] = 0xff0000ff,
        [ConsoleColor.Green      ] = 0xff00ff00,
        [ConsoleColor.Cyan       ] = 0xff00ffff,
        [ConsoleColor.Red        ] = 0xffff0000,
        [ConsoleColor.Magenta    ] = 0xffff00ff,
        [ConsoleColor.Yellow     ] = 0xffffff00,
        [ConsoleColor.White      ] = 0xffffffff,
    };
}

https://github.com/Unknown6656/Unknown6656.Core/blob/master/Unknown6656.Core/Imaging/RGBAColor.cs

Usage

public void Main()
{
    ConsoleExtensions.ForegroundColor = 0xfe80; // set color using hex literals.
    ConsoleExtensions.BackgroundColor = ConsoleColor.Blue; // traditional method.

    Console.WriteLine("Hello World!");
    Console.WriteLine("\x1b[4mHello World!\x1b[24m"); // this is underlined text using VT100-commands!
}

Maybe this feature could be handy for you...

Support build server pipelines (add feature to pause output & flush)

Support build server pipelines (add feature to pause output & flush)

This is specifically so that Konsole can be used on build servers ; e.g. AzureDevOps pipeline stage.
It's not possible to use printat or scroll (not a full list) when redirecting output.

Trying to set cursorVisible when in an AzureDevops build script, will thrown an IO ? Exception.

What I'd like to see is the ability to detect running on a build server or that these permissions are not available and then either manually or automatically fall back to either writing to a hidden buffer and flushing the result of the buffer to output on demand, or automatically when finished scope. e.g. in a using.

Alternatively create some build server friendly versions of progressbar and other ways of reporting on some long running task when you can't change cursor position.

Add support to fields to Form.Write

Right now forms support only class properties. It would be nice to add fields too.

class Test
{
    public int P { get; } //shown when outputting through a form
    public int F;            //not shown when outputting through a form
}
new Form().Write(new Test());

Prevent console window resize

Thank you for your great library!
However, I ran into the problem that resizing of the console messes up the styling.
I have read your closed "issue" about it, but I cannot figure out how to implement it with the current version.
Could you give me a hint?
Thx!

Incorrect percentage display

Hi, I found percentage display issue. When I create ProgressBarSlim with max as 100:
var bar = new ProgressBarSlim(100, ProgressWindow);
and I call 10 times:
bar.Next("Test");
I can see the following numbers 0, 1, 2, 3, 5, 5, 7, 7, 9, 10.

I looked at the code and noticed that the problem is with casting floats that are not written to variables:
image
Convert.Int32(percentage * 100) also works if you don't want variables

Line Draw when changing from double line to single and merging (error)

bug when changing from double line to single and merging (error)

            var c = new Window(60,20);
            var draw = new Draw(c, LineThickNess.Double);
            draw.Box(2, 2, 42, 8, "my test box", LineThickNess.Double)
                .Line(2, 5, 42, 5, LineThickNess.Double)
                .Line(4, 5, 4, 8, LineThickNess.Single);

gives you

  ╔═════════════ my test box ═════════════╗
  ║                                       ║
  ║                                       ║
  ╠═╦═════════════════════════════════════╣
  ║ │                                     ║
  ║ │                                     ║
  ╚═╩═════════════════════════════════════╝

and should give you

  ╔═════════════ my test box ═════════════╗
  ║                                       ║
  ║                                       ║
  ╠═╤═════════════════════════════════════╣
  ║ │                                     ║
  ║ │                                     ║
  ╚═╧═════════════════════════════════════╝

new Window(width, height) does not set the parent (host) console top to below newly created window

When creating an inline window, the cursor of the host needs to be moved to below the newly created window, so that host processes can continue to write to the console oblivious of the fact that something new is going to be written to the console at the same time.

this test fails

            var console = new MockConsole(10, 9);
            Window.HostConsole = console;
            console.WriteLine("one");
            var box1 = new Window(5, 3);
            box1.WriteLine("aaaaa");
            box1.WriteLine("bbbbb");
            box1.Write("ccccc");
            console.WriteLine("two");
            var box2 = new Window(5, 3);
            box2.WriteLine("aaaaa");
            box2.WriteLine("bbbbb");
            box2.Write("ccccc");
            console.Write("Under B");
            var expected = new[]
            {
                "one       ",
                "aaaaa     ",
                "bbbbb     ",
                "ccccc     ",
                "two       ",
                "aaaaa     ",
                "bbbbb     ",
                "ccccc     ",
                "Under B   "
            };
            console.Buffer.Should().BeEquivalentTo(expected);

disable console screen resizing

When running a console executable in windows, if you resize the console window while the app is running it totally wrecks the layout. Like below ... (this is not a bug with Konsole, it's a behavior of windows consoles, there's a checkbox setting in options, that is on by default to "wrap text on resize". ) Using platform detection it might be possible to provide a switch to lock the window from being resized. Alternatively, launch a custom console emulator? This issue goes hand in hand with issue #32 as a core requirement for Konsole to be able to be used to build commercial applications and utilities. If the speed of Konsole can be dramatically improved, then detecting resize and offering a Redraw facility similar to Winforms then becomes a realistic option.

Capture

Capture

Itemless based progress

I have a single task that gives updates over time using a double with rangle 0-1. Showing this is a bit of a pain. I currently pretend to have 10,000 items and just multiply the double by that to get the number of items done. The main issue with this method is the Item X of 10000 show which is invalid. Would be nice to have a mode of showing progress of a single task.

Window.Open not showing Title

see screenshot and code from "Window.Open with Single line thickness opens with double"
Window.Open with a title, should show the title.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.