Terminal.Gui layout is declarative and responsive. To define layout, describe how a View should relate to its SuperView, its content, and sibling views. To inspect the resolved result, read the final Frame after layout runs, including after terminal resizes.
To apply a mental model similar to responsive web or React-style layouts, declare relationships such as "center this", "fill the remaining space", "stay 1 cell to the right of that view", or "use 50% of the available width", and let the layout engine resolve the actual coordinates.
See View Deep Dive, Arrangement Deep Dive, Scrolling Deep Dive, and Drawing Deep Dive for more.
- Lexicon & Taxonomy
- Arrangement Modes
- Composition
- The Content Area
- The Viewport
- Responsive Mental Model
- Layout Engine
- How To
[!INCLUDE Layout Lexicon]
See Arrangement Deep Dive for more.
[!INCLUDE View Composition]
Content Area refers to the rectangle with a location of 0,0 with the size returned by GetContentSize().
The content area is the area where the view's content is drawn. Content can be any combination of the Text property, SubViews, and other content drawn by the View. The GetContentSize() method gets the size of the content area of the view.
The Content Area size tracks the size of the Viewport by default. If the content size is set via SetContentSize(), the content area is the provided size. If the content size is larger than the Viewport, scrolling is enabled.
The Viewport is a rectangle describing the portion of the Content Area that is 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.
To enable scrolling call View.SetContentSize() and then set Viewport.Location to positive values. Making Viewport.Location positive moves the Viewport down and to the right in the content.
See the Scrolling Deep Dive for details on how to enable scrolling.
The ViewportSettings property controls how the Viewport is constrained using xref:Terminal.Gui.ViewBase.ViewportSettingsFlags. By default, ViewportSettings is None, which provides sensible constraints for typical scrolling scenarios.
With no flags set, the Viewport is constrained as follows:
- No negative scrolling:
Viewport.XandViewport.Ycannot go below0. The user cannot scroll above or to the left of the content origin. - Content fills the viewport: The Viewport is clamped so that
Viewport.X + Viewport.Width <= ContentSize.WidthandViewport.Y + Viewport.Height <= ContentSize.Height. This prevents blank space from appearing when scrolling - the content always fills the visible area. - Last row/column always visible: Even if trying to scroll past the end of content, at least the last row and last column remain visible.
The flags are organized into categories:
Negative Location Flags - Allow scrolling before the content origin (0,0):
AllowNegativeX- PermitsViewport.X < 0(scroll left of content)AllowNegativeY- PermitsViewport.Y < 0(scroll above content)AllowNegativeLocation- Combines both X and Y
Greater Than Content Flags - Allow scrolling past the last row/column:
AllowXGreaterThanContentWidth- PermitsViewport.X >= ContentSize.WidthAllowYGreaterThanContentHeight- PermitsViewport.Y >= ContentSize.HeightAllowLocationGreaterThanContentSize- Combines both X and Y
Blank Space Flags - Allow blank space to appear when scrolling:
AllowXPlusWidthGreaterThanContentWidth- PermitsViewport.X + Viewport.Width > ContentSize.Width(blank space on right)AllowYPlusHeightGreaterThanContentHeight- PermitsViewport.Y + Viewport.Height > ContentSize.Height(blank space on bottom)AllowLocationPlusSizeGreaterThanContentSize- Combines both X and Y
Conditional Negative Flags - Allow negative scrolling only when viewport is larger than content:
AllowNegativeXWhenWidthGreaterThanContentWidth- Useful for centering content smaller than the viewAllowNegativeYWhenHeightGreaterThanContentHeight- Useful for centering content smaller than the viewAllowNegativeLocationWhenSizeGreaterThanContentSize- Combines both X and Y
Drawing Flags - Control clipping and clearing behavior:
ClipContentOnly- Clips drawing to the visible content area instead of the entire ViewportClearContentOnly- Clears only the visible content area (requiresClipContentOnly)Transparent- The view does not clear its background when drawingTransparentMouse- Mouse events pass through areas not occupied by SubViews
ScrollBar Flags - Enable built-in scrollbars:
HasVerticalScrollBar- Enables the built-inVerticalScrollBarwith xref:Terminal.Gui.Views.ScrollBarVisibilityMode.Auto behavior (automatically shown when content exceeds viewport)HasHorizontalScrollBar- Enables the built-inHorizontalScrollBarwith xref:Terminal.Gui.Views.ScrollBarVisibilityMode.Auto behavior (automatically shown when content exceeds viewport)HasScrollBars- Combines both vertical and horizontal scrollbar flags
To reason about Terminal.Gui layout, think of it as a small responsive layout language for TUIs:
XandYanswer where should this view start?WidthandHeightanswer how much space should it take?Posexpresses location relationships.Dimexpresses size relationships.Frameis the resolved result after layout runs.
To build adaptive UIs, work with Pos and Dim, not Frame.
Common patterns include:
- Pinning to an edge with
Pos.AnchorEnd () - Centering with
Pos.Center () - Following another view with
Pos.Right (otherView)orPos.Bottom (otherView) - Taking a percentage of the available space with
Dim.Percent (...) - Filling leftover space with
Dim.Fill ()orDim.Fill (to: otherView) - Growing to content with
Dim.Auto ()
When the terminal size changes, or when the size of a SuperView or referenced view changes, layout runs again and these relationships are resolved into a new Frame. This is what makes Terminal.Gui layouts responsive.
The primary layout API is:
XandYfor position, using xref:Terminal.Gui.ViewBase.PosWidthandHeightfor size, using xref:Terminal.Gui.ViewBase.Dim
These values are relative to the SuperView's content area, not the screen.
Label nameLabel = new () { Text = "Name:" };
Button okButton = new () { Text = "OK", X = Pos.AnchorEnd () };
TextField nameField = new ()
{
X = Pos.Right (nameLabel) + 1,
Y = Pos.Top (nameLabel),
Width = Dim.Fill (to: okButton)
};In this example:
nameLabelkeeps its content-based widthokButtonstays anchored to the end of the linenameFieldstretches and shrinks between them
If the terminal or SuperView grows or shrinks, the same declarations are re-evaluated and the final Frame values change automatically.
For advanced scenarios and custom layout primitives, the layout system also exposes virtual categorization hooks such as ReferencesOtherViews(), DependsOnSuperViewContentSize, CanContributeToAutoSizing, GetMinimumContribution(), IsFixed, and RequiresTargetLayout.
Label absoluteLabel = new () { X = 1, Y = 2, Width = 12, Height = 1, Text = "Absolute" };
Label responsiveLabel = new ()
{
Text = "Responsive",
X = Pos.Right (otherView),
Y = Pos.Center (),
Width = Dim.Fill (),
Height = Dim.Percent (50)
};xref:Terminal.Gui.ViewBase.Pos is the type of View.X and View.Y. To make a view's position respond to available space or other views instead of using a fixed coordinate, use it.
- Absolute position, by passing an integer -
Pos.Absolute () - Percentage of the
SuperViewsize -Pos.Percent () - Anchored from the end of the dimension -
Pos.AnchorEnd () - Centered -
Pos.Center () - Tracking another view -
Pos.Left (),Pos.Right (),Pos.Top (),Pos.Bottom () - Aligning as a group -
Pos.Align () - Computing from a function -
Pos.Func ()
All xref:Terminal.Gui.ViewBase.Pos coordinates are relative to the SuperView's content area.
xref:Terminal.Gui.ViewBase.Pos values can be combined using addition or subtraction, making it easy to express offsets in a responsive layout:
// Set the X coordinate to 10 characters left from the center
view.X = Pos.Center () - 10;
view.Y = Pos.Percent (20);
anotherView.X = Pos.AnchorEnd (10);
anotherView.Width = 9;
myView.X = Pos.X (view);
myView.Y = Pos.Bottom (anotherView) + 5;xref:Terminal.Gui.ViewBase.Dim is the type of View.Width and View.Height. To make size respond to content, terminal size, or sibling views instead of using a fixed number of cells, use it.
- Automatic size based on the view's content -
Dim.Auto ()- See Dim.Auto Deep Dive - Absolute size, by passing an integer -
Dim.Absolute () - Percentage of the
SuperViewcontent area -Dim.Percent () - Fill the remaining space -
Dim.Fill () - Fill up to another view -
Dim.Fill (to: otherView) - Track another view's size -
Dim.Width (),Dim.Height () - Compute from a function -
Dim.Func ()
Dim.Fill () is especially useful for responsive forms and panes. Note: Dim.Fill does not contribute to a SuperView's Dim.Auto () sizing unless minimumContentDim is specified. See Dim.Auto Deep Dive for details.
All xref:Terminal.Gui.ViewBase.Dim dimensions are relative to the SuperView's content area.
Like xref:Terminal.Gui.ViewBase.Pos, objects of type xref:Terminal.Gui.ViewBase.Dim can be combined using addition or subtraction:
// 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;classDiagram
class View ["View — location and size relative to SuperView"]
class Frame ["Frame — Rectangle"]
class Viewport ["Viewport — visible portion of Content Area"]
class Margin ["Margin — where Shadows live"]
class Border ["Border — Title and Arrangement controls"]
class Padding ["Padding — where ScrollBars live"]
class AdornmentImpl ["AdornmentImpl — lightweight settings"]
class Thickness ["Thickness — each side has a width"]
View --> Frame
View --> Viewport
View --> Margin : has
View --> Border : has
View --> Padding : has
Margin --|> AdornmentImpl
Border --|> AdornmentImpl
Padding --|> AdornmentImpl
AdornmentImpl --> Thickness : has
To solve common layout scenarios, use this section.
Scenario: A label on the left, a text field that stretches to fill available space, and a button anchored to the right. This is a classic responsive form row:
[label][ stretching text field ][button]
Label label = new () { Text = "_Name:" };
Button btn = new () { Text = "_OK", X = Pos.AnchorEnd () };
TextField textField = new ()
{
X = Pos.Right (label) + 1,
Width = Dim.Fill (to: btn)
};
superView.Add (label, textField, btn);The text field expands and contracts automatically as the available width changes.
Here to: btn names the Dim.Fill parameter that tells the text field where its fill should stop.
Scenario: Align buttons horizontally using Pos.Align(), as xref:Terminal.Gui.Views.Dialog does:
Button cancelBtn = new ()
{
Text = "_Cancel",
X = Pos.Align (Alignment.End)
};
Button okBtn = new ()
{
Text = "_OK",
X = Pos.Align (Alignment.End)
};
superView.Add (cancelBtn, okBtn);The Pos.Align method supports different alignments (Start, Center, End, Fill) and can add spacing between items via AlignmentModes.
Scenario: A centered view that auto-sizes to its content, with minimum and maximum constraints that account for adornments (xref:Terminal.Gui.ViewBase.Border, xref:Terminal.Gui.ViewBase.Margin, xref:Terminal.Gui.ViewBase.Padding). This is how xref:Terminal.Gui.Views.Dialog positions and sizes itself:
Window popup = new ()
{
X = Pos.Center (),
Y = Pos.Center (),
Width = Dim.Auto (
minimumContentDim: 20, // Minimum width
maximumContentDim: Dim.Percent (100) - Dim.Func (_ => popup.GetAdornmentsThickness ().Horizontal)),
Height = Dim.Auto (
minimumContentDim: 5, // Minimum height
maximumContentDim: Dim.Percent (100) - Dim.Func (_ => popup.GetAdornmentsThickness ().Vertical))
};The key insight is maximumContentDim subtracts the adornments thickness from 100% to ensure the view (including its xref:Terminal.Gui.ViewBase.Border, xref:Terminal.Gui.ViewBase.Margin, and xref:Terminal.Gui.ViewBase.Padding) never exceeds the SuperView's bounds.