Terminal.Gui provides a powerful Arrangement system that enables users to interactively move and resize views using the keyboard and mouse. This system supports both Tiled and Overlapped layout modes, allowing for flexible UI organization.
See the Layout Deep Dive for the broader layout system context.
- Overview
- Arrangement Modes
- Arrange Mode (Interactive)
- Tiled vs Overlapped Layouts
- Movable Views
- Resizable Views
- Creating Resizable Splitters
- Modal Views
- Runnable Views
- Examples
The Arrangement property controls how users can arrange views within their SuperView. The xref:Terminal.Gui.ViewBase.ViewArrangement enum provides flags that can be combined to specify arrangement behavior.
[!INCLUDE Arrangement Lexicon]
The ViewArrangement enum supports these flags (can be combined):
- Fixed (0) - View cannot be moved or resized (default)
- Movable (1) - View can be moved by the user
- LeftResizable (2) - Left edge can be resized
- RightResizable (4) - Right edge can be resized
- TopResizable (8) - Top edge can be resized
- BottomResizable (16) - Bottom edge can be resized
- Resizable (30) - All edges can be resized (combines all resize flags)
- Overlapped (32) - View overlaps other views (enables Z-order)
Views with ViewArrangement.Fixed cannot be moved or resized by the user:
var view = new View
{
Arrangement = ViewArrangement.Fixed // Default
};Views with ViewArrangement.Movable can be dragged with the mouse or moved with keyboard:
var window = new Window
{
Title = "Movable Window",
Arrangement = ViewArrangement.Movable
};User Interaction:
- Mouse: Drag the top Border
- Keyboard: Press
Ctrl+F5to enter Arrange Mode, use arrow keys to move
Views with ViewArrangement.Resizable can be resized by the user:
var window = new Window
{
Title = "Resizable Window",
Arrangement = ViewArrangement.Resizable
};User Interaction:
- Mouse: Drag any border edge
- Keyboard: Press
Ctrl+F5to enter Arrange Mode, pressTabto cycle resize handles
Combine flags for full desktop-like experience:
var window = new Window
{
Title = "Movable and Resizable",
Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable
};Note: When both xref:Terminal.Gui.ViewBase.ViewArrangement.Movable and xref:Terminal.Gui.ViewBase.ViewArrangement.Resizable are set, the top edge cannot be resized (Movable takes precedence).
For fine-grained control, use individual edge flags:
// Only bottom edge resizable
var view = new View
{
Arrangement = ViewArrangement.BottomResizable
};
// Left and right edges resizable
var view2 = new View
{
Arrangement = ViewArrangement.LeftResizable | ViewArrangement.RightResizable
};Arrange Mode is an interactive mode for arranging views using the keyboard. It is activated by pressing the Arrange Key (default: Ctrl+F5, configurable via Application.DefaultKeyBindings for Command.Arrange).
When the user presses Ctrl+F5:
- Visual indicators appear on arrangeable views
- If ViewArrangement.Movable, a move indicator (
◊) appears in top-left corner - If ViewArrangement.Resizable, pressing
Tabcycles to resize indicators - Arrow keys move or resize the view
- Press
Esc,Ctrl+F5, or click outside to exit
The Border shows visual indicators based on arrangement options:
| Arrangement Flag | Indicator | Location |
|---|---|---|
| Movable | ◊ (Glyphs.Move) |
Top-left corner |
| Resizable | ⇲ (Glyphs.SizeBottomRight) |
Bottom-right corner |
| LeftResizable | ↔ (Glyphs.SizeHorizontal) |
Left edge, centered |
| RightResizable | ↔ (Glyphs.SizeHorizontal) |
Right edge, centered |
| TopResizable | ↕ (Glyphs.SizeVertical) |
Top edge, centered |
| BottomResizable | ↕ (Glyphs.SizeVertical) |
Bottom edge, centered |
- Arrow Keys - Move or resize based on active mode
- Tab - Cycle between move and resize modes (if both available)
- Shift+Tab - Cycle backwards
- Esc - Exit Arrange Mode
- Ctrl+F5 - Exit Arrange Mode
For a View to be arrangeable:
- Must be part of a
SuperView - Position and dimensions must be independent of other SubViews
- Must have
Arrangementflags set - Typically needs a xref:Terminal.Gui.ViewBase.Border for mouse interaction
In Tiled layouts, SubViews typically do not overlap. There is no Z-order; all views are at the same layer.
var container = new View { Arrangement = ViewArrangement.Fixed };
var view1 = new View { X = 0, Y = 0, Width = 20, Height = 10 };
var view2 = new View { X = 21, Y = 0, Width = 20, Height = 10 };
container.Add(view1, view2);
// Views are side-by-side, non-overlappingCharacteristics:
- Default mode for most TUI applications
- Views use Pos and Dim for relative positioning
- No Z-order management needed
- More predictable layout behavior
In Overlapped layouts, SubViews can overlap with Z-order determining visual stacking.
Enable overlapped mode with ViewArrangement.Overlapped:
var container = new View
{
Arrangement = ViewArrangement.Overlapped
};
var window1 = new Window
{
X = 5, Y = 3, Width = 40, Height = 15,
Arrangement = ViewArrangement.Movable | ViewArrangement.Overlapped
};
var window2 = new Window
{
X = 15, Y = 8, Width = 40, Height = 15,
Arrangement = ViewArrangement.Movable | ViewArrangement.Overlapped
};
container.Add(window1, window2);
// window2 will overlap window1Characteristics:
- Z-order determined by SubViews collection order
- Later views appear above earlier views
- Tab navigation constrained to current overlapped view
- Use
Ctrl+Tab/Ctrl+Shift+Tabto switch between overlapped views
Views with ViewArrangement.Movable can be repositioned by the user.
var window = new Window
{
Title = "Drag Me!",
X = 10,
Y = 5,
Width = 40,
Height = 15,
Arrangement = ViewArrangement.Movable,
BorderStyle = LineStyle.Single
};- Click and drag the top Border to move the view
- The view's
Frameupdates as it moves - Release the mouse to complete the move
- Press
Ctrl+F5to enter Arrange Mode - A move indicator (
◊) appears in the top-left corner - Use arrow keys to move the view
- Press
EscorCtrl+F5to exit Arrange Mode
Views with resizable flags can be resized by the user on specific edges.
var window = new Window
{
Title = "Resize Me!",
Arrangement = ViewArrangement.Resizable,
BorderStyle = LineStyle.Single
};// Only right and bottom edges resizable
var view = new View
{
Arrangement = ViewArrangement.RightResizable | ViewArrangement.BottomResizable,
BorderStyle = LineStyle.Single
};- Click and drag any enabled border edge
- Resize indicators appear on hover
- The view's
WidthandHeightupdate
- Press
Ctrl+F5to enter Arrange Mode - Press
Tabto cycle to resize mode - Resize indicator (
⇲) appears - Use arrow keys to resize
- Press
EscorCtrl+F5to exit
A common pattern in tiled layouts is creating a resizable splitter between two panes.
View leftPane = new ()
{
X = 0,
Y = 0,
Width = Dim.Fill(Dim.Func(_ => rightPane.Frame.Width)),
Height = Dim.Fill(),
BorderStyle = LineStyle.Single
};
View rightPane = new ()
{
X = Pos.Right(leftPane) - 1,
Y = 0,
Width = Dim.Fill(),
Height = Dim.Fill(),
Arrangement = ViewArrangement.LeftResizable,
BorderStyle = LineStyle.Single,
SuperViewRendersLineCanvas = true
};
rightPane.Border.Thickness = new Thickness(1, 0, 0, 0); // Only left border
container.Add(leftPane, rightPane);How it works:
rightPanehas ViewArrangement.LeftResizable - its left border is draggableleftPaneuses Dim.Fill with a function to fill remaining spaceSuperViewRendersLineCanvas = trueensures proper line rendering- Only the left border is visible, acting as the splitter
View topPane = new ()
{
X = 0,
Y = 0,
Width = Dim.Fill(),
Height = Dim.Fill(Dim.Func(_ => bottomPane.Frame.Height)),
BorderStyle = LineStyle.Single
};
View bottomPane = new ()
{
X = 0,
Y = Pos.Bottom(topPane) - 1,
Width = Dim.Fill(),
Height = Dim.Fill(),
Arrangement = ViewArrangement.TopResizable,
BorderStyle = LineStyle.Single,
SuperViewRendersLineCanvas = true
};
bottomPane.Border.Thickness = new Thickness(1, 0, 0, 0); // Only top border
container.Add(topPane, bottomPane);Modal views run as exclusive applications that capture all user input until closed.
See the Multitasking Deep Dive for complete details on modal execution.
A view is modal when:
- Run via xref:Terminal.Gui.App.IApplication
.Run - Runnable.Modal ==
true
- Exclusive Input - All keyboard and mouse input goes to the modal view
- Constrained Z-Order - Modal view has Z-order of 1, everything else at 0
- Blocks Execution - xref:Terminal.Gui.App.IApplication
.Runblocks untilApplication.RequestStop()is called - Own SessionToken - Each modal view has its own SessionToken
- Dialog - Centered modal window with button support
- MessageBox - Simple message dialogs
- Wizard - Multi-step modal dialogs
var dialog = new Dialog
{
Title = "Confirm",
Width = 40,
Height = 10
};
var label = new Label
{
Text = "Are you sure?",
X = Pos.Center(),
Y = 2
};
dialog.Add(label);
var ok = new Button { Text = "OK" };
ok.Accepting += (s, e) => Application.RequestStop();
dialog.AddButton(ok);
// Run modally - blocks until closed
Application.Run(dialog);
// Dialog has been closedRunnable views are those run via Application.Run. Each non-modal Runnable view operates as a self-contained "application" with its own SessionToken.
See the Multitasking Deep Dive for complete details.
var runnable = new Runnable
{
Modal = false // Non-modal
};
// Runs as independent application
Application.Run(runnable);Characteristics:
- Has its own
SessionToken - Events dispatched independently
- Can run on separate threads
- See
BackgroundWorkerCollectionfor multi-threaded examples
| Aspect | Modal | Non-Modal |
|---|---|---|
| Input | Exclusive | Shared |
| Z-Order | Constrained (1 vs 0) | Full Z-order support |
| Blocks Execution | Yes | No |
| Use Case | Dialogs, confirmations | Multi-window apps |
SubViews do not overlap, positioned side-by-side or top-to-bottom:
var container = new View();
var left = new View
{
X = 0,
Y = 0,
Width = Dim.Percent(50),
Height = Dim.Fill()
};
var right = new View
{
X = Pos.Right(left),
Y = 0,
Width = Dim.Fill(),
Height = Dim.Fill()
};
container.Add(left, right);Benefits:
- Simpler layout logic
- No Z-order management
- More predictable behavior
- Standard for most TUI applications
SubViews can overlap with Z-order determining which is on top:
var container = new View
{
Arrangement = ViewArrangement.Overlapped
};
var window1 = new Window
{
X = 5,
Y = 3,
Width = 40,
Height = 15,
Arrangement = ViewArrangement.Movable | ViewArrangement.Overlapped
};
var window2 = new Window
{
X = 15,
Y = 8,
Width = 40,
Height = 15,
Arrangement = ViewArrangement.Movable | ViewArrangement.Overlapped
};
container.Add(window1, window2);
// window2 appears on top of window1Z-Order:
- Order in
SubViewsdetermines Z-order - Later views appear above earlier views
- Use View.MoveSubViewToEnd / View.MoveSubViewToStart to change Z-order
Navigation:
Tab/Shift+Tab- Navigate within current overlapped viewCtrl+Tab(Ctrl+PageDown) - Switch to next overlapped viewCtrl+Shift+Tab(Ctrl+PageUp) - Switch to previous overlapped view
using Terminal.Gui;
using IApplication app = Application.Create();
app.Init();
Window window = new ()
{
Title = "Drag and Resize Me! (Ctrl+F5 for keyboard mode)",
X = Pos.Center(),
Y = Pos.Center(),
Width = 50,
Height = 15,
Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable,
BorderStyle = LineStyle.Double
};
Label label = new ()
{
Text = "Try dragging the border with mouse\nor press Ctrl+F5!",
X = Pos.Center(),
Y = Pos.Center()
};
window.Add(label);
app.Run(window);
window.Dispose();using IApplication app = Application.Create();
app.Init();
Runnable top = new ();
FrameView leftPane = new ()
{
Title = "Left Pane",
X = 0,
Y = 0,
Width = Dim.Fill(Dim.Func(_ => rightPane.Frame.Width)),
Height = Dim.Fill()
};
FrameView rightPane = new ()
{
Title = "Right Pane (drag left edge)",
X = Pos.Right(leftPane) - 1,
Y = 0,
Width = Dim.Fill(),
Height = Dim.Fill(),
Arrangement = ViewArrangement.LeftResizable,
SuperViewRendersLineCanvas = true
};
rightPane.Border.Thickness = new Thickness(1, 0, 0, 0);
top.Add(leftPane, rightPane);
app.Run(top);
top.Dispose();using IApplication app = Application.Create();
app.Init();
Runnable desktop = new ()
{
Arrangement = ViewArrangement.Overlapped
};
Window window1 = new ()
{
Title = "Window 1",
X = 5,
Y = 3,
Width = 40,
Height = 12,
Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable | ViewArrangement.Overlapped
};
Window window2 = new ()
{
Title = "Window 2 (overlaps Window 1)",
X = 15,
Y = 8,
Width = 40,
Height = 12,
Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable | ViewArrangement.Overlapped
};
desktop.Add(window1, window2);
app.Run(desktop);
desktop.Dispose();using Terminal.Gui;
using Terminal.Gui.Configuration;
using IApplication app = Application.Create();
// Change the arrange key
Application.DefaultKeyBindings[Command.Arrange] = Bind.All (Key.F2);
app.Init();
Window window = new ()
{
Title = "Press F2 to enter arrange mode",
Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable
};
app.Run(window);
window.Dispose();Arrangement only works when:
- View has a SuperView - Root views cannot be arranged
- Independent Position/Size - Views with Pos.Align or complex Dim constraints may not resize properly
- Border Required - Mouse-based arrangement requires a visible Border
When creating splitters, set SuperViewRendersLineCanvas = true:
rightPane.SuperViewRendersLineCanvas = true;This ensures LineCanvas properly handles line intersections at borders.
For overlapped views, manage Z-order with:
// Bring a view to the front (end of SubViews = highest Z-order)
container.MoveSubViewToEnd (window1);
// Send a view to the back (start of SubViews = lowest Z-order)
container.MoveSubViewToStart (window2);
// Move one position towards front/back
container.MoveSubViewTowardsEnd (window1);
container.MoveSubViewTowardsStart (window2);Monitor arrangement changes by handling layout events:
view.FrameChanged += (_, e) =>
{
// Fires when Frame changes (move or resize)
Console.WriteLine ($"View moved/resized to {e.CurrentValue}");
};- Layout Deep Dive - Overall layout system
- View Deep Dive - View base class
- Multitasking Deep Dive - Modal and runnable views
- Drawing Deep Dive - LineCanvas and borders
- Configuration Deep Dive - Configuring the Arrange key via
Application.DefaultKeyBindings
Arrangement- xref:Terminal.Gui.ViewBase.ViewArrangement
- xref:Terminal.Gui.ViewBase.Border
Application.GetDefaultKey (Command.Arrange)- Runnable.Modal
The UICatalog application demonstrates arrangement:
- Arrangement Editor - Interactive arrangement demonstration
- Overlapped scenario - Shows overlapped window management
- Splitter examples - Various splitter configurations