mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
new docfx!
This commit is contained in:
192
docfx/docs/View.md
Normal file
192
docfx/docs/View.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# V2 Spec for View Refactor - WORK IN PROGRESS
|
||||
|
||||
IMPORTANT: I am critical of the existing codebase below. Do not take any of this personally. It is about the code, not the amazing people who wrote the code.
|
||||
|
||||
ALSO IMPORTANT: I've written this to encourage and drive DEBATE. My style is to "Have strong opinions, weakly held." If you read something here you don't understand or don't agree with, SAY SO. Tell me why. Take a stand.
|
||||
|
||||
This covers my thinking on how we will refactor `View` and the classes in the `View` hierarchy (including `Responder`). It does not cover Text formatting which will be covered in another spec.
|
||||
* TrueColor support will be covered separately.
|
||||
* ConsoleDriver refactor.
|
||||
|
||||
## Goals
|
||||
|
||||
1. Refactor View to have "real" Bounds where the Location part can be non-zero
|
||||
2. Enable a real "margin", "border", and "padding" thickness can be implemented that matches how these concepts work in HTML
|
||||
3. Leverage LineCanvas to draw borders and auto-join borders. Remove the need for `TileVeiw` and `SplitView` classes.
|
||||
4. Reduce 20/30% of the existing View, Toplevel, Window, and FrameView can code.
|
||||
5. Make porting apps to use the new architecture relatively easy, but result in less code in apps.
|
||||
6. Make it easier to add new Views and View-like classes.
|
||||
|
||||
## Terminal.Gui v2 View-related Lexicon & Taxonomy
|
||||
|
||||
* *Responder* - A class that can handle user input. Implemented in the `Responder` base class.
|
||||
* In v2 we will move more mouse/keyboard base-logic out of `View` and `Window` and into `Responder`.
|
||||
* *View* - A base class for implementing higher-level visual/interactive Terminal.Gui elements. Implemented in the `View` base class, which is a `Responder` and hosts several `Frame`s.
|
||||
* In v2 we will move all logic for rendering out of `Toplevel`, `FrameView`, and `Window` into `View`.
|
||||
* *SubView* - A View that is contained in another view and will be rendered as part of the containing view's *ContentArea*. SubViews are added to another view via the `View.Add` method. A View may only be a SubView of a single View.
|
||||
* *SuperView* - The View that is a container for SubViews. Referring to the View another View was added to as *SubView*.
|
||||
* *Child View* - A view that is held by another view in a parent/child relationship, but is NOT a SubView. Examples of this are the submenus of `MenuBar`.
|
||||
* *Parent View* - A view that holds a reference to another view in a parent/child relationship, but is NOT a SuperView of the child.
|
||||
* *Thickness* - A class describing a rectangle where each of the four sides can have a width. Valid width values are >= 0. The inner area of a Thickness is the sum of the widths of the four sides minus the size of the rectangle. The `Thickness` class has a `Draw` method that clears the rectangle.
|
||||
* *Frame Class* - A `Frame` is a special form of `View` that appears outside of a normal `View`'s content area. Examples of `Frame`s are `Margin`, `Border`, and `Padding`. The `Frame` class is derived from `View` and uses a `Thickness` to hold the rectangle.
|
||||
* *Frame* - The `Rect` that defines the location and size of the `View` including all of the margin, border, adornments, padding, and content area. The coordinates are relative to the SuperView of the View (or, in the case of `Application.Top`, `ConsoleDriver.Row == 0; ConsoleDriver.Col == 0`). The Frame's location and size are controlled by either `Absolute` or `Computed` positioning via the `.X`, `.Y`, `.Height`, and `.Width` properties of the View.
|
||||
* In v2, `View.Frame.Size` is the size of the `View`'s `ContentArea` plus the `Thickness` of the `View`'s `Margin`, `Border`, and `Padding`.
|
||||
* *Margin* - The `Frame` that separates a View from other SubViews of the same SuperView. The Margin is not part of the View's content and is not clipped by the View's `ClipArea`. By default `Margin` is `{0,0,0,0}`. `Margin` can be used instead of (or with) `Dim.Pos` to position a View relative to another View.
|
||||
Eg.
|
||||
```cs
|
||||
view.X = Pos.Right (otherView) + 1;
|
||||
view.Y = Pos.Bottom (otherView) + 1;
|
||||
```
|
||||
is equivalent to
|
||||
```cs
|
||||
otherView.Margin.Thickness = new Thickness (0, 0, 1, 1);
|
||||
view.X = Pos.Right (otherView);
|
||||
view.Y = Pos.Bottom (otherView);
|
||||
```
|
||||
* QUESTION: Will it be possible to have a negative Margin? If so, will that allow us to have "magic borderframe connections" as I've demonstrated in my TileViewExperiment? Or, should the magic happen when a View's dimensions overlap with another, independent of the Margin?
|
||||
* *Title* - Text that is displayed for the View that describes the View to users. Typically the Title is displayed at the top-left, overlaying the Border. The title is not part of the View's content and is not clipped by the View's `ClipArea`.
|
||||
* *Text* - Text that is rendered by the view within the view's content area, using `TextFormatter`. `Text` is part of the View's content and is clipped by the View's `ClipArea`.
|
||||
* *Border* (currently `BorderFrame` until the old `Border` can be removed) - The `Frame` where a visual border (drawn using line-drawing glyphs) and the Title are drawn. The Border expands inward; in other words if `Border.Thickness.Top == 2` the border & title will take up the first row and the second row will be filled with spaces. The Border is not part of the View's content and is not clipped by the View's `ClipArea`.
|
||||
* *Adornments* (NOT IMPLEMENTED YET; May replace `BorderFrame`)- The `Frame` between the `Border` and `Padding`. Adornments are not part of the View's content and are not clipped by the View's `ClipArea`. Examples of Adornments:
|
||||
* A `TitleBar` renders the View's `Title` and a horizontal line defining the top of the View. Adds thickness to the top of Adornments.
|
||||
* One or more `LineView`s that render the View's border (NOTE: The magic of `LineCanvas` lets us automatically have the right joins for these and `TitleBar`!).
|
||||
* A `Vertical Scrollbar` adds thickness to `Adornments.Right` (or `.Left` when right-to-left language support is added).
|
||||
* A `Horizontal Scrollbar` adds thickness to `Adornments.Bottom` when enabled.
|
||||
* A `MenuBar` adds thickness to `Adornments.Top` (NOTE: This is a change from v1 where `subview.Y = 1` is required).
|
||||
* A `StatusBar` adds thickness ot `Adornments.Bottom` and is rendered at the bottom of `Padding`.
|
||||
* NOTE: The use of `View.Add` in v1 to add adornments to Views is the cause of much code complexity. Changing the API such that `View.Add` is ONLY for subviews and adding a `View.Adornments.Add` API for menu, StatusBar, scroll bar... will enable us to significantly simplify the codebase.
|
||||
* *Padding* - The `Frame` inside of an element that offsets the `Content` from the Border. (NOTE: in v1 `Padding` is OUTSIDE of the `Border`). Padding is `{0, 0, 0, 0}` by default. Padding is not part of the View's content and is not clipped by the View's `ClipArea`.
|
||||
* *VisibleArea* - (NOT IMPLEMENTED YET) Means the area inside of the Margin + Border (Title) + Padding. `VisibleArea.Location` is always `{0, 0}`. `VisibleArea.Size` is the `View.Frame.Size` shrunk by Margin + Border + Padding.
|
||||
* *ContentArea* - (NOT IMPLEMENTED YET; currently `Bounds`) The `Rect` that describes the location and size of the View's content, relative to `VisibleArea`. If `ContentArea.Location` is negative, anything drawn there will be clipped and any subview positioned in the negative area will cause (optional) scrollbars to appear (making the Thickness of Padding thicker on the appropriate sides). If `ContentArea.Size` is changed such that the dimensions fall outside of `Frame.Size shrunk by Margin + Border + `Padding`, drawing will be clipped and (optional) scrollbars will appear.
|
||||
* QUESTION: Can we just have one `ContentArea` property that is the `Rect` that describes the location and size of the View's content, relative to `Frame`? If so, we can remove `VisibleArea` and `Bounds` and just have `ContentArea` and `Frame`? The key to answering this is all wrapped up in scrolling and clipping.
|
||||
* *Bounds* - Synomous with *VisibleArea*. (Debate: Do we rename `Bounds` to `VisbleArea` in v2?)
|
||||
* *ClipArea* - The currently visible portion of the *Content*. This is defined as a`Rect` in coordinates relative to *ContentArea* (NOT *VisibleArea*) (e.g. `ClipArea {X = 0, Y = 0} == ContentArea {X = 0, Y = 0}`). In v2 we will NOT pass this `Rect` is passed `View.Redraw` and instead just have `Redraw` use `Bounds`.
|
||||
* QUESTION: Do we need `ClipArea` at all? Can we just have `Redraw` use `Bounds`?
|
||||
|
||||
* *Modal* - *Modal* - The term used when describing a `View` that was created using the `Application.Run(view)` or `Application.Run<T>` APIs. When a View is running as a modal, user input is restricted to just that View until `Application.Run` exits. A `Modal` View has its own `RunState`.
|
||||
* In v1, classes derived from `Dialog` were originally thought to only work modally. However, `Wizard` proved that a `Dialog`-based class can also work non-modally.
|
||||
* In v2, we will simplify the `Dialog` class, and let any class be run via `Applicaiton.Run`. The `Modal` property will be set by `Application.Run` so the class can detect it is running modally if it needs to.
|
||||
|
||||
* *TopLevel* - The v1 term used to describe a view that can have a MenuBar and/or StatusBar. In v2, we will delete the `TopLevel` class and ensure ANY View can have a menu bar and/or status bar (via `Adornments`).
|
||||
* NOTE: There will still be an `Application.Top` which is the `View` that is the root of the `Application`'s view hierarchy.
|
||||
|
||||
* *Window* - A View that, by default, has a `Border` and a `Title`.
|
||||
* QUESTION: Why can't this just be a property on `View` (e.g. `View.Border = true`)? Why do we need a `Window` class at all in v2?
|
||||
|
||||
* *Tile*, *Tiled*, *Tiling* (NOT IMPLEMENTED YET) - Refer to a form of `ComputedLayout` where SubViews of a `View` are visually arranged such that they abut each other and do not overlap. In a Tiled view arrangement, Z-ordering only comes into play when a developer intentionally causes views to be aligned such that they overlap. Borders that are drawn between the SubViews can optionally support resizing the SubViews (negating the need for `TileView`).
|
||||
|
||||
* *Overlap*, *Overlapped*, *Overlapping* (NOT IMPLEMENTED YET) - Refers to a form of `ComputedLayout` where SubViews of a View are visually arranged such that their Frames overlap. In Overlap view arrangements there is a Z-axis (Z-order) in addition to the X and Y dimension. The Z-order indicates which Views are shown above other views.
|
||||
|
||||
## Focus
|
||||
|
||||
* Focus is a concept that is used to describe which Responder is currently receiving user input.
|
||||
* QUESTION: Since `Frame`s are `Views` in v2, the `Frame` is a `Responder` that receives user input. This raises the question of how a user can use the keyboard to navigate between `Frame`s and `View`s within a `Frame` (and the `Frame`'s `Parent`'s subviews).
|
||||
|
||||
|
||||
## View classes to be nuked
|
||||
* PanelView (done)
|
||||
* FrameView (almost done)
|
||||
* TileVeiw
|
||||
* TopLevel?
|
||||
* Window?
|
||||
* `LineView` can be reimplemented using `LineCanvas`?
|
||||
* `Button` and `Label` can be merged.
|
||||
* `StatusBar` and `MenuBar` could be combined. If not, then at least made consistent (e.g. in how hotkeys are specified).
|
||||
* `ComboBox` can be replaced by `MenuBar`
|
||||
|
||||
## What's wrong with the View and the View-class hierarchy in v1?
|
||||
|
||||
* `Frame`, `Bounds`, and `ClipRect` are confusing and not consistently applied...
|
||||
* `Bounds` is `Rect` but is used to describe a `Size` (e.g. `Bounds.Size` is the size of the `View`'s content area). It literally is implemented as a property that returns `new Rect(0, 0, Width, Height)`. Throughtout the codebase `bounds` is used for things that have non-zero `Size` (and actually descibe either the cliprect or the Frame).
|
||||
* The restrictive nature of how `Bounds` is defined led to the hacky `FrameView` and `Window` classes with an embedded `ContentView` in order to draw a border around the content.
|
||||
* The only reason FrameView exists is because the original architecture didn't support offsetting `View.Bounds` such that a border could be drawn and the interior content would clip correctly. Thus Miguel (or someone) built
|
||||
FrameView with nested `ContentView` that was at `new Rect(+1, +1, -2, -2)`.
|
||||
* `Border` was added later, but couldn't be retrofitted into `View` such that if `View.Border ~= null` just worked like `FrameView`.
|
||||
* Thus devs are forced to use the clunky `FrameView` instead of just setting `View.Border`.
|
||||
* `Border` has a bunch of confusing concepts that don't match other systems (esp the Web/HTML)
|
||||
* `Margin` on the web means the space between elements - `Border` doesn't have a margin property, but does has the confusing `DrawMarginFrame` property.
|
||||
* `Border` on the web means the space where a border is drawn. The current implementaiton confuses the term `Frame` and `Border`. `BorderThickness` is provided.
|
||||
* `Padding` on the web means the padding inside of an element between the `Border` and `Content`. In the current implementation `Padding` is actually OUTSIDE of the `Border`. This means it's not possible for a view to offset internally by simply changing `Bounds`.
|
||||
* `Content` on the web means the area inside of the Margin + Border + Padding. `View` does not currently have a concept of this (but `FrameView` and `Window` do via the embedded `ContentView`s.
|
||||
* `Border` has a `Title` property. So does `Window` and `FrameView`. This is duplicate code.
|
||||
* It is not possible for a class derived from View to override the drawing of the "Border" (frame, title, padding, etc...). Multiple devs have asked to be able to have the border frame to be drawn with a different color than `View.ColorScheme`. The API should explicitly enable devs to override the drawing of `Border` independently of the `View.Draw` method. See how `WM_NCDRAW` works in Windows (Draw non-client). It should be easy to do this from within a `View` sub-class (e.g. override `OnDrawBorder`) and externally (e.g. `DrawBorder += () => ...`.
|
||||
|
||||
* `AutoSize` mostly works, but only because of heroic special-casing logic all over the place by @bdisp. This should be massively simplified.`FrameView` is superfluous and should be removed from the hierarchy (instead devs should just be able to manipulate `View.Border` (or similar) to achieve what `FrameView` provides). The internal `FrameView.ContentView` is a bug-farm and un-needed if `View.Border` worked correctly.
|
||||
* `TopLevel` is currently built around several concepts that are muddled:
|
||||
* Views that host a Menu and StatusBar. It is not clear why this is and if it's needed as a concept.
|
||||
* Views that can be run via `Application.Run<TopLevel>` (need a separate `RunState`). It is not clear why ANY VIEW can't be run this way, but it seems to be a limitation of the current implementation.
|
||||
* Views that can be used as a pop-up (modal) (e.g. `Dialog`). As proven by `Wizard`, it is possible to build a View that works well both ways. But it's way too hard to do this today.
|
||||
* Views that can be moved by the user must inherit from `Window` today. It should be possilbe to enable moving of any View (e.g. `View.CanMove = true`).
|
||||
* The `MdiContainer` stuff is complex, perhaps overly so, and is not actually used by anyone outside of the project. It's also mis-named because Terminal.Gui doesn't actually support "documents" nor does it have a full "MDI" system like Windows (did). It seems to represent features useful in overlapping Views, but it is super confusing on how this works, and the naming doesn't help. This all can be refactored to support specific scenarios and thus be simplified.
|
||||
* There is no facility for users' resizing of Views. @tznind's awesome work on `LineCanvas` and `TileView` combined with @tig's experiments show it could be done in a great way for both modal (overlapping) and tiled Views.
|
||||
* `DrawFrame` and `DrawTitle` are implemented in `ConsoleDriver` and can be replaced by a combination of `LineCanvas` and `Border`.
|
||||
* Colors -
|
||||
* As noted above each of Margin, Border, Padding, and Content should support independent colors.
|
||||
* Many View sub-classes bastardize the existing ColorSchemes to get look/feel that works (e.g. `TextView` and `Wizard`). Separately we should revamp ColorSchemes to enable more scenarios.
|
||||
* TrueColor support is needed and should be the default.
|
||||
* `Responder` is supposed to be where all common, non-visual-related, code goes. We should ensure this is the case.
|
||||
* `View` should have default support for scroll bars. e.g. assume in the new world `View.ContentBounds` is the clip area (defined by `VIew.Frame` minus `Margin` + `Border` + `Padding`) then if any view is added with `View.Add` that has Frame coordinates outside of `ContentBounds` the appropriate scroll bars show up automatgically (optionally of course). Without any code, scrolling just works.
|
||||
* We have many requests to support non-full-screen apps. We need to ensure the `View` class hierarchy supports this in a simple, understandable way. In a world with non-full-screen (where screen is defined as the visible terminal view) apps, the idea that `Frame` is "screen relative" is broken. Although we COULD just define "screen" as "the area that bounds the Terminal.GUI app.".
|
||||
|
||||
|
||||
## Design
|
||||
|
||||
* `Responder`("Responder base class implemented by objects that want to participate on keyboard and mouse input.") remains mostly unchanged, with minor changes:
|
||||
* Methods that take `View` parameters (e.g. `OnEnter`) change to take `Responder` (bad OO design).
|
||||
* Nuke `IsOverriden` (bad OO design)
|
||||
* Move `View.Data` to `Responder` (primitive)
|
||||
* Move `Command` and `KeyBinding` stuff from `View`.
|
||||
* Move the generic mouse and keyboard stuff from `View` (e.g. `WantMousePositionReports`)
|
||||
|
||||
|
||||
## Example of creating Adornments
|
||||
```cs
|
||||
// ends up looking just like the v1 default Window with a menu & status bar
|
||||
// and a vertical scrollbar. In v2 the Window class would do all of this automatically.
|
||||
var top = new TitleBar() {
|
||||
X = 0, Y = 0,
|
||||
Width = Dim.Fill(),
|
||||
Height = 1
|
||||
LineStyle = LineStyle.Single
|
||||
};
|
||||
var left = new LineView() {
|
||||
X = 0, Y = 0,
|
||||
Width = 1,
|
||||
Height = Dim.Fill(),
|
||||
LineStyle = LineStyle.Single
|
||||
};
|
||||
var right = new LineView() {
|
||||
X = Pos.AnchorEnd(), Y = 0,
|
||||
Width = 1,
|
||||
Height = Dim.Fill(),
|
||||
LineStyle = LineStyle.Single
|
||||
};
|
||||
var bottom = new LineView() {
|
||||
X = 0, Y = Pos.AnchorEnd(),
|
||||
Width = Dim.Fill(),
|
||||
Height = 1,
|
||||
LineStyle = LineStyle.Single
|
||||
};
|
||||
|
||||
var menu = new MenuBar() {
|
||||
X = Pos.Right(left), Y = Pos.Bottom(top)
|
||||
};
|
||||
var status = new StatusBar () {
|
||||
X = Pos.Right(left), Y = Pos.Top(bottom)
|
||||
};
|
||||
var vscroll = new ScrollBarView () {
|
||||
X = Pos.Left(right),
|
||||
Y = Dim.Fill(2) // for menu & status bar
|
||||
};
|
||||
|
||||
Adornments.Add(titleBar);
|
||||
Adornments.Add(left);
|
||||
Adornments.Add(right);
|
||||
Adornments.Add(bottom);
|
||||
Adornments.Add(vscroll);
|
||||
|
||||
var treeView = new TreeView () {
|
||||
X = 0, Y = 0, Width = Dim.Fill(), Height = Dim.Fill()
|
||||
};
|
||||
Add (treeView);
|
||||
```
|
||||
130
docfx/docs/config.md
Normal file
130
docfx/docs/config.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# Configuration Management
|
||||
|
||||
Terminal.Gui provides configuration and theme management for Terminal.Gui applications via the [`ConfigurationManager`](~/api/Terminal.Gui.
|
||||
|
||||
1) **Settings**. Settings are applied to the [`Application`](~/api/Terminal.Gui.Application.yml) class. Settings are accessed via the `Settings` property of the [`ConfigurationManager`](~/api/Terminal.Gui.ConfigurationManager.yml) class.
|
||||
2) **Themes**. Themes are a named collection of settings impacting how applications look. The default theme is named "Default". The built-in configuration stored within the Terminal.Gui library defines two additional themes: "Dark", and "Light". Additional themes can be defined in the configuration files.
|
||||
3) **AppSettings**. AppSettings allow applicaitons to use the [`ConfigurationManager`](~/api/Terminal.Gui.ConfigurationManager.yml) to store and retrieve application-specific settings.
|
||||
|
||||
The The [`ConfigurationManager`](~/api/Terminal.Gui.ConfigurationManager.yml) will look for configuration files in the `.tui` folder in the user's home directory (e.g. `C:/Users/username/.tui` or `/usr/username/.tui`), the folder where the Terminal.Gui application was launched from (e.g. `./.tui`), or as a resource within the Terminal.Gui application's main assembly.
|
||||
|
||||
Settings that will apply to all applications (global settings) reside in files named config.json. Settings that will apply to a specific Terminal.Gui application reside in files named appname.config.json, where appname is the assembly name of the application (e.g. `UICatalog.config.json`).
|
||||
|
||||
Settings are applied using the following precedence (higher precedence settings overwrite lower precedence settings):
|
||||
|
||||
1. App specific settings found in the users's home directory (`~/.tui/appname.config.json`). -- Highest precedence.
|
||||
|
||||
2. App specific settings found in the directory the app was launched from (`./.tui/appname.config.json`).
|
||||
|
||||
3. App settings in app resources (`Resources/config.json`).
|
||||
|
||||
4. Global settings found in the the user's home directory (`~/.tui/config.json`).
|
||||
|
||||
5. Global settings found in the directory the app was launched from (`./.tui/config.json`).
|
||||
|
||||
6. Default settings defined in the Terminal.Gui assembly -- Lowest precedence.
|
||||
|
||||
The `UI Catalog` application provides an example of how to use the [`ConfigurationManager`](~/api/Terminal.Gui.ConfigurationManager.yml) class to load and save configuration files. The `Configuration Editor` scenario provides an editor that allows users to edit the configuration files. UI Catalog also uses a file system watcher to detect changes to the configuration files to tell [`ConfigurationManager`](~/api/Terminal.Gui.ConfigurationManager.yml) to reaload them; allowing users to change settings without having to restart the application.
|
||||
|
||||
# What Can Be Configured
|
||||
|
||||
## Settings
|
||||
|
||||
(Note, this list may not be complete; search the source code for `SerializableConfigurationProperty` to find all settings that can be configured.)
|
||||
|
||||
* [Application.QuitKey](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_QuitKey)
|
||||
* [Application.AlternateForwardKey](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_AlternateForwardKey)
|
||||
* [Application.AlternateBackwardKey](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_AlternateBackwardKey)
|
||||
* [Application.UseSystemConsole](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_UseSystemConsole)
|
||||
* [Application.IsMouseDisabled](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_IsMouseDisabled)
|
||||
* [Application.EnableConsoleScrolling](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_EnableConsoleScrolling)
|
||||
|
||||
## Glyphs
|
||||
|
||||
Defines the standard set of glyphs used for standard views (e.g. the default indicator for [Button](~/api/Terminal.Gui.Button.yml)) and line drawing (e.g. [LineCanvas](~/api/Terminal.Gui.LineCanvas.yml)).
|
||||
|
||||
The value can be either a decimal number or a string. The string may be:
|
||||
|
||||
- A unicode char (e.g. "☑")
|
||||
- A hex value in U+ format (e.g. "U+2611")
|
||||
- A hex value in UTF-16 format (e.g. "\\u2611")
|
||||
|
||||
```json
|
||||
"Glyphs": {
|
||||
"RightArrow": "►",
|
||||
"LeftArrow": "U+25C4",
|
||||
"DownArrow": "\\u25BC",
|
||||
"UpArrow": 965010
|
||||
}
|
||||
```
|
||||
|
||||
## Themes
|
||||
|
||||
A Theme is a named collection of settings that impact the visual style of Terminal.Gui applications. The default theme is named "Default". The built-in configuration stored within the Terminal.Gui library defines two more themes: "Dark", and "Light". Additional themes can be defined in the configuration files.
|
||||
|
||||
The Json property `Theme` defines the name of the theme that will be used. If the theme is not found, the default theme will be used.
|
||||
|
||||
Themes support defining ColorSchemes as well as various default settings for Views. Both the default color schemes and user defined color schemes can be configured. See [ColorSchemes](~/api/Terminal.Gui.Colors.yml) for more information.
|
||||
|
||||
# Example Configuration File
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json",
|
||||
"Application.QuitKey": {
|
||||
"Key": "Esc"
|
||||
},
|
||||
"AppSettings": {
|
||||
"UICatalog.StatusBar": false
|
||||
},
|
||||
"Theme": "UI Catalog Theme",
|
||||
"Themes": [
|
||||
{
|
||||
"UI Catalog Theme": {
|
||||
"ColorSchemes": [
|
||||
{
|
||||
"UI Catalog Scheme": {
|
||||
"Normal": {
|
||||
"Foreground": "White",
|
||||
"Background": "Green"
|
||||
},
|
||||
"Focus": {
|
||||
"Foreground": "Green",
|
||||
"Background": "White"
|
||||
},
|
||||
"HotNormal": {
|
||||
"Foreground": "Blue",
|
||||
"Background": "Green"
|
||||
},
|
||||
"HotFocus": {
|
||||
"Foreground": "BrightRed",
|
||||
"Background": "White"
|
||||
},
|
||||
"Disabled": {
|
||||
"Foreground": "BrightGreen",
|
||||
"Background": "Gray"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"TopLevel": {
|
||||
"Normal": {
|
||||
"Foreground": "DarkGray",
|
||||
"Background": "White"
|
||||
...
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Dialog.DefaultEffect3D": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
# Configuration File Schema
|
||||
|
||||
Settings are defined in JSON format, according to the schema found here:
|
||||
|
||||
https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json
|
||||
8
docfx/docs/drivers.md
Normal file
8
docfx/docs/drivers.md
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
# Cross-Platform Driver Model
|
||||
|
||||
**Terminal.Gui** has support for [ncurses](https://github.com/gui-cs/Terminal.Gui/blob/master/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs), [`System.Console`](https://github.com/gui-cs/Terminal.Gui/blob/master/Terminal.Gui/ConsoleDrivers/NetDriver.cs), and a full [Win32 Console](https://github.com/gui-cs/Terminal.Gui/blob/master/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs) front-end.
|
||||
|
||||
`ncurses` is used on Mac/Linux/Unix with color support based on what your library is compiled with; the Windows driver supports full color and mouse, and an easy-to-debug `System.Console` can be used on Windows and Unix, but lacks mouse support.
|
||||
|
||||
You can force the use of `System.Console` on Unix as well; see `Core.cs`.
|
||||
1
docfx/docs/getting-started.md
Normal file
1
docfx/docs/getting-started.md
Normal file
@@ -0,0 +1 @@
|
||||
# Getting Started
|
||||
10
docfx/docs/index.md
Normal file
10
docfx/docs/index.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Conceptual Documentation
|
||||
|
||||
* [Terminal.Gui Overview](overview.md)
|
||||
* [List of Views](views.md)
|
||||
* [Keyboard Event Processing](keyboard.md)
|
||||
* [Event Processing and the Application Main Loop](mainloop.md)
|
||||
* [Cross-platform Driver Model](drivers.md)
|
||||
* [Configuration and Theme Manager](config.md)
|
||||
* [TableView Deep Dive](tableview.md)
|
||||
* [TreeView Deep Dive](treeview.md)
|
||||
1
docfx/docs/introduction.md
Normal file
1
docfx/docs/introduction.md
Normal file
@@ -0,0 +1 @@
|
||||
# Introduction
|
||||
86
docfx/docs/keyboard.md
Normal file
86
docfx/docs/keyboard.md
Normal file
@@ -0,0 +1,86 @@
|
||||
Keyboard Event Processing
|
||||
=========================
|
||||
|
||||
**Terminal.Gui** respects common Linux, Mac, and Windows keyboard idioms. For example, clipboard operations use the familiar `Control/Command-C, X, V` model. `CTRL-Q` is used for exiting views (and apps).
|
||||
|
||||
The input handling of **Terminal.Gui** is similar in some ways to Emacs and the Midnight Commander, so you can expect some of the special key combinations to be active.
|
||||
|
||||
The key `ESC` can act as an Alt modifier (or Meta in Emacs parlance), to allow input on terminals that do not have an alt key. So to produce the sequence `Alt-F`, you can press either `Alt-F`, or `ESC` followed by the key `F`.
|
||||
|
||||
To enter the key `ESC`, you can either press `ESC` and wait 100 milliseconds, or you can press `ESC` twice.
|
||||
|
||||
`ESC-0`, and `ESC-1` through `ESC-9` have a special meaning, they map to `F10`, and `F1` to `F9` respectively.
|
||||
|
||||
Apps can change key bindings using the `AddKeyBinding` API.
|
||||
|
||||
Keyboard events are sent by the [Main Loop](mainloop.md) 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
|
||||
interfering 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:
|
||||
|
||||
```csharp
|
||||
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.
|
||||
161
docfx/docs/mainloop.md
Normal file
161
docfx/docs/mainloop.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# Event Processing and the Application Main Loop
|
||||
|
||||
_See also [Cross-platform Driver Model](drivers.md)_
|
||||
|
||||
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`](~/api/Terminal.Gui.Application.yml)
|
||||
provides access to these functions.
|
||||
|
||||
When your code invokes `Application.Run (Toplevel)`, the application
|
||||
will prepare the current
|
||||
[`Toplevel`](~/api/Terminal.Gui.Toplevel.yml) 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](keyboard.md) 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:
|
||||
|
||||
```csharp
|
||||
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:
|
||||
|
||||
```csharup
|
||||
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.
|
||||
3
docfx/docs/newinv2.md
Normal file
3
docfx/docs/newinv2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Terminal.Gui v2
|
||||
|
||||
Checkout this Discussion: https://github.com/gui-cs/Terminal.Gui/discussions/2448
|
||||
398
docfx/docs/overview.md
Normal file
398
docfx/docs/overview.md
Normal file
@@ -0,0 +1,398 @@
|
||||
# Terminal.Gui v2 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:
|
||||
|
||||
```csharp
|
||||
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
|
||||
[Applicaton.Init](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_Init_Terminal_Gui_ConsoleDriver_) 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](~/api/Terminal.Gui.Application.yml) class, additionally creates an instance of the [Toplevel](~/api/Terminal.Gui.Toplevel.yml) class that is ready to be consumed,
|
||||
this instance is available in the `Application.Top` property, and can be used like this:
|
||||
|
||||
```csharp
|
||||
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:
|
||||
|
||||
```csharp
|
||||
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](~/api/Terminal.Gui.View.yml). Views are self-contained objects that take care of displaying themselves, can receive keyboard and mouse input and participate in the focus mechanism.
|
||||
|
||||
See the full list of [Views provided by the Terminal.Gui library here](views.md).
|
||||
|
||||
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](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_Add_Terminal_Gui_View_) method, for example, to add a couple of buttons to a UI, you can do this:
|
||||
|
||||
```csharp
|
||||
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.
|
||||
|
||||
## Layout
|
||||
|
||||
Terminal.Gui supports two different layout systems, absolute and computed \
|
||||
(controlled by the [LayoutStyle](~/api/Terminal.Gui.LayoutStyle.yml)
|
||||
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](~/api/Terminal.Gui.Rect.yml). 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:
|
||||
|
||||
```csharp
|
||||
|
||||
// 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](~/api/Terminal.Gui.Pos.yml) and the `Width` and `Height` properties are of type [Dim](~/api/Terminal.Gui.Dim.yml) 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:
|
||||
|
||||
```csharp
|
||||
// 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:
|
||||
|
||||
|
||||
```csharp
|
||||
// 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](~/api/Terminal.Gui.Toplevel.yml) view (or any of its subclasses), like [Window](~/api/Terminal.Gui.Window.yml) or [Dialog](~/api/Terminal.Gui.Dialog.yml) 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](~/api/Terminal.Gui.Toplevel.yml) 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:
|
||||
|
||||
```csharp
|
||||
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](~/api/Terminal.Gui.Window.yml) 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](~/api/Terminal.Gui.Dialog.yml) are [Window](~/api/Terminal.Gui.Window.yml) 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`](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui.Dialog.yml#Terminal_Gui_Dialog_AddButton_Terminal_Gui_Button_) API which manages the layout
|
||||
of any button passed to it, ensuring that the buttons are at the bottom of the dialog.
|
||||
|
||||
Example:
|
||||
```csharp
|
||||
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.
|
||||
|
||||
```csharp
|
||||
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 details are available on the [Keyboard Event Processing](keyboard.md) 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](~/api/Terminal.Gui.Colors.yml) 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](~/api/Terminal.Gui.ColorScheme.yml) 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](~/docs/mainloop.md) document.
|
||||
|
||||
## Cross-Platform Drivers
|
||||
|
||||
See [Cross-platform Driver Model](drivers.md).
|
||||
85
docfx/docs/tableview.md
Normal file
85
docfx/docs/tableview.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# Table View
|
||||
|
||||
This control supports viewing and editing tabular data. It provides a view of a [System.DataTable](https://docs.microsoft.com/en-us/dotnet/api/system.data.datatable?view=net-5.0).
|
||||
|
||||
System.DataTable is a core class of .net standard and can be created very easily
|
||||
|
||||
[TableView API Reference](~/api/Terminal.Gui.TableView.yml)
|
||||
|
||||
## 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.
|
||||
|
||||
```csharp
|
||||
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:
|
||||
|
||||
```csharp
|
||||
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:
|
||||
|
||||
```csharp
|
||||
tableView = new TableView () {
|
||||
X = 0,
|
||||
Y = 0,
|
||||
Width = 50,
|
||||
Height = 10,
|
||||
};
|
||||
|
||||
tableView.Table = new DataTableSource(yourDataTable);
|
||||
```
|
||||
|
||||
## Object data
|
||||
If your data objects are not stored in a `System.Data.DataTable` then you can instead
|
||||
create a table using `EnumerableTableSource<T>` or implementing your own `ITableSource`
|
||||
class.
|
||||
|
||||
For example to render data for the currently running processes:
|
||||
|
||||
```csharp
|
||||
tableView.Table = new EnumerableTableDataSource<Process> (Process.GetProcesses (),
|
||||
new Dictionary<string, Func<Process, object>>() {
|
||||
{ "ID",(p)=>p.Id},
|
||||
{ "Name",(p)=>p.ProcessName},
|
||||
{ "Threads",(p)=>p.Threads.Count},
|
||||
{ "Virtual Memory",(p)=>p.VirtualMemorySize64},
|
||||
{ "Working Memory",(p)=>p.WorkingSet64},
|
||||
});
|
||||
```
|
||||
|
||||
## Table Rendering
|
||||
TableView supports any size of table. 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.
|
||||
12
docfx/docs/toc.yml
Normal file
12
docfx/docs/toc.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
- name: What's new in Terminal.Gui v2
|
||||
href: newinv2.md
|
||||
- name: Overview
|
||||
href: overview.md
|
||||
- name: Conceptual Docs
|
||||
href: index.md
|
||||
# - name: API Reference
|
||||
# href: api/Terminal.Gui/
|
||||
# - name: UI Catalog API
|
||||
# href: api/UICatalog/
|
||||
- name: Source
|
||||
href: https://github.com/gui-cs/Terminal.Gui
|
||||
189
docfx/docs/treeview.md
Normal file
189
docfx/docs/treeview.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# Tree View
|
||||
|
||||
TreeView is a control for navigating hierarchical objects. It comes in two forms `TreeView` and `TreeView<T>`.
|
||||
|
||||
[TreeView API Reference](~/api/Terminal.Gui.TreeView.yml)
|
||||
|
||||
## Using TreeView
|
||||
|
||||
The basic non generic TreeView class is populated by `ITreeNode` objects. The simplest tree you can make would look something like:
|
||||
|
||||
```csharp
|
||||
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:
|
||||
|
||||
|
||||
```csharp
|
||||
// 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
|
||||
|
||||
```csharp
|
||||
|
||||
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:
|
||||
|
||||
```csharp
|
||||
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:
|
||||
|
||||
```csharp
|
||||
|
||||
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:
|
||||
|
||||
```csharp
|
||||
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:
|
||||
|
||||
```csharp
|
||||
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:
|
||||
|
||||
```csharp
|
||||
treeViewFiles.AspectGetter = (f)=>f.FullName;
|
||||
```
|
||||
31
docfx/docs/views.md
Normal file
31
docfx/docs/views.md
Normal file
@@ -0,0 +1,31 @@
|
||||
*Terminal.Gui* provides a rich set of views and controls for building terminal user interfaces:
|
||||
|
||||
* [Button](~/api/Terminal.Gui.Button.yml) - A View that provides an item that invokes an System.Action when activated by the user.
|
||||
* [CheckBox](~/api/Terminal.Gui.CheckBox.yml) - Shows an on/off toggle that the user can set.
|
||||
* [ColorPicker](~/api/Terminal.Gui.ColorPicker.yml) - Enables to user to pick a color.
|
||||
* [ComboBox](~/api/Terminal.Gui.ComboBox.yml) - Provides a drop-down list of items the user can select from.
|
||||
* [Dialog](~/api/Terminal.Gui.Dialog.yml) - A pop-up Window that contains one or more Buttons.
|
||||
* [OpenDialog](~/api/Terminal.Gui.OpenDialog.yml) - A Dialog providing an interactive pop-up Window for users to select files or directories.
|
||||
* [SaveDialog](~/api/Terminal.Gui.SaveDialog.yml) - A Dialog providing an interactive pop-up Window for users to save files.
|
||||
* [FrameView](~/api/Terminal.Gui.FrameView.yml) - A container View that draws a frame around its contents. Similar to a GroupBox in Windows.
|
||||
* [GraphView](~/api/Terminal.Gui.GraphView.yml) - A View for rendering graphs (bar, scatter etc).
|
||||
* [Hex viewer/editor](~/api/Terminal.Gui.HexView.yml) - A hex viewer and editor that operates over a file stream.
|
||||
* [Label](~/api/Terminal.Gui.Label.yml) - Displays a string at a given position and supports multiple lines.
|
||||
* [ListView](~/api/Terminal.Gui.ListView.yml) - Displays a scrollable list of data where each item can be activated to perform an action.
|
||||
* [MenuBar](~/api/Terminal.Gui.MenuBar.yml) - Provides a menu bar with drop-down and cascading menus.
|
||||
* [MessageBox](~/api/Terminal.Gui.MessageBox.yml) - Displays a modal (pup-up) message to the user, with a title, a message and a series of options that the user can choose from.
|
||||
* [ProgressBar](~/api/Terminal.Gui.ProgressBar.yml) - Displays a progress Bar indicating progress of an activity.
|
||||
* [RadioGroup](~/api/Terminal.Gui.RadioGroup.yml) - Displays a group of labels each with a selected indicator. Only one of those can be selected at a given time
|
||||
* [ScrollView](~/api/Terminal.Gui.ScrollView.yml) - Present a window into a virtual space where subviews are added. Similar to the iOS UIScrollView.
|
||||
* [ScrollBarView](~/api/Terminal.Gui.ScrollBarView.yml) - display a 1-character scrollbar, either horizontal or vertical.
|
||||
* [StatusBar](~/api/Terminal.Gui.StatusBar.yml) - A View that snaps to the bottom of a Toplevel displaying set of status items. Includes support for global app keyboard shortcuts.
|
||||
* [TableView](~/api/Terminal.Gui.TableView.yml) - A View for tabular data based on a System.Data.DataTable.
|
||||
* [TimeField](~/api/Terminal.Gui.TimeField.yml) & [DateField](~/api/Terminal.Gui.TimeField.yml) - Enables structured editing of dates and times.
|
||||
* [TextField](~/api/Terminal.Gui.TextField.yml) - Provides a single-line text entry.
|
||||
* [TextValidateField](~/api/Terminal.Gui.TextValidateField.yml) - Text field that validates input through a ITextValidateProvider.
|
||||
* [TextView](~/api/Terminal.Gui.TextView.yml)- A multi-line text editing View supporting word-wrap, auto-complete, context menus, undo/redo, and clipboard operations,
|
||||
* [TopLevel](~/api/Terminal.Gui.Toplevel.yml) - The base class for modal/pop-up Windows.
|
||||
* [TreeView](~/api/Terminal.Gui.TreeView.yml) - A hierarchical tree view with expandable branches. Branch objects are dynamically determined when expanded using a user defined ITreeBuilder.
|
||||
* [View](~/api/Terminal.Gui.View.yml) - The base class for all views on the screen and represents a visible element that can render itself and contains zero or more nested views.
|
||||
* [Window](~/api/Terminal.Gui.Window.yml) - A Toplevel view that draws a border around its Frame with a title at the top.
|
||||
* [Wizard](~/api/Terminal.Gui.Wizard.yml) - Provides navigation and a user interface to collect related data across multiple steps.
|
||||
Reference in New Issue
Block a user