Coder Social home page Coder Social logo

c-sharp-console-gui-framework's Introduction

ConsoleGUI

ConsoleGUI is a simple layout-driven .NET framework for creating console-based GUI applications.

It provides most essential layout management utilities as well as a set of basic controls.

The example above is not really a playable chess game. The board on the left is simply just a grid with some text in it - it's here for display purposes only. But of course, with a little bit of code behind, it could be made interactive.

Supported platforms

This framework is platform agnostic and dependency free. The library targets .NET standard 2.0 and should run fine on both Windows and Linux machines.

Motivation

What sets this library apart from other projects that provide similar functionalities, is the fact that the ConsoleGUI framework is fully layout-driven. In this regard it’s more like WPF or HTML, than for example Windows Forms. You don’t specify exact coordinates at which a given control should reside, but rather let stack panels, dock panels and other layout managers do their work. I don’t claim it’s THE right way of doing things, it’s just what my background is.

More details about this project (as well as a glimpse at the working example) can be found in this video: https://youtu.be/YIrmjENTaaU

Setup

First install the NuGet package:

dotnet add package ConsoleGUI

then include required namespaces in your code:

using ConsoleGUI;
using ConsoleGUI.Controls;
using ConsoleGUI.Space;

and finally setup the ConsoleManager:

// optional: adjusts the buffer size and sets the output encoding to the UTF8
ConsoleManager.Setup();

// optional: resizes the console window (the size is set in a number of characters, not pixels)
ConsoleManager.Resize(new Size(150, 40));

// sets the main layout element and prints it on the screen
ConsoleManager.Content = new TextBlock { Text = "Hello world" };

And that's it. As you can see most of those steps are optional, depending on how you want to configure your window.

After that, whenever you make a change to any of the controls within the UI tree, the updates will be propagated and displayed automatically. No manual Redraw() calls are required.

Threading

The ConsoleGUI (as many other UI frameworks) is not thread-safe. All UI changes should be performed from the same thread.

Compatibility mode

By default, the ConsoleGUI uses the true color formatting to provide the best possible user experience by supporting 16777216 foreground and background colors. Unfortunately this way of formatting is not supported by all terminals. Some of them (depending on the platform and software) support only 16bit colors, or just the 4bit colors defined in the ConsoleColor enum.

Terminals that DO NOT support the true color formatting are (for example): powershell.exe and cmd.exe.

Terminals that DO support the true color formatting are (for example): the new Windows Terminal and the terminal that is built in into the VS.

If after starting the application you see the same output as the one on the left, you have to enable the compatibility mode by changing the Console interface used by the framework:

ConsoleManager.Console = new SimplifiedConsole();

The SimplifiedConsole translates all of the RGB colors into 4bit values of the ConsoleColor enum. It also prevents the bottom-right character from being printed, to avoid the bug visible on the right image (in some terminals printing the last character can cause the buffer to scroll).

If the output is still being printed incorrectly (or if you want to print it on a non-standard console) you can implement the IConsole interface yourself and set the Console property of the ConsoleManager with the instance of your custom class. Alternatively you can also derive from the StandardConsole class (which is being used by default) and only override its virtual void Write(Position, in Character) method.

Responsiveness

If the window size is not set explicitly, the layout will be adjusted to the initial size of that window. It's important to note that this framework doesn't detect terminal size changes automatically (as there is no standard way of listening to such events). If the user resizes the window manually, the layout will become broken.

To adjust the layout to the updated size of the window, remember to call the AdjustBufferSize method of the ConsoleManager on every frame (on every iteration of your program's main loop). It will compare the current window size with its previous value and, if necessary, redraw the entire screen.

Basic controls

This is a list of all available controls:

Background

Sets the background color of the Content control. If the Important property is set, the background color will be updated even if the stored control already sets its own background color.

Border

Draws a border around the Content control. The BorderPlacement and the BorderStyle can be adjusted to change the look of the generated outline.

Boundary

Allows the user to modify the MinWidth, MinHeight, MaxWidth and MaxHeight of the Content control in relation to its parent control.

Especially useful to limit the space taken by controls that would otherwise stretch to fill all of the available space (like when storing a HorizontalStackPanel within a horizontal DockPanel)

Box

Aligns the Content control vertically (Top/Center/Bottom/Stretch) and horizontally (Left/Center/Right/Stretch).

BreakPanel

Breaks a single line of text into multiple lines based on the available vertical space and new line characters. It can be used with any type of control (TextBox, TextBlock but also HorizontalStackPanel and any other).

Canvas

Can host multiple child controls, each displayed within a specified rectangle. Allows content overlapping.

DataGrid

Displays Data in a grid based on provided column definitions.

The ColumnDefinition defines the column header, its width and the data selector. The selector can be used to extract text from a data row, specify that cell's color, or even define a custom content generator.

Decorator

Decorator is an abstract class that allows the user to define custom formatting rules (like applying foreground and background colors based on the content and position of a cell), while preserving the layout of the Content control. See the SimpleDecorator class in the ConsoleGUI.Example project for an example.

DockPanel

DockPanel consists of two parts: DockedControl and FillingControl. The DockedControl is placed within the available space according to the Placement property value (Top/Right/Bottom/Left). The FillingControl takes up all of the remaining space.

Grid

Splits the available space into smaller pieces according to the provided Columns and Rows definitions. Each cell can store up to one child control.

HorizontalSeparator

Draws a horizontal line.

HorizontalStackPanel

Stacks multiple controls horizontally.

Margin

Adds the Offset around the Content control when displaying it. It affects both the MinSize and the MaxSize of the IDrawingContext.

Overlay

Allows two controls to be displayed on top of each other. Unlike the Canvas, it uses its own size when specifying size limits for child controls.

Style

Modifies the Background and the Foreground colors of its Content.

TextBlock

Displays a single line of text.

TextBox

An input control. Allows the user to insert a single line of text.

VerticalScrollPanel

Allows its Content to expand infinitely in the vertical dimension and displays only the part of it that is currently in view. The ScrollBarForeground and the ScrollBarBackground can be modified to adjust the look of the scroll bar.

VerticalSeparator

Draws a vertical line.

VerticalStackPanel

Stacks multiple controls vertically.

WrapPanel

Breaks a single line of text into multiple lines based on the available vertical space. It can be used with any type of control (TextBox, TextBlock but also HorizontalStackPanel and any other).

Creating custom controls

The set of predefined control is relatively small, but it's very easy to create custom ones. There are two main ways to do it.

Inheriting the SimpleControl class

If you want to define a control that is simply composed of other controls (like a text box with a specific background and border), inheriting from the SimpleControl class is the way to go.

All you have to do is to set the protected Content property with a content that you want to display.

internal sealed class MyControl : SimpleControl
{
	private readonly TextBlock _textBlock;

	public MyControl()
	{
		_textBlock = new TextBlock();

		Content = new Background
		{
			Color = new Color(200, 200, 100),
			Content = new Border
			{
				Content = _textBlock
			}
		};
	}

	public string Text
	{
		get => _textBlock.Text;
		set => _textBlock.Text = value;
	}
}

Implementing the IControl interface or inheriting the Control class

This approach can be used to define fully custom controls. All of the basic controls within this library are implemented this way.

The IControl interface requires providing 3 members:

public interface IControl
{
	Character this[Position position] { get; }
	Size Size { get; }
	IDrawingContext Context { get; set; }
}

The [] operator must return a character that is to be displayed on the specific position. The position is defined relative to this control's space and not to the screen.

The control can also notify its parent about its internal changes using the provided Context. The IDrawingContext interface is defined as follows:

public interface IDrawingContext
{
	Size MinSize { get; }
	Size MaxSize { get; }

	void Redraw(IControl control);
	void Update(IControl control, in Rect rect);

	event SizeLimitsChangedHandler SizeLimitsChanged;
}

If only a part of the control has changed, it should call the Update method, providing a reference to itself and the rect (once again - in its local space) that has to be redrawn. If the Size of the control has changed or the entire control requires redrawing, the control should call the Redraw method of its current Context.

The Context is also used to notify the child control about changes in size limits imposed on it by its parent. The child control should listen to the SizeLimitsChanged event and update its layout according to the MinSize and MaxSize values of the current Context.

When defining a custom control that can host other controls, you might have to implement a custom IDrawingContext class.

Instead of implementing the IControl and IDrawingContext directly, you can also use the Control and DrawingContext base classes. They allow for a similar level of flexibility, at the same time providing more advanced functionalities.

The DrawingContext is an IDisposable non-abstract class that translates the parent's space into the child's space based on the provided size limits and offset. It also ensures that propagated notifications actually come from the hosted control and not from controls that were previously assigned to a given parent.

The Control class not only trims all of the incoming and outgoing messages to the current size limits but also allows to temporarily freeze the control so that only a single update message is generated after multiple related changes are performed.

For more information on how to define custom controls using the IControl/IDrawingContext interfaces or the Control/DrawingContext classes, please see the source code of one of the controls defined within this library.

Input

As the standard Console class doesn't provide any event-based interface for detecting incoming characters, the availability of input messages has to be checked periodically within the main loop of your application. Of course, it's not required if your layout doesn't contain any interactive components.

To handle pending input messages, call the ReadInput method of the ConsoleManager class. It accepts a single argument being a collection of IInputListener objects. You can define this collection just once and reuse it - it specifies the list of input elements that are currently active and should be listening to keystrokes. The order of those elements is important, because if one control sets the Handled property of the provided InputEvent, the propagation will be terminated.

var input = new IInputListener[]
{
	scrollPanel,
	tabPanel,
	textBox
};

for (int i = 0; ; i++)
{
	Thread.Sleep(10);
	ConsoleManager.ReadInput(input);
}

The IInputListener interface is not restricted only for classes that implement the IControl interface, but can also be used to define any custom (user defined) controllers that manage application behavior.

Forms

As you might have noticed, there is no general purpose Form control available in this framework. That’s because it’s very hard to come up with a design that would fit all needs. Of course such an obstacle is not a good reason on its own, but at the same time it’s extremely easy to implement a tailor made form controller within the target application itself. Here is an example:

class FromController : IInputListener
{
	IInputListener _currentInput;
	
	// ...

	public void OnInput(InputEvent inputEvent)
	{
		if (inputEvent.Key.Key == ConsoleKey.Tab)
		{
			_currentInput = // nextInput...
			inputEvent.Handled = true;
		}
		else
		{
			_currentInput.OnInput(inputEvent)
		}
	}
}

After implementing it, all you have to do is to initialize an instance of this class with a list of your inputs and call the ConsoleManager.ReadInput(fromControllers) on each frame.

The biggest strength of this approach is that you decide what is the order of controls within the form, you can do special validation after leaving each input, create a custom layout of the form itself, highlight currently active input, and much, much more. I believe it’s a good tradeoff.

Mouse

The ConsoleGUI framework does support mouse input, but it doesn’t create mouse event bindings automatically. That's because intercepting and translating mouse events is a very platform-specific operation that might vary based on the operating system and the terminal you are using.

An example code that properly handles mouse events in the Powershell.exe and cmd.exe terminals can be found in the ConsoleGUI.MouseExample/MouseHandler.cs source file. (To use this example you will have to disable the QuickEdit option of your console window).

When creating your own bindings, all you have to do from the framework perspective, is to set the MousePosition and MouseDown properties of the ConsoleManager whenever a user interaction is detected. For example:

private static void ProcessMouseEvent(in MouseRecord mouseEvent)
{
	ConsoleManager.MousePosition = new Position(mouseEvent.MousePosition.X, mouseEvent.MousePosition.Y);
	ConsoleManager.MouseDown = (mouseEvent.ButtonState & 0x0001) != 0;
}

The ConsoleManager will take care of the rest. It will find a control that the cursor is currently hovering over and raise a proper method as described in the IMouseListener interface.

Performance

This library is designed with high performance applications in mind. It means that if a control requests an Update, only the specified screen rectangle will be recalculated, and only if all of its parent controls agree that this part of the content is actually visible.

As the most expensive operation of the whole process is printing characters on the screen, the ConsoleManager defines its own, additional buffer. If the requested pixel (character) didn't change, it's not repainted.

Contributions

I'm open to all sorts of contributions and feedback.

Also, please feel free to request new controls/features through github issues.

c-sharp-console-gui-framework's People

Contributors

bt-02 avatar ciwchris avatar ittennull avatar tomaszrewak 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  avatar  avatar  avatar

c-sharp-console-gui-framework's Issues

Sadly not working under Linux / JetBrains Rider

It just puts out an unhandled exception:

Unhandled exception. System.PlatformNotSupportedException: Operation is not supported on this platform.GUI.Example/bin/Debug/netcoreapp3.0/ConsoleGUI.Example.dll at System.ConsolePal.SetWindowPosition(Int32 left, Int32 top) at System.Console.SetWindowPosition(Int32 left, Int32 top) at ConsoleGUI.ConsoleManager.ResizeBuffer(Int32 width, Int32 height) in /home/benjamin.piepiora/Downloads/C-sharp-console-gui-framework-master/ConsoleGUI/ConsoleManager.cs:line 139 at ConsoleGUI.ConsoleManager.Setup() in /home/benjamin.piepiora/Downloads/C-sharp-console-gui-framework-master/ConsoleGUI/ConsoleManager.cs:line 124 at ConsoleGUI.Example.Program.Main() in /home/benjamin.piepiora/Downloads/C-sharp-console-gui-framework-master/ConsoleGUI.Example/Program.cs:line 266

Is it possible to work around that?

Not working example

After download zip file and run example there only works Key UP/DOWN on Popup.
Example not reacts on mouse when i want e.g change tabs

.NET Standard

Please make it clear in the README that this targets netstandard2.0 (a good thing!) and not .NET framework :)

Expanding elements without using `Box`

Just learning the framework. Is this as expected? I thought it required a ´Box´ in order for the content to be centered.

class Program
{
    static void Main(string[] args)
    {
        ConsoleManager.Setup();
        ConsoleManager.Resize(new Size(150, 40));

        ConsoleManager.Content =

                new Border()
                {
                    Content =
                        new HorizontalStackPanel()
                        {
                            Children = new[] { new TextBlock { Text = "Hello world" }, new TextBlock { Text = "22Hello world22" }, }
                        }
                }
            ;

        Console.ReadKey();
    }

image

Missing using directive in TabPanel example

There is a missing using directive in the TabPanel example. When I was trying to implement the example this error was thrown:
error CS0246: The type or namespace name 'IControl' could not be found (are you missing a using directive or an assembly reference?)

The fix was to add using ConsoleGUI; to the TabPanel.cs file. I suggest that the example is updated unless it is an error that is solely on my machine for some reason.

Custom control margin not updating.

I have a custom control for labels with an equal spacing using the Margin control:

    internal class LabelControl: SimpleControl
    {
        private readonly TextBlock _label;
        private readonly TextBlock _value;
        private Offset _offset;
        public LabelControl()
        {
            _label = new TextBlock();   //┤
            _value = new TextBlock();
            _offset = new Offset(0,0,0,0);
            Content = new Margin
            {
                Offset = _offset,
                Content = new Overlay
                {
                    
                    TopContent = _label,
                    BottomContent = _value
                }
            };
        }
        public Offset Margin
        {
            get => _offset;
            set => _offset = value;
        }
        public string Label
        {
            get => _label.Text;
            set => _label.Text = value;
        }
        public string Value
        {
            get => _value.Text;
            set => _value.Text = value;
        }
    }

Changing parameters Label and Value within each instance after initialization changes the text within the accordingly assigned TextBlock controls just fine (e.g, mylabelcontrol.Value = "new value", mylabelcontrol.Label = "new label").
However, assigning a new Offset for the Margin control after initialization yields no result (e.g: mylabelcontrol.Margin = new Offset(1,2,3,4)). Is this behaviour by design?

correct formatting?

Just learning the framework. Is this as expected?

class Program
{
    static void Main(string[] args)
    {
        ConsoleManager.Setup();
        ConsoleManager.Resize(new Size(150, 40));
        ConsoleManager.Content =
            new Box()
            {
                Content = new Border()
                {
                    Content =
                        new HorizontalStackPanel()
                        {
                            Children = new[] { new TextBlock { Text = "Hello world" }, new TextBlock { Text = "22Hello world22" }, }
                        }
                }
            };

        Console.ReadKey();
    }
}

image

Feature request: Character column

Support for a control for drawing vertical columns of characters.

So far I've tried using a BreakPanel and a VerticalSeparator within a Box control.
Both methods yield inconsistent results. A BreakPanel displays no text inside a Box control's content, unlike a plain TextBlock (Which doesn't repect line break characters). And a VerticalSeparator for some reason fills the whole Box control both diagonally and vertically with the VerticalSeparator's characters.

In case these shortcomings aren't replicable, here is my code:

_window = new Overlay                                                                  //Base window element for vertical bar graph
{
    TopContent = new Margin                                                          //Title formatting for window element
    {
        Offset = new Offset(1, 0, 0, 0),                                               
        Content = _titleText                                                          
    },
    BottomContent = new Border                                                     //Borders for Window element
    {
        BorderPlacement = BorderPlacement.All,                                         
        BorderStyle = BorderStyle.Single,                                              
                                                                                       
        Content = new Box                                                                  //Box control for vertical character columns
        {
            HorizontalContentPlacement = Box.HorizontalPlacement.Center,
            VerticalContentPlacement = Box.VerticalPlacement.Center,
            Content = _barStack                                                             //_barStack to be used for character column control
        }
    }
};

How to disable border on DataGrid?

Thanks for making this amazing framework of making console GUI.

I don't know how to disable the border line between rows in DataGrid.

feature: listview

What are your thoughts on listviews? Is it something I have to compose from existing components?

for my application I need to display a lot of lines and have the user select one of them. The line currently being the candidate for selection is hightligted and some hotkey defines that the selection is made (possibly enter).

Naviagation of the current selected line needs to be accessed from outside the component (eg. a method MoveUpOne(), MoveUpPage(), MoveDownOne(),..). This allows me to combine the listview with a textfield so that the content of the listview is filtered.

Multi color multi line textbox

A feature idea. Change the color or background color based on some rules.

E.g. first char on the line is simple rule that governs colouring git diffs

Bug: BreakPanels display newline as an empty character

Hello.
I have an issue, where drawing over a Border control with a BreakPanel results in every newline character \n overdrawn as an empty character over the underlying control, even when using an Overlay control.
For example:

Content = new BreakPanel
{
    Content = new TextBlock {Text = "┬\n│\n│\nTest\n[Value]" }
}

Drawn over a border looks like this:
fig0

Alternative frameworks

Found out about a parallel project to yours, here: https://parall.ax/blog/view/3131/vtop-revisiting-the-activity-monitor

Quote:

I decided to write vtop using Node.js. It’s built on Chrome’s V8 JavaScript engine and allows you to write fast and scalable applications. This choice could pave the way for a web-based frontend to be added in the future. JavaScript is coming into it’s own – it’s no longer the scrappy, badly implemented language that everyone used to make sparkles follow your cursor on your Geocities page. Node.js has evolved the language – it’s now a a fully formed toolchain with a thriving community. There’s a Node package for just about anything you can think of, you can really hit the ground running by picking up other people’s modules instead of writing from scratch.

At the beginning of the rewrite, I made an outline using simple box drawing characters that I used to love playing with in my early DOS programming days. While this worked ok, I felt there might be an easier way. I’d seen ncurses and wondered if there was anything more modern kicking about. I eventually came across Blessed.

Blessed abstracts away the complexities of drawing a GUI in the terminal. You tell it where to draw boxes, and they are automatically resized based on the terminal width and height. You can also listen to scroll wheel and click events to enable even easier interaction. I’d highly recommend you check it out.

DataGrid pages

Hi,
would it be possible to have side page scrolling for the DataGrid?
Let's say the DataGrid contains more items than the console can contain for it's size, it could display only a determinate amount of them and allow page navigation with left and right arrows, cycling through the items.
My current solution for this is having custom input on the tab the DataGrid is in, and 2 Arrays, one as "all items" and one as the DataGrid source. On arrow press, i update the DataGrid source array with the range of items needed, it works fine but can you perhaps suggest of a better way?

Please tell me if I'm missing something, I am aware of the endless vertical panel, but i need not to lose track of how many items are behind.

Thanks

Create Controls of XML-files

It might be possible to use XML-files to create a "Control-Tree", it would be a bit easier to use and is just an idea...

Create an editor

I kind of need some guidance. I am trying to implement a simple editor. Im using the new BreakPanel. My issue is that the content is larger than the allocated screen. So when I move right I expect the content to change when I reach the end of the lowest line. It doesn't

I also would expect the TextBox to support arrow up/down.

Maybe I need to

	class Program
	{
		static void Main(string[] args)
		{
			// optional: adjusts the buffer size and sets the output encoding to the UTF8
			ConsoleManager.Setup();

			// optional: resizes the console window (the size is set in a number of characters, not pixels)
			ConsoleManager.Resize(new Size(50, 10));

			var textBox = new TextBox()
			{
				
				Text = @"1 alsjdasldk
2 asjdkalsj
3 iieieiei
4 fkkrkrk
5 fldksjf
6 fsdlkfjls
7 dslakfj
8 sldkf
9 sdlkf
10 dkdkdkdk
11 jdjdjdjd
12 qpqpqpqpq

the end", 
			};

			ConsoleManager.Content =
				new Border()
				{
					Content = new BreakPanel(){Content =  textBox, }
				};

			var input = new IInputListener[] {textBox};

			while (true)
			{
				Thread.Sleep(10);
				ConsoleManager.ReadInput(input);
			}

			Console.ReadKey();
		}
	}

wrapping it in a ScrollPanel gives a nice schrollbar. But no scrolling

        ConsoleManager.Content =
            new Border()
            {
                Content =
                    new VerticalScrollPanel()
                    {
                        Content = new BreakPanel() {Content = textBox,}
                    }
            };

IOException thrown when calling AdjustWindowSize

Calling ConsoleManager.AdjustWindowSize() in a tight loop while resizing the window with the mouse can cause a System.IO.IOException to be thrown.

Exception Details

System.IO.IOException: The parameter is incorrect.
   at System.ConsolePal.SetCursorPosition(Int32 left, Int32 top)
   at ConsoleGUI.Api.StandardConsole.set_Size(Size value)
   at ConsoleGUI.ConsoleManager.Resize(Size& size)
   at ConsoleGUI.ConsoleManager.AdjustWindowSize()

Might want to add a SafeConsole.SetCursorPosition method.

new feature: menus

Have you given any thoughts on supporting menus with perhaps sub-menus?

Updating Content within InputListener

Hi, thanks for a great library.
I was testing something out for a project in which I want to update the ConsoleManager.Content from within the IInputListener so I created a few test files and it seems that the window isn't updating properly. I wondered whether perhaps it was because you shouldn't be able to do this, or if it is a genuine bug (or maybe something I'm missing)?

Code

Program.cs

ConsoleManager.Setup();
ConsoleManager.Content = new TestView(1);
var flag = new TestFlag();
var listener = new TestListener(2, flag);
while(!flag.Exit)
{
    Thread.Sleep(10);
    ConsoleManager.ReadInput(new[] { listener });
}

TestFlag.cs

public class TestFlag
{
    public bool Exit { get; set; }
}

TestListener.cs

public class TestListener : IInputListener
{
    private readonly int i;
    private readonly TestFlag flag;

    public TestListener(int i, TestFlag flag)
    {
        this.i = i;
        this.flag = flag;
    }

    public void OnInput(InputEvent inputEvent)
    {
        if (inputEvent.Key.Key == System.ConsoleKey.Enter)
        {
            ConsoleManager.Content = new TestView(i);
            var listener = new TestListener(i + 1, flag);
            while (!flag.Exit)
            {
                ConsoleManager.ReadInput(new[] { listener });
            }
            inputEvent.Handled = true;
        }
        else if(inputEvent.Key.Key == System.ConsoleKey.Q)
        {
            flag.Exit = true;
            inputEvent.Handled = true;
        }
    }
}

TestView.cs

public class TestView : SimpleControl
{
    public TestView(int i)
    {
        Content = new Border { Content = new TextBlock { Text = $"test {i}" } };
    }
}

Result

The first iteration appears as you would expect:
image

However subsequent iterations look like only the changed cell is displayed (no border or "test"):
image

Add mouse support

So I've spent some time thinking about how may I implement the mouse support for this library and this it the proposal:

  • The library will continue to be dependency free and platform agnostic
  • The user will be able to easily hook up a library of choice to this framework (for example the MouseKeyHook: https://github.com/gmamaladze/globalmousekeyhook)
  • The Character struct will be extended by two fields:
    • Control - the top level IControl that produced this character
    • Position - a local position of the Control from which the character was produced
  • Some interactive controls (like a Button) will be able to override the Control and Position fields of characters they are passing through
  • The ConsoleManager will be extended with a method that returns a Control and Position values of a specific "pixel".
  • A new IMouseListener interface will be added (the list of methods to be decided, but probably OnMuseDown(Position local), OnMouseMove(Position local), OnMoseUp(Position local))
  • Whenever an external library detects a mouse input, the user should get the Control and its local Position at the specified global position, check if that control is in fact an instance of the IMouseListener, and call the proper callback.
  • The rest is up to the internal implementation of the control

That should keep the interface simple and make it possible to hook up the mouse using one of the already existing projects.

Support Console Resize w/ Caveat Lector

@bpiepiora wrote:

Is it possible to detect size changes of the console and update the content accordingly?

@jzabroski wrote:

Handling console window size changes is not safe: https://stackoverflow.com/a/34208406

@TomaszRewak wrote

Yeah, unfortunately that's the case.
But actually thinking about it I could add a new method to the ConsoleManager that the user would have to call each "frame" (the same way it's currently implemented for keyboard input handling) that would simply check if the size of the window has changed as compared to the value from the previous frame.
It wouldn't be 100% foolproof (because if the window size would change to a different value and back to the initial value during one frame it might break the layout), but it would still be better than nothing.

Cleanup console

Is it possible to use the library to prompt an user for input, and then cleanup all the elements and proceed the console application as if nothing happened?

Right now all console output is removed after drawing elements on the screen.

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.