mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 07:47:54 +01:00
Cleans up/Refactors View.Subviews (#3962)
* Subview clean up * New Add/Remove event pattern * Using Logging * cleanup * Subview -> SubView * Test code cleanup. Killed many warnings. * Fix tznind feedback * Refactored AllViewTest helpers * Moved keyboard tests to parallel * Moved mouse tests to parallel * Moved view tests to parallel * Test code cleanup. Killed many warnings. * dupe test * Some mouse tests can't run in parallel because MouseGrabView * Made SpinnerView more testable * Moved more tests * SubViews to IReadOnlyCollection<View> * SubViews to IReadOnlyCollection<View> 2 * scrollbar tests * shortcut tests * Use InternalSubViews vs. _subviews * Nuked View.IsAdded. Added View.SuperViewChanged. * API doc updats * Unit Test tweak * Unit Test tweak
This commit is contained in:
@@ -6,13 +6,13 @@
|
||||
|
||||
* *@"Terminal.Gui.View"* - The base class for implementing higher-level visual/interactive Terminal.Gui elements. Implemented in the @Terminal.Gui.View base class.
|
||||
|
||||
* *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 @"Terminal.Gui.View.Add(Terminal.Gui.View)" method. A View may only be a SubView of a single View. Each View has a @Terminal.Gui.View.Subviews property that is a list of all Subviews that have been added.
|
||||
* *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 @"Terminal.Gui.View.Add(Terminal.Gui.View)" method. A View may only be a SubView of a single View. Each View has a @Terminal.Gui.View.SubViews property that is a list of all SubViews that have been added.
|
||||
|
||||
* *@"Terminal.Gui.View.SuperView"* - The View that is a container for SubViews. Each View has a @Terminal.Gui.View.SuperView property that references it's SuperView after it has been added.
|
||||
|
||||
* *Child View* - A view that holds a reference to another view in a parent/child relationship. Terminal.Gui uses the terms "Child" and "Parent" sparingly. Generally Subview/SuperView is preferred.
|
||||
* *Child View* - A view that holds a reference to another view in a parent/child relationship. Terminal.Gui uses the terms "Child" and "Parent" sparingly. Generally SubView/SuperView is preferred.
|
||||
|
||||
* *Parent View* - A view that holds a reference to another view in a parent/child relationship, but is NOT a SuperView of the child. Terminal.Gui uses the terms "Child" and "Parent" sparingly. Generally Subview/SuperView is preferred.
|
||||
* *Parent View* - A view that holds a reference to another view in a parent/child relationship, but is NOT a SuperView of the child. Terminal.Gui uses the terms "Child" and "Parent" sparingly. Generally SubView/SuperView is preferred.
|
||||
|
||||
### Input
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ Terminal.Gui provides a feature of Layout known as **Arrangement**, which contro
|
||||
|
||||
* **Sizable** - Describes a View that can be sized by the user using the keyboard or mouse. **Sizable** is enabled on a per-View basis by setting the @Terminal.Gui.ViewArrangement.Resizable flag on @Terminal.Gui.View.Arrangement. Dragging on the left, right, or bottom @Terminal.Gui.View.Border of a View will size that side of such a view. Pressing `Ctrl+F5` will activate **Arrange Mode** letting the user size the view with the up/down/left/right cursor keys.
|
||||
|
||||
* **Tiled** - A form of layout where SubViews of a View are visually arranged such that their Frames do not typically overlap. With **Tiled** views, there is no 'z-order` to the Subviews of a View. In most use-cases, subviews do not overlap with each other (the exception being when it's done intentionally to create some visual effect). As a result, the default layout for most TUI apps is "tiled", and by default @Terminal.Gui.View.Arrangement is set to @Terminal.Gui.ViewArrangement.Fixed.
|
||||
* **Tiled** - A form of layout where SubViews of a View are visually arranged such that their Frames do not typically overlap. With **Tiled** views, there is no 'z-order` to the SubViews of a View. In most use-cases, subviews do not overlap with each other (the exception being when it's done intentionally to create some visual effect). As a result, the default layout for most TUI apps is "tiled", and by default @Terminal.Gui.View.Arrangement is set to @Terminal.Gui.ViewArrangement.Fixed.
|
||||
|
||||
* **Runnable** - Today, Overlapped and Runnable are intrinsically linked. A runnable view is one where `Application.Run(Toplevel)` is called. Each *Runnable* view where (`Modal == false`) has it's own `RunState` and is, effectively, a self-contained "application". `Application.Run()` non-preemptively dispatches events (screen, keyboard, mouse , Timers, and Idle handlers) to the associated `Toplevel`. It is possible for such a `Toplevel` to create a thread and call `Application.Run(someotherToplevel)` on that thread, enabling pre-emptive user-interface multitasking (`BackgroundWorkerCollection` does this).
|
||||
|
||||
|
||||
@@ -29,18 +29,18 @@ Line drawing is accomplished using the @Terminal.Gui.LineCanvas API:
|
||||
|
||||
The @Terminal.Gui.Application MainLoop will iterate over all Views in the view hierarchy, starting with @Terminal.Gui.Application.Toplevels. The @Terminal.Gui.View.Draw method will be called which, in turn:
|
||||
|
||||
0) Determines if @Terminal.Gui.View.NeedsDraw or @Terminal.Gui.View.SubviewNeedsDraw are set. If neither is set, processing stops.
|
||||
0) Determines if @Terminal.Gui.View.NeedsDraw or @Terminal.Gui.View.SubViewNeedsDraw are set. If neither is set, processing stops.
|
||||
1) Sets the clip to the view's Frame.
|
||||
2) Draws the @Terminal.Gui.View.Border and @Terminal.Gui.View.Padding (but NOT the Margin).
|
||||
3) Sets the clip to the view's Viewport.
|
||||
4) Sets the Normal color scheme.
|
||||
5) Calls Draw on any @Terminal.Gui.View.Subviews.
|
||||
5) Calls Draw on any @Terminal.Gui.View.SubViews.
|
||||
6) Draws @Terminal.Gui.View.Text.
|
||||
7) Draws any non-text content (the base View does nothing.)
|
||||
8) Sets the clip back to the view's Frame.
|
||||
9) Draws @Terminal.Gui.View.LineCanvas (which may have been added to by any of the steps above).
|
||||
10) Draws the @Terminal.Gui.View.Border and @Terminal.Gui.View.Padding Subviews (just the subviews). (but NOT the Margin).
|
||||
11) The Clip at this point excludes all Subviews NOT INCLUDING their Margins. This clip is cached so @Terminal.Gui.View.Margin can be rendered later.
|
||||
10) Draws the @Terminal.Gui.View.Border and @Terminal.Gui.View.Padding SubViews (just the subviews). (but NOT the Margin).
|
||||
11) The Clip at this point excludes all SubViews NOT INCLUDING their Margins. This clip is cached so @Terminal.Gui.View.Margin can be rendered later.
|
||||
12) DrawComplete is raised.
|
||||
13) The current View's Frame NOT INCLUDING the Margin is excluded from the current Clip region.
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ The @Terminal.Gui.View.Frame property of a `View` is a rectangle that describes
|
||||
|
||||
## The Content Area
|
||||
|
||||
The content area is the area where the view's content is drawn. Content can be any combination of the @Terminal.Gui.View.Text property, `Subviews`, and other content drawn by the View. The @Terminal.Gui.View.GetContentSize method gets the size of the content area of the view. *Content Area* refers to the rectangle with a location of `0,0` with the size returned by @Terminal.Gui.View.GetContentSize.
|
||||
The content area is the area where the view's content is drawn. Content can be any combination of the @Terminal.Gui.View.Text property, `SubViews`, and other content drawn by the View. The @Terminal.Gui.View.GetContentSize method gets the size of the content area of the view. *Content Area* refers to the rectangle with a location of `0,0` with the size returned by @Terminal.Gui.View.GetContentSize.
|
||||
|
||||
The Content Area size tracks the size of the @Terminal.Gui.View.Viewport by default. If the content size is set via @Terminal.Gui.View.SetContentSize, the content area is the provided size. If the content size is larger than the @Terminal.Gui.View.Viewport, scrolling is enabled.
|
||||
|
||||
@@ -86,7 +86,7 @@ The @Terminal.Gui.View.ViewportSettings property controls how the Viewport is co
|
||||
|
||||
The default `ViewportSettings` also constrains the Viewport to the size of the content, ensuring the right-most column or bottom-most row of the content will always be visible (in v1 the equivalent concept was `ScrollBarView.AlwaysKeepContentInViewport`). To allow the Viewport to be smaller than the content, set `ViewportSettings.AllowXGreaterThanContentWidth` and/or `ViewportSettings.AllowXGreaterThanContentHeight`.
|
||||
|
||||
* *@Terminal.Gui.View.GetContentSize()* - The content area is the area where the view's content is drawn. Content can be any combination of the @Terminal.Gui.View.Text property, `Subviews`, and other content drawn by the View. The @Terminal.Gui.View.GetContentSize method gets the size of the content area of the view. *Content Area* refers to the rectangle with a location of `0,0` with the size returned by @Terminal.Gui.View.GetContentSize. The [Layout Deep Dive](layout.md) has more details on the Content Area.
|
||||
* *@Terminal.Gui.View.GetContentSize()* - The content area is the area where the view's content is drawn. Content can be any combination of the @Terminal.Gui.View.Text property, `SubViews`, and other content drawn by the View. The @Terminal.Gui.View.GetContentSize method gets the size of the content area of the view. *Content Area* refers to the rectangle with a location of `0,0` with the size returned by @Terminal.Gui.View.GetContentSize. The [Layout Deep Dive](layout.md) has more details on the Content Area.
|
||||
|
||||
* *@Terminal.Gui.View.Viewport* A rectangle describing the portion of the *Content Area* that is currently visible to the user. It is a "portal" into the content. The `Viewport.Location` is relative to the top-left corner of the inner rectangle of `View.Padding`. If `Viewport.Size` is the same as `View.GetContentSize()`, `Viewport.Location` will be `0,0`.
|
||||
|
||||
@@ -139,7 +139,7 @@ myView.Y = Pos.Bottom (anotherView) + 5;
|
||||
|
||||
The @Terminal.Gui.Dim is the type of `View.Width` and `View.Height` and supports the following sub-types:
|
||||
|
||||
* Automatic size based on the View's content (either Subviews or Text) - @Terminal.Gui.Dim.Auto(Terminal.Gui.DimAutoStyle,Terminal.Gui.Dim,Terminal.Gui.Dim) - See [Dim.Auto Deep Dive](dimauto.md).
|
||||
* Automatic size based on the View's content (either SubViews or Text) - @Terminal.Gui.Dim.Auto(Terminal.Gui.DimAutoStyle,Terminal.Gui.Dim,Terminal.Gui.Dim) - See [Dim.Auto Deep Dive](dimauto.md).
|
||||
* Absolute size, by passing an integer - @Terminal.Gui.Dim.Absolute(System.Int32).
|
||||
* Percentage of the SuperView's Content Area - @Terminal.Gui.Dim.Percent(System.Int32).
|
||||
* Fill to the end of the SuperView's Content Area - @Terminal.Gui.Dim.Fill.
|
||||
|
||||
@@ -262,7 +262,7 @@ See also [Keyboard](keyboard.md) where HotKey is covered more deeply...
|
||||
- `public bool FocusDeepest (NavigationDirection direction, TabBehavior? behavior)`
|
||||
* In v1, the `View.OnEnter/Enter` and `View.OnLeave/Leave` virtual methods/events could be used to notify that a view had gained or lost focus, but had confusing semantics around what it mean to override (requiring calling `base`) and bug-ridden behavior on what the return values signified. The "Enter" and "Leave" terminology was confusing. In v2, `View.OnHasFocusChanging/HasFocusChanging` and `View.OnHasFocusChanged/HasFocusChanged` replace `View.OnEnter/Enter` and `View.OnLeave/Leave`. These virtual methods/events follow standard Terminal.Gui event patterns. The `View.OnHasFocusChanging/HasFocusChanging` event supports being cancelled.
|
||||
* In v1, the concept of `Mdi` views included a large amount of complex code (in `Toplevel` and `Application`) for dealing with navigation across overlapped Views. This has all been radically simplified in v2. Any View can work in an "overlapped" or "tiled" way. See [navigation.md](navigation.md) for more details.
|
||||
* The `View.TabIndex` and `View.TabIndexes` have been removed. Change the order of the views in `View.Subviews` to change the navigation order (using, for example `View.MoveSubviewTowardsStart()`).
|
||||
* The `View.TabIndex` and `View.TabIndexes` have been removed. Change the order of the views in `View.SubViews` to change the navigation order (using, for example `View.MoveSubViewTowardsStart()`).
|
||||
|
||||
### How to Fix (Focus API)
|
||||
|
||||
@@ -276,7 +276,7 @@ In v2, `HotKey`s can be used to navigate across the entire application view-hier
|
||||
|
||||
In v2, unlike v1, multiple Views in an application (even within the same SuperView) can have the same `HotKey`. Each press of the `HotKey` will invoke the next `HotKey` across the View hierarchy (NOT IMPLEMENTED YET)*
|
||||
|
||||
In v1, the keys used for navigation were both hard-coded and configurable, but in an inconsistent way. `Tab` and `Shift+Tab` worked consistently for navigating between Subviews, but were not configurable. `Ctrl+Tab` and `Ctrl+Shift+Tab` navigated across `Overlapped` views and had configurable "alternate" versions (`Ctrl+PageDown` and `Ctrl+PageUp`).
|
||||
In v1, the keys used for navigation were both hard-coded and configurable, but in an inconsistent way. `Tab` and `Shift+Tab` worked consistently for navigating between SubViews, but were not configurable. `Ctrl+Tab` and `Ctrl+Shift+Tab` navigated across `Overlapped` views and had configurable "alternate" versions (`Ctrl+PageDown` and `Ctrl+PageUp`).
|
||||
|
||||
In v2, this is made consistent and configurable:
|
||||
|
||||
@@ -447,16 +447,16 @@ In v1, you could add timeouts via `Application.MainLoop.AddTimeout` among other
|
||||
+ Application.AddTimeout (TimeSpan time, Func<bool> callback)
|
||||
```
|
||||
|
||||
## `SendSubviewXXX` renamed and corrected
|
||||
## `SendSubViewXXX` renamed and corrected
|
||||
|
||||
In v1, the `View` methods to move Subviews within the Subviews list were poorly named and actually operated in reverse of what their names suggested.
|
||||
In v1, the `View` methods to move SubViews within the SubViews list were poorly named and actually operated in reverse of what their names suggested.
|
||||
|
||||
In v2, these methods have been named correctly.
|
||||
|
||||
- `SendSubViewToBack` -> `MoveSubviewToStart` - Moves the specified subview to the start of the list.
|
||||
- `SendSubViewBackward` -> `MoveSubviewTowardsStart` - Moves the specified subview one position towards the start of the list.
|
||||
- `SendSubViewToFront` -> `MoveSubviewToEnd` - Moves the specified subview to the end of the list.
|
||||
- `SendSubViewForward` -> `MoveSubviewTowardsEnd` - Moves the specified subview one position towards the end of the list.
|
||||
- `SendSubViewToBack` -> `MoveSubViewToStart` - Moves the specified subview to the start of the list.
|
||||
- `SendSubViewBackward` -> `MoveSubViewTowardsStart` - Moves the specified subview one position towards the start of the list.
|
||||
- `SendSubViewToFront` -> `MoveSubViewToEnd` - Moves the specified subview to the end of the list.
|
||||
- `SendSubViewForward` -> `MoveSubViewTowardsEnd` - Moves the specified subview one position towards the end of the list.
|
||||
|
||||
## `Mdi` Replaced by `ViewArrangement.Overlapped`
|
||||
|
||||
@@ -474,7 +474,7 @@ v1 conflated the concepts of
|
||||
|
||||
* `View` and all subclasses support `IDisposable` and must be disposed (by calling `view.Dispose ()`) by whatever code owns the instance when the instance is longer needed.
|
||||
|
||||
* To simplify programming, any `View` added as a Subview another `View` will have it's lifecycle owned by the Superview; when a `View` is disposed, it will call `Dispose` on all the items in the `Subviews` property. Note this behavior is the same as it was in v1, just clarified.
|
||||
* To simplify programming, any `View` added as a SubView another `View` will have it's lifecycle owned by the Superview; when a `View` is disposed, it will call `Dispose` on all the items in the `SubViews` property. Note this behavior is the same as it was in v1, just clarified.
|
||||
|
||||
* In v1, `Application.End` called `Dispose ()` on @Terminal.Gui.Application.Top (via `Runstate.Toplevel`). This was incorrect as it meant that after `Application.Run` returned, `Application.Top` had been disposed, and any code that wanted to interrogate the results of `Run` by accessing `Application.Top` only worked by accident. This is because GC had not actually happened; if it had the application would have crashed. In v2 `Application.End` does NOT call `Dispose`, and it is the caller to `Application.Run` who is responsible for disposing the `Toplevel` that was either passed to `Application.Run (View)` or created by `Application.Run<T> ()`.
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ Mouse-based navigation is straightforward in comparison to keyboard: If a view i
|
||||
|
||||
The answer to both questions is:
|
||||
|
||||
If the View was previously focused, the system keeps a record of the Subview that was previously most-focused and restores focus to that Subview (`RestoreFocus()`).
|
||||
If the View was previously focused, the system keeps a record of the SubView that was previously most-focused and restores focus to that SubView (`RestoreFocus()`).
|
||||
|
||||
If the View was not previously focused, `AdvanceFocus()` is called.
|
||||
|
||||
@@ -116,13 +116,13 @@ For keyboard navigation, the `TabStop` property is a filter for which views are
|
||||
|
||||
* `null` - This View is still being initialized; acts as a signal to `set_CanFocus` to set `TabStop` to `TabBehavior.TabStop` as convince for the most common use-case. Equivalent to `TabBehavior.NoStop` when determining if a view is focusable by the keyboard or not.
|
||||
* `TabBehavior.NoStop` - Prevents the user from using keyboard navigation to cause view (and by definition it's subviews) to gain focus. Note: The view can still be focused using code or the mouse.
|
||||
* `TabBehavior.TabStop` - Indicates a View is a focusable view with no focusable subviews. `Application.Next/PrevTabStopKey` will advance ONLY through the peer-Views (`SuperView.Subviews`).
|
||||
* `TabBehavior.TabStop` - Indicates a View is a focusable view with no focusable subviews. `Application.Next/PrevTabStopKey` will advance ONLY through the peer-Views (`SuperView.SubViews`).
|
||||
|
||||
* `TabBehavior.GroupStop` - Indicates a View is a focusable container for other focusable views and enables keyboard navigation across these containers. This applies to both tiled and overlapped views. For example, `FrameView` is a simple view designed to be a visible container of other views tiled scenarios. It has `TabStop` set to `TabBehavior.GroupStop` (and `Arrangement` set to `ViewArrangement.Fixed`). Likewise, `Window` is a simple view designed to be a visible container of other views in overlapped scenarios. It has `TabStop` set to `TabBehavior.GroupStop` (and `Arrangement` set to `ViewArrangement.Movable | ViewArrangement.Resizable | ViewArrangement.Overlapped`). `Application.Next/PrevGroupStopKey` will advance across all `GroupStop` views in the application (unless blocked by a `NoStop` SuperView).
|
||||
|
||||
## How To Tell if a View has focus? And which view is the most-focused?
|
||||
|
||||
`View.HasFocus` indicates whether the `View` is focused or not. It is the definitive signal. If the view has no focusable Subviews then this property also indicates the view is the most-focused view in the application.
|
||||
`View.HasFocus` indicates whether the `View` is focused or not. It is the definitive signal. If the view has no focusable SubViews then this property also indicates the view is the most-focused view in the application.
|
||||
|
||||
Setting this property to `true` has the same effect as calling `View.SetFocus ()`, which also means the focus may not change as a result.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user