|
@@ -1,56 +1,72 @@
|
|
-# V2 Spec for View refactor
|
|
|
|
|
|
+# 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.
|
|
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.
|
|
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` heirarchy(inclidng `Responder`). It does not cover
|
|
|
|
- * Text formatting which will be covered in another spec.
|
|
|
|
|
|
+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 which will be covered separately.
|
|
* TrueColor support which will be covered separately.
|
|
* ConsoleDriver refactor.
|
|
* ConsoleDriver refactor.
|
|
|
|
|
|
-## What's wrong with the View and the View-class Heirarchy in v1?
|
|
|
|
-
|
|
|
|
|
|
+## 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
|
|
## 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 all mouse/keyboard base-logic out of `View` and `Window` and into `Responder`.
|
|
|
|
- * *Container* - A class that can hold other Responders. Implemented in the `Container` base-class.
|
|
|
|
- * In v2 we will move all logic for adding/removing views out of `View` and `Window` and into `Container`.
|
|
|
|
- * NOT DONE YET and somewhat confused in my current WIP.
|
|
|
|
- * *View* - A base-class for implementing higher level visual/interactive Terminal.Gui elements. Implemented in the `View` base-class, which is a `Responder` and a `Container`.
|
|
|
|
- * In v2 we will move all logic for rendering out of `View` and `Window` and into `View`.
|
|
|
|
- * *SubView* - A View that is contained in antoher 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. Referrs to the View another View was was added to as *SubView*.
|
|
|
|
- * *Child View* - A view that is held by another view in a parent/child relationshiop, but is NOT a SubView. Examples of this are sub-menus of `MenuBar`.
|
|
|
|
|
|
+ * *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.
|
|
* *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* - Describes 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.
|
|
|
|
- * *Margin* - Means the Thickness that separtes 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`.
|
|
|
|
- * QUESTION: Will it be possilble to have a negative Margin? If so, will that allow us to have "magic borderframe connections" as I've demonsrated in my TileViewExperiment?
|
|
|
|
- * *Title* - Means 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`.
|
|
|
|
- * *Border* - Means the Thickness 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)- The Thickness between the Margin and Padding. The Adornments property of `View` is a `View`-subclass that hosts SubViews that are not part of the View's content and are rendered within the Adornment Thickness. Adornments are not part of the View's content and are not clipped by the View's `ClipArea`. Examples of Adornments:
|
|
|
|
|
|
+ * *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.
|
|
|
|
+ * *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.
|
|
* 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`!).
|
|
* 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 `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 `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 `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 signficantly simplify the codebase.
|
|
|
|
- * *Padding* - Means the Thickness 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`.
|
|
|
|
- * *Frame* - Means 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.
|
|
|
|
|
|
+ * 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.
|
|
* *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`, drawning will be clipped and (optional) scrollbars will appear.
|
|
|
|
|
|
+ * *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.
|
|
* 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?)
|
|
* *Bounds* - Synomous with *VisibleArea*. (Debate: Do we rename `Bounds` to `VisbleArea` in v2?)
|
|
- * *ClipArea* - Means the currently vislble 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}`). This `Rect` is passed to `View.Redraw` (and should be named "clipArea" not "bounds"). It defines the clip-region the caller desires the `Redraw` implementation to clip itself to (see notes on clipping below).
|
|
|
|
|
|
+ * *ClipArea* - The currently vislble 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* - 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`.
|
|
* *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`.
|
|
- * *TopLevel* - The v1 term used to describe a view that is both Modal and can have a MenuBar and/or StatusBar. I propose in v2 we deprecate the term `TopLevel` and instead use `Modal` to describe the same thing. I do not think `Modal` should be a class, but a property of `View` that can be set to `true` or `false`.
|
|
|
|
|
|
+ * *TopLevel* - The v1 term used to describe a view that is both Modal and can have a MenuBar and/or StatusBar. I propose in v2 we deprecate the term `TopLevel` and instead use `Modal` to describe the same thing. We can completely get rid of the `TopLevel` class! I do not think `Modal` should be a class, but a property of `View` that can be set to `true` or `false`.
|
|
* *Window* - A View that, by default, has a `Border` and a `Title`.
|
|
* *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?
|
|
* 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* - 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, there is no Z-ordering. Borders that are drawn between the SubViews can optionally support resizing the SubViews (negating the need for `TileView`).
|
|
|
|
+ * *Overlap*, *Overlapped*, *Overlapping* - 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
|
|
|
|
|
|
@@ -58,18 +74,18 @@ This covers my thinking on how we will refactor `View` and the classes in the `V
|
|
* QUESTION: Since `Frame`s are `Views` in v2, the `Frame` is a `Responder` that receives user input. This raises the quesiton 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).
|
|
* QUESTION: Since `Frame`s are `Views` in v2, the `Frame` is a `Responder` that receives user input. This raises the quesiton 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
|
|
|
|
|
|
+## View classes to be nuked
|
|
* PanelView (done)
|
|
* PanelView (done)
|
|
* FrameView (almost done)
|
|
* FrameView (almost done)
|
|
|
|
+* TileVeiw
|
|
|
|
+* TopLevel?
|
|
* Window?
|
|
* Window?
|
|
- * *Tile*, *Tiled*, *Tiling* - Refers to a form of layout where the SubViews of a View are visually arranged such that their Frames abut each other and do not overlap. In a Tiled view arragnement there is no Z-ordering.
|
|
|
|
- * *Overlap*, *Overlapped*, *Overlapping* - Refers to a form of layout where the SubViews of a View are visually arranged such that their Frames can overlap. In Overlap view arragements 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.
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- ### Questions
|
|
|
|
-
|
|
|
|
|
|
+* `LineView` can be reimplemented using `LineCanvas`?
|
|
|
|
+* `Button` and `Label` can be merged.
|
|
|
|
+* `StatusBar` and `MenuBar` could be combined. If not, then at least made more consistent (e.g. in how hotkeys are specified).
|
|
|
|
+* `ComboBox` can be replaced by `MenuBar`
|
|
|
|
|
|
-### Problems with Current Architecture & Implementation
|
|
|
|
|
|
+## What's wrong with the View and the View-class hierarchy in v1?
|
|
|
|
|
|
* `Frame`, `Bounds`, and `ClipRect` are confusing and not consistently applied...
|
|
* `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 literaly 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).
|
|
* `Bounds` is `Rect` but is used to describe a `Size` (e.g. `Bounds.Size` is the size of the `View`'s content area). It literaly 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).
|
|
@@ -104,10 +120,6 @@ This covers my thinking on how we will refactor `View` and the classes in the `V
|
|
* `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 (optioally of course). Without any code, scrolling just works.
|
|
* `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 (optioally 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 heirachy suppports 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.".
|
|
* We have many requests to support non-full-screen apps. We need to ensure the `View` class heirachy suppports 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.".
|
|
|
|
|
|
-## Thoughts on Built-in Views
|
|
|
|
-* `LineView` can be replaced by `LineCanvas`?
|
|
|
|
-* `Button` and `Label` can be merged.
|
|
|
|
-* `StatusBar` and `Menu` could be combined. If not, then at least made more consistent (e.g. in how hotkeys are specified).
|
|
|
|
|
|
|
|
## Design
|
|
## Design
|
|
|
|
|