mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-27 00:07:58 +01:00
47 lines
28 KiB
JSON
47 lines
28 KiB
JSON
{
|
|
"articles/index.html": {
|
|
"href": "articles/index.html",
|
|
"title": "Conceptual Documentation",
|
|
"keywords": "Conceptual Documentation Terminal.Gui Overview Keyboard Event Processing Event Processing and the Application Main Loop TableView Deep Dive TreeView Deep Dive"
|
|
},
|
|
"articles/keyboard.html": {
|
|
"href": "articles/keyboard.html",
|
|
"title": "Keyboard Event Processing",
|
|
"keywords": "Keyboard Event Processing Keyboard events are sent by the Main Loop to the Application class for processing. The keyboard events are sent exclusively to the current Toplevel , this being either the default that is created when you call Application.Init , or one that you created an passed to Application.Run(Toplevel) . Flow Keystrokes are first processes as hotkeys, then as regular keys, and there is a final cold post-processing event that is invoked if no view processed the key. HotKey Processing Events are first send to all views as a \"HotKey\", this means that the View.ProcessHotKey method is invoked on the current toplevel, which in turns propagates this to all the views in the hierarchy. If any view decides to process the event, no further processing takes place. This is how hotkeys for buttons are implemented. For example, the keystroke \"Alt-A\" is handled by Buttons that have a hot-letter \"A\" to activate the button. Regular Processing Unlike the hotkey processing, the regular processing is only sent to the currently focused view in the focus chain. The regular key processing is only invoked if no hotkey was caught. Cold-key Processing This stage only is executed if the focused view did not process the event, and is broadcast to all the views in the Toplevel. This method can be overwritten by views that want to provide accelerator functionality (Alt-key for example), but without interefering with normal ProcessKey behavior. Key Bindings Terminal.Gui supports rebinding keys. For example the default key for activating a button is Enter. You can change this using the ClearKeybinding and AddKeybinding methods: var btn = new Button (\"Press Me\"); btn.ClearKeybinding (Command.Accept); btn.AddKeyBinding (Key.b, Command.Accept); The Command enum lists generic operations that are implemented by views. For example Command.Accept in a Button results in the Clicked event firing while in TableView it is bound to CellActivated . Not all commands are implemented by all views (e.g. you cannot scroll in a Button). To see which commands are implemented by a View you can use the GetSupportedCommands() method. Not all controls have the same key bound for a given command, for example Command.Accept defaults to Key.Enter in a Button but defaults to Key.Space in RadioGroup . Global Key Handler Sometimes you may want to define global key handling logic for your entire application that is invoked regardless of what Window/View has focus. This can be achieved by using the Application.RootKeyEvent event."
|
|
},
|
|
"articles/mainloop.html": {
|
|
"href": "articles/mainloop.html",
|
|
"title": "Event Processing and the Application Main Loop",
|
|
"keywords": "Event Processing and the Application Main Loop The method Application.Run that we covered before will wait for events from either the keyboard or mouse and route those events to the proper view. The job of waiting for events and dispatching them in the Application is implemented by an instance of the MainLoop class. Mainloops are a common idiom in many user interface toolkits so many of the concepts will be familiar to you if you have used other toolkits before. This class provides the following capabilities: Keyboard and mouse processing .NET Async support Timers processing Invoking of UI code from a background thread Idle processing handlers Possibility of integration with other mainloops. On Unix systems, it can monitor file descriptors for readability or writability. The MainLoop property in the the Application provides access to these functions. When your code invokes Application.Run (Toplevel) , the application will prepare the current Toplevel instance by redrawing the screen appropriately and then calling the mainloop to run. You can configure the Mainloop before calling Application.Run, or you can configure the MainLoop in response to events during the execution. The keyboard inputs is dispatched by the application class to the current TopLevel window this is covered in more detail in the Keyboard Event Processing document. Async Execution On startup, the Application class configured the .NET Asynchronous machinery to allow you to use the await keyword to run tasks in the background and have the execution of those tasks resume on the context of the main thread running the main loop. Once you invoke Application.Main the async machinery will be ready to use, and you can merely call methods using await from your main thread, and the awaited code will resume execution on the main thread. Timers Processing You can register timers to be executed at specified intervals by calling the AddTimeout method, like this: void UpdateTimer () { time.Text = DateTime.Now.ToString (); } var token = Application.MainLoop.AddTimeout (TimeSpan.FromSeconds (20), UpdateTimer); The return value from AddTimeout is a token value that you can use if you desire to cancel the timer before it runs: Application.MainLoop.RemoveTimeout (token); Idle Handlers You can register code to be executed when the application is idling and there are no events to process by calling the AddIdle method. This method takes as a parameter a function that will be invoked when the application is idling. Idle functions should return true if they should be invoked again, and false if the idle invocations should stop. Like the timer APIs, the return value is a token that can be used to cancel the scheduled idle function from being executed. Threading Like other UI toolkits, Terminal.Gui is generally not thread safe. You should avoid calling methods in the UI classes from a background thread as there is no guarantee that they will not corrupt the state of the UI application. Generally, as there is not much state, you will get lucky, but the application will not behave properly. You will be served better off by using C# async machinery and the various APIs in the System.Threading.Tasks.Task APIs. But if you absolutely must work with threads on your own you should only invoke APIs in Terminal.Gui from the main thread. To make this simple, you can use the Application.MainLoop.Invoke method and pass an Action . This action will be queued for execution on the main thread at an appropriate time and will run your code there. For example, the following shows how to properly update a label from a background thread: void BackgroundThreadUpdateProgress () { Application.MainLoop.Invoke (() => { progress.Text = $\"Progress: {bytesDownloaded/totalBytes}\"; }); } Integration With Other Main Loop Drivers It is possible to run the main loop in a way that it does not take over control of your application, but rather in a cooperative way. To do this, you must use the lower-level APIs in Application : the Begin method to prepare a toplevel for execution, followed by calls to MainLoop.EventsPending to determine whether the events must be processed, and in that case, calling RunLoop method and finally completing the process by calling End . The method Run is implemented like this: void Run (Toplevel top) { var runToken = Begin (view); RunLoop (runToken); End (runToken); } Unix File Descriptor Monitoring On Unix, it is possible to monitor file descriptors for input being available, or for the file descriptor being available for data to be written without blocking the application. To do this, you on Unix, you can cast the MainLoop instance to a UnixMainLoop and use the AddWatch method to register an interest on a particular condition."
|
|
},
|
|
"articles/overview.html": {
|
|
"href": "articles/overview.html",
|
|
"title": "Terminal.Gui API Overview",
|
|
"keywords": "Terminal.Gui API Overview Terminal.Gui is a library intended to create console-based applications using C#. The framework has been designed to make it easy to write applications that will work on monochrome terminals, as well as modern color terminals with mouse support. This library works across Windows, Linux and MacOS. This library provides a text-based toolkit as works in a way similar to graphic toolkits. There are many controls that can be used to create your applications and it is event based, meaning that you create the user interface, hook up various events and then let the a processing loop run your application, and your code is invoked via one or more callbacks. The simplest application looks like this: using Terminal.Gui; class Demo { static int Main () { Application.Init (); var n = MessageBox.Query (50, 7, \"Question\", \"Do you like console apps?\", \"Yes\", \"No\"); Application.Shutdown (); return n; } } This example shows a prompt and returns an integer value depending on which value was selected by the user (Yes, No, or if they use chose not to make a decision and instead pressed the ESC key). More interesting user interfaces can be created by composing some of the various views that are included. In the following sections, you will see how applications are put together. In the example above, you can see that we have initialized the runtime by calling the Init method in the Application class - this sets up the environment, initializes the color schemes available for your application and clears the screen to start your application. The Application class, additionally creates an instance of the Toplevel class that is ready to be consumed, this instance is available in the Application.Top property, and can be used like this: using Terminal.Gui; class Demo { static int Main () { Application.Init (); var label = new Label (\"Hello World\") { X = Pos.Center (), Y = Pos.Center (), Height = 1, }; Application.Top.Add (label); Application.Run (); Application.Shutdown (); } } Typically, you will want your application to have more than a label, you might want a menu, and a region for your application to live in, the following code does this: using Terminal.Gui; class Demo { static int Main () { Application.Init (); var menu = new MenuBar (new MenuBarItem [] { new MenuBarItem (\"_File\", new MenuItem [] { new MenuItem (\"_Quit\", \"\", () => { Application.RequestStop (); }) }), }); var win = new Window (\"Hello\") { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill () - 1 }; // Add both menu and win in a single call Application.Top.Add (menu, win); Application.Run (); Application.Shutdown (); } } Views All visible elements on a Terminal.Gui application are implemented as Views . Views are self-contained objects that take care of displaying themselves, can receive keyboard and mouse input and participate in the focus mechanism. Every view can contain an arbitrary number of children views. These are called the Subviews. You can add a view to an existing view, by calling the Add method, for example, to add a couple of buttons to a UI, you can do this: void SetupMyView (View myView) { var label = new Label (\"Username: \") { X = 1, Y = 1, Width = 20, Height = 1 }; myView.Add (label); var username = new TextField (\"\") { X = 1, Y = 2, Width = 30, Height = 1 }; myView.Add (username); } The container of a given view is called the SuperView and it is a property of every View. There are many views that you can use to spice up your application: Buttons , Labels , Text entry , Text view , Radio buttons , Checkboxes , Dialog boxes , Message boxes , Windows , Menus , ListViews , Frames , ProgressBars , Scroll views and Scrollbars . Layout Terminal.Gui supports two different layout systems, absolute and computed \\ (controlled by the LayoutStyle property on the view. The absolute system is used when you want the view to be positioned exactly in one location and want to manually control where the view is. This is done by invoking your View constructor with an argument of type Rect . When you do this, to change the position of the View, you can change the Frame property on the View. The computed layout system offers a few additional capabilities, like automatic centering, expanding of dimensions and a handful of other features. To use this you construct your object without an initial Frame , but set the X , Y , Width and Height properties after the object has been created. Examples: // Dynamically computed var label = new Label (\"Hello\") { X = 1, Y = Pos.Center (), Width = Dim.Fill (), Height = 1 }; // Absolute position using the provided rectangle var label2 = new Label (new Rect (1, 2, 20, 1), \"World\") The computed layout system does not take integers, instead the X and Y properties are of type Pos and the Width and Height properties are of type Dim both which can be created implicitly from integer values. The Pos Type The Pos type on X and Y offers a few options: Absolute position, by passing an integer Percentage of the parent's view size - Pos.Percent(n) Anchored from the end of the dimension - AnchorEnd(int margin=0) Centered, using Center() Reference the Left (X), Top (Y), Bottom, Right positions of another view The Pos values can be added or subtracted, like this: // Set the X coordinate to 10 characters left from the center view.X = Pos.Center () - 10; view.Y = Pos.Percent (20); anotherView.X = AnchorEnd (10); anotherView.Width = 9; myView.X = Pos.X (view); myView.Y = Pos.Bottom (anotherView); The Dim Type The Dim type is used for the Width and Height properties on the View and offers the following options: Absolute size, by passing an integer Percentage of the parent's view size - Dim.Percent(n) Fill to the end - Dim.Fill () Reference the Width or Height of another view Like, Pos , objects of type Dim can be added an subtracted, like this: // Set the Width to be 10 characters less than filling // the remaining portion of the screen view.Width = Dim.Fill () - 10; view.Height = Dim.Percent(20) - 1; anotherView.Height = Dim.Height (view)+1 TopLevels, Windows and Dialogs. Among the many kinds of views, you typically will create a Toplevel view (or any of its subclasses, like Window or Dialog which is special kind of views that can be executed modally - that is, the view can take over all input and returns only when the user chooses to complete their work there. The following sections cover the differences. TopLevel Views Toplevel views have no visible user interface elements and occupy an arbitrary portion of the screen. You would use a toplevel Modal view for example to launch an entire new experience in your application, one where you would have a new top-level menu for example. You typically would add a Menu and a Window to your Toplevel, it would look like this: using Terminal.Gui; class Demo { static void Edit (string filename) { var top = new Toplevel () { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () }; var menu = new MenuBar (new MenuBarItem [] { new MenuBarItem (\"_File\", new MenuItem [] { new MenuItem (\"_Close\", \"\", () => { Application.RequestStop (); }) }), }); // nest a window for the editor var win = new Window (filename) { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill () - 1 }; var editor = new TextView () { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () }; editor.Text = System.IO.File.ReadAllText (filename); win.Add (editor); // Add both menu and win in a single call top.Add (win, menu); Application.Run (top); Application.Shutdown (); } } Window Views Window views extend the Toplevel view by providing a frame and a title around the toplevel - and can be moved on the screen with the mouse (caveat: code is currently disabled) From a user interface perspective, you might have more than one Window on the screen at a given time. Dialogs Dialog are Window objects that happen to be centered in the middle of the screen. Dialogs are instances of a Window that are centered in the screen, and are intended to be used modally - that is, they run, and they are expected to return a result before resuming execution of your application. Dialogs are a subclass of Window and additionally expose the AddButton API which manages the layout of any button passed to it, ensuring that the buttons are at the bottom of the dialog. Example: bool okpressed = false; var ok = new Button(\"Ok\"); var cancel = new Button(\"Cancel\"); var dialog = new Dialog (\"Quit\", 60, 7, ok, cancel); Which will show something like this: +- Quit -----------------------------------------------+ | | | | | [ Ok ] [ Cancel ] | +------------------------------------------------------+ Running Modally To run your Dialog, Window or Toplevel modally, you will invoke the Application.Run method on the toplevel. It is up to your code and event handlers to invoke the Application.RequestStop() method to terminate the modal execution. bool okpressed = false; var ok = new Button(3, 14, \"Ok\") { Clicked = () => { Application.RequestStop (); okpressed = true; } }; var cancel = new Button(10, 14, \"Cancel\") { Clicked = () => Application.RequestStop () }; var dialog = new Dialog (\"Login\", 60, 18, ok, cancel); var entry = new TextField () { X = 1, Y = 1, Width = Dim.Fill (), Height = 1 }; dialog.Add (entry); Application.Run (dialog); if (okpressed) Console.WriteLine (\"The user entered: \" + entry.Text); There is no return value from running modally, so your code will need to have a mechanism of indicating the reason that the execution of the modal dialog was completed, in the case above, the okpressed value is set to true if the user pressed or selected the Ok button. Input Handling Every view has a focused view, and if that view has nested views, one of those is the focused view. This is called the focus chain, and at any given time, only one View has the focus. The library binds the key Tab to focus the next logical view, and the Shift-Tab combination to focus the previous logical view. Keyboard processing is divided in three stages: HotKey processing, regular processing and cold key processing. Hot key processing happens first, and it gives all the views in the current toplevel a chance to monitor whether the key needs to be treated specially. This for example handles the scenarios where the user pressed Alt-o, and a view with a highlighted \"o\" is being displayed. If no view processed the hotkey, then the key is sent to the currently focused view. If the key was not processed by the normal processing, all views are given a chance to process the keystroke in their cold processing stage. Examples include the processing of the \"return\" key in a dialog when a button in the dialog has been flagged as the \"default\" action. The most common case is the normal processing, which sends the keystrokes to the currently focused view. Mouse events are processed in visual order, and the event will be sent to the view on the screen. The only exception is that no mouse events are delivered to background views when a modal view is running. More details are available on the Keyboard Event Processing document. Colors and Color Schemes All views have been configured with a color scheme that will work both in color terminals as well as the more limited black and white terminals. The various styles are captured in the Colors class which defined color schemes for the toplevel, the normal views, the menu bar, popup dialog boxes and error dialog boxes, that you can use like this: Colors.Toplevel Colors.Base Colors.Menu Colors.Dialog Colors.Error You can use them for example like this to set the colors for a new Window: var w = new Window (\"Hello\"); w.ColorScheme = Colors.Error The ColorScheme represents four values, the color used for Normal text, the color used for normal text when a view is focused an the colors for the hot-keys both in focused and unfocused modes. By using ColorSchemes you ensure that your application will work correctbly both in color and black and white terminals. Some views support setting individual color attributes, you create an attribute for a particular pair of Foreground/Background like this: var myColor = Application.Driver.MakeAttribute (Color.Blue, Color.Red); var label = new Label (...); label.TextColor = myColor MainLoop, Threads and Input Handling Detailed description of the mainloop is described on the Event Processing and the Application Main Loop document."
|
|
},
|
|
"articles/tableview.html": {
|
|
"href": "articles/tableview.html",
|
|
"title": "Table View",
|
|
"keywords": "Table View This control supports viewing and editing tabular data. It provides a view of a System.DataTable . System.DataTable is a core class of .net standard and can be created very easily TableView API Reference Csv Example You can create a DataTable from a CSV file by creating a new instance and adding columns and rows as you read them. For a robust solution however you might want to look into a CSV parser library that deals with escaping, multi line rows etc. var dt = new DataTable(); var lines = File.ReadAllLines(filename); foreach(var h in lines[0].Split(',')){ dt.Columns.Add(h); } foreach(var line in lines.Skip(1)) { dt.Rows.Add(line.Split(',')); } Database Example All Ado.net database providers (Oracle, MySql, SqlServer etc) support reading data as DataTables for example: var dt = new DataTable(); using(var con = new SqlConnection(\"Server=myServerAddress;Database=myDataBase;Trusted_Connection=True;\")) { con.Open(); var cmd = new SqlCommand(\"select * from myTable;\",con); var adapter = new SqlDataAdapter(cmd); adapter.Fill(dt); } Displaying the table Once you have set up your data table set it in the view: tableView = new TableView () { X = 0, Y = 0, Width = 50, Height = 10, }; tableView.Table = yourDataTable; Table Rendering TableView supports any size of table (limited only by the RAM requirements of System.DataTable ). You can have thousands of columns and/or millions of rows if you want. Horizontal and vertical scrolling can be done using the mouse or keyboard. TableView uses ColumnOffset and RowOffset to determine the first visible cell of the System.DataTable . Rendering then continues until the avaialble console space is exhausted. Updating the ColumnOffset and RowOffset changes which part of the table is rendered (scrolls the viewport). This approach ensures that no matter how big the table, only a small number of columns/rows need to be evaluated for rendering."
|
|
},
|
|
"articles/treeview.html": {
|
|
"href": "articles/treeview.html",
|
|
"title": "Tree View",
|
|
"keywords": "Tree View TreeView is a control for navigating hierarchical objects. It comes in two forms TreeView and TreeView<T> . TreeView API Reference Using TreeView The basic non generic TreeView class is populated by ITreeNode objects. The simplest tree you can make would look something like: var tree = new TreeView() { X = 0, Y = 0, Width = 40, Height = 20 }; var root1 = new TreeNode(\"Root1\"); root1.Children.Add(new TreeNode(\"Child1.1\")); root1.Children.Add(new TreeNode(\"Child1.2\")); var root2 = new TreeNode(\"Root2\"); root2.Children.Add(new TreeNode(\"Child2.1\")); root2.Children.Add(new TreeNode(\"Child2.2\")); tree.AddObject(root1); tree.AddObject(root2); Having to create a bunch of TreeNode objects can be a pain especially if you already have your own objects e.g. House , Room etc. There are two ways to use your own classes without having to create nodes manually. Firstly you can implement the ITreeNode interface: // Your data class private class House : TreeNode { // Your properties public string Address {get;set;} public List<Room> Rooms {get;set;} // ITreeNode member: public override IList<ITreeNode> Children => Rooms.Cast<ITreeNode>().ToList(); public override string Text { get => Address; set => Address = value; } } // Your other data class private class Room : TreeNode{ public string Name {get;set;} public override string Text{get=>Name;set{Name=value;}} } After implementing the interface you can add your objects directly to the tree var myHouse = new House() { Address = \"23 Nowhere Street\", Rooms = new List<Room>{ new Room(){Name = \"Ballroom\"}, new Room(){Name = \"Bedroom 1\"}, new Room(){Name = \"Bedroom 2\"} } }; var tree = new TreeView() { X = 0, Y = 0, Width = 40, Height = 20 }; tree.AddObject(myHouse); Alternatively you can simply tell the tree how the objects relate to one another by implementing ITreeBuilder<T> . This is a good option if you don't have control of the data objects you are working with. TreeView<T> The generic Treeview<T> allows you to store any object hierarchy where nodes implement Type T. For example if you are working with DirectoryInfo and FileInfo objects then you could create a TreeView<FileSystemInfo> . If you don't have a shared interface/base class for all nodes you can still declare a TreeView<object> . In order to use TreeView<T> you need to tell the tree how objects relate to one another (who are children of who). To do this you must provide an ITreeBuilder<T> . Implementing ITreeBuilder<T> Consider a simple data model that already exists in your program: private abstract class GameObject { } private class Army : GameObject { public string Designation {get;set;} public List<Unit> Units {get;set;} public override string ToString () { return Designation; } } private class Unit : GameObject { public string Name {get;set;} public override string ToString () { return Name; } } An ITreeBuilder<T> for these classes might look like: private class GameObjectTreeBuilder : ITreeBuilder<GameObject> { public bool SupportsCanExpand => true; public bool CanExpand (GameObject model) { return model is Army; } public IEnumerable<GameObject> GetChildren (GameObject model) { if(model is Army a) return a.Units; return Enumerable.Empty<GameObject>(); } } To use the builder in a tree you would use: var army1 = new Army() { Designation = \"3rd Infantry\", Units = new List<Unit>{ new Unit(){Name = \"Orc\"}, new Unit(){Name = \"Troll\"}, new Unit(){Name = \"Goblin\"}, } }; var tree = new TreeView<GameObject>() { X = 0, Y = 0, Width = 40, Height = 20, TreeBuilder = new GameObjectTreeBuilder() }; tree.AddObject(army1); Alternatively you can use DelegateTreeBuilder<T> instead of implementing your own ITreeBuilder<T> . For example: tree.TreeBuilder = new DelegateTreeBuilder<GameObject>( (o)=>o is Army a ? a.Units : Enumerable.Empty<GameObject>()); Node Text and ToString The default behavior of TreeView is to use the ToString method on the objects for rendering. You can customise this by changing the AspectGetter . For example: treeViewFiles.AspectGetter = (f)=>f.FullName;"
|
|
},
|
|
"articles/views.html": {
|
|
"href": "articles/views.html",
|
|
"title": "Views",
|
|
"keywords": "Views Layout Creating Custom Views Constructor Rendering Using Custom Colors Keyboard processing Mouse event processing"
|
|
},
|
|
"index.html": {
|
|
"href": "index.html",
|
|
"title": "Terminal.Gui - Terminal UI toolkit for .NET",
|
|
"keywords": "Terminal.Gui - Terminal UI toolkit for .NET A simple UI toolkit for .NET, .NET Core, and Mono that works on Windows, the Mac, and Linux/Unix. Terminal.Gui Project on GitHub Terminal.Gui API Documentation API Reference Terminal.Gui API Overview Keyboard Event Processing Event Processing and the Application Main Loop TableView Deep Dive TreeView Deep Dive UI Catalog UI Catalog is a comprehensive sample library for Terminal.Gui. It provides a simple UI for adding to the catalog of scenarios. UI Catalog API Reference UI Catalog Source"
|
|
},
|
|
"README.html": {
|
|
"href": "README.html",
|
|
"title": "To Generate the Docs",
|
|
"keywords": "This folder generates the API docs for Terminal.Gui. The API documentation is generated using the DocFX tool . The output of docfx gets put into the ./docs folder which is then checked in. The ./docs folder is then picked up by Github Pages and published to Miguel's Github Pages ( https://migueldeicaza.github.io/gui.cs/ ). To Generate the Docs Install DotFX https://dotnet.github.io/docfx/tutorial/docfx_getting_started.html Change to the ./docfx folder and run ./build.ps1 Browse to http://localhost:8080 and verify everything looks good. Hit ctrl-c to stop the script. If docfx fails with a Stackoverflow error. Just run it again. And again. Sometimes it takes a few times. If that doesn't work, create a fresh clone or delete the docfx/api , docfx/obj , and docs/ folders and run the steps above again."
|
|
}
|
|
} |