arrangement.md 6.0 KB

View Layout Arrangement

Terminal.Gui provides a feature of Layout known as Arrangement, which controls how the user can use the mouse and keyboard to arrange views and enables either Tiled or Overlapped layouts. Arrangement is a sub-topic of Layout.

Arrangement Taxonomy & Lexicon

[!INCLUDE Arrangement Lexicon]

Arrangement

Describes the feature of Layout which controls how the user can use the mouse and keyboard to arrange views and enables either Tiled or Overlapped layouts. The @Terminal.Gui.ViewBase.View.Arrangement property controls the arrangement behavior for each view.

Arrange Mode

The Arrange Modes are set via the @Terminal.Gui.ViewBase.View.Arrangement property. When a user presses Ctrl+F5 (configurable via the @Terminal.Gui.App.Application.ArrangeKey property) the application goes into Arrange Mode.

In this mode, indicators are displayed on an arrangeable view indicating which aspect of the View can be arranged:

The up/down/left/right cursor keys will act appropriately. Esc, Ctrl+F5 or clicking outside of the Border will exit Arrange Mode.

Modal

A modal view is one that is run as an "application" via @Terminal.Gui.App.Application.Run(System.Func{System.Exception,System.Boolean},Terminal.Gui.Drivers.IConsoleDriver) where Modal == true.

Dialog, MessageBox, and Wizard are the prototypical examples. When run this way, there IS a z-order but it is highly-constrained: the modal view has a z-order of 1 and everything else is at 0.

Movable

Describes a View that can be moved by the user using the keyboard or mouse. Movable is enabled on a per-View basis by setting the @Terminal.Gui.ViewBase.ViewArrangement.Movable flag on @Terminal.Gui.ViewBase.View.Arrangement.

Dragging on the top @Terminal.Gui.ViewBase.View.Border of a View will move such a view. Pressing Ctrl+F5 will activate Arrange Mode letting the user move the view with the up/down/left/right cursor keys.

Overlapped

A form of layout 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.

Overlapped is enabled on a per-View basis by setting the @Terminal.Gui.ViewBase.ViewArrangement.Overlapped flag on @Terminal.Gui.ViewBase.View.Arrangement.

Resizable

Describes a View that can be sized by the user using the keyboard or mouse. Resizable is enabled on a per-View basis by setting the @Terminal.Gui.ViewBase.ViewArrangement.Resizable flag on @Terminal.Gui.ViewBase.View.Arrangement.

Dragging on the left, right, or bottom @Terminal.Gui.ViewBase.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.ViewBase.View.Arrangement is set to @Terminal.Gui.ViewBase.ViewArrangement.Fixed.

Creating a Resizable Splitter

A common pattern in tiled layouts is to create a resizable splitter between two views. This can be achieved using the @Terminal.Gui.ViewBase.ViewArrangement.LeftResizable, @Terminal.Gui.ViewBase.ViewArrangement.RightResizable, @Terminal.Gui.ViewBase.ViewArrangement.TopResizable, or @Terminal.Gui.ViewBase.ViewArrangement.BottomResizable flags.

Here's an example of creating a horizontal resizable splitter between two views:

// Create left pane that fills remaining space
View leftPane = new ()
{
    X = 0,
    Y = 0,
    Width = Dim.Fill (Dim.Func (_ => rightPane.Frame.Width)),
    Height = Dim.Fill (),
    CanFocus = true
};

// Create right pane with resizable left border (acts as splitter)
View rightPane = new ()
{
    X = Pos.Right (leftPane) - 1,
    Y = 0,
    Width = Dim.Fill (),
    Height = Dim.Fill (),
    Arrangement = ViewArrangement.LeftResizable,
    BorderStyle = LineStyle.Single,
    SuperViewRendersLineCanvas = true,
    CanFocus = true
};
rightPane.Border!.Thickness = new (1, 0, 0, 0); // Only left border

container.Add (leftPane, rightPane);

In this example:

  • The rightPane has ViewArrangement.LeftResizable which makes its left border draggable
  • The left border acts as a splitter that users can drag to resize both panes
  • The leftPane uses Dim.Fill with a function that subtracts the rightPane's width to automatically fill the remaining space
  • The rightPane has SuperViewRendersLineCanvas = true to ensure the border is rendered properly
  • Only the left border is shown by setting Border.Thickness to (1, 0, 0, 0)

For a vertical splitter (top and bottom panes), use ViewArrangement.TopResizable or ViewArrangement.BottomResizable instead.

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).