Skip to content

HStackWidget ​

Arrange child widgets horizontally from left to right.

HStackWidget is a layout container that positions its children in a horizontal row. It automatically manages width distribution, allowing children to size based on content, fill available space, or use fixed widths.

Basic Usage ​

Create a horizontal layout using the fluent API with collection expression syntax:

csharp
using Hex1b;

var state = new FormState();

await using var terminal = Hex1bTerminal.CreateBuilder()
    .WithHex1bApp((app, options) => ctx => ctx.HStack(h => [
        h.Text("Name:"),
        h.Text("  "),
        h.TextBox(state.Name)
            .OnTextChanged(args => state.Name = args.NewText)
            .Fill(),
        h.Text("  "),
        h.Button("Save").OnClick(_ => state.Save())
    ]))
    .Build();

await terminal.RunAsync();

class FormState
{
    public string Name { get; set; } = "";
    public void Save() { /* ... */ }
}

Focus Management

HStackWidget manages focus for all its descendant widgets. Use Tab to move focus forward and Shift+Tab to move backward through focusable children.

Simple Horizontal Layout ​

Arrange widgets side by side:

csharp

All children are positioned from left to right with their natural content widths.

Sizing Children ​

HStackWidget supports three sizing modes for children:

Content Sizing (Default) ​

Children size to their natural content width:

csharp
ctx.HStack(h => [
    h.Text("Short"),
    h.Text("Longer Text"),
    h.Text("X")
])
// Each Text widget takes exactly the width it needs

Fill Sizing ​

Children with .Fill() expand to consume remaining space:

csharp

When multiple children use .Fill(), space is distributed evenly:

csharp
ctx.HStack(h => [
    h.Text("Left").Fill(),
    h.Text("Right").Fill()
])
// Both children get 50% of available width

Fixed Sizing ​

Children with .FixedWidth() take exactly the specified width:

csharp
ctx.HStack(h => [
    h.Text("Label").FixedWidth(20),
    h.TextBox(value, onChange).Fill()
])
// Label is always 20 cells wide

Weighted Fill ​

Use weighted fills to distribute space proportionally:

csharp
ctx.HStack(h => [
    h.Panel(sidebar).FillWidth(1),      // Gets 1/3 of space
    h.Panel(main).FillWidth(2)          // Gets 2/3 of space
])

Layout Algorithm ​

HStackWidget distributes width in two passes:

  1. Fixed Pass: Measure content-sized and fixed-width children, sum their widths
  2. Fill Pass: Distribute remaining width among fill children based on their weights

All children receive the full height of the HStack.

Focus Navigation ​

HStackWidget provides default keyboard bindings:

KeyAction
TabMove focus to next focusable widget
Shift+TabMove focus to previous focusable widget

Focus order follows the left-to-right order of children in the array.

Clipping ​

Content that extends beyond the HStack's bounds is clipped by default. Children cannot render outside the stack's area.

To allow overflow (not recommended), you would need to configure clipping at a lower level via the layout system.

Common Patterns ​

Form Layouts ​

Create label-value pairs:

csharp
ctx.VStack(v => [
    v.HStack(h => [
        h.Text("Name:").FixedWidth(15),
        h.TextBox(state.Name)
            .OnTextChanged(args => state.Name = args.NewText)
            .Fill()
    ]),
    v.HStack(h => [
        h.Text("Email:").FixedWidth(15),
        h.TextBox(state.Email)
            .OnTextChanged(args => state.Email = args.NewText)
            .Fill()
    ])
])

Button Groups ​

Arrange multiple buttons horizontally:

csharp
ctx.HStack(h => [
    h.Button("Save").OnClick(_ => Save()),
    h.Text("  "),
    h.Button("Cancel").OnClick(_ => Cancel()),
    h.Text("  "),
    h.Button("Help").OnClick(_ => ShowHelp())
])

Toolbar Layouts ​

Create toolbars with left and right sections:

csharp
ctx.HStack(h => [
    h.Text("File Edit View"),
    h.Text("").Fill(),  // Spacer pushes next items to right
    h.Text("User: Admin")
])

Two-Column Layouts ​

Split content into side-by-side columns:

csharp
ctx.HStack(h => [
    h.Border(b => [
        b.Text("Sidebar content")
    ]).FixedWidth(30),
    h.Border(b => [
        b.Text("Main content")
    ]).Fill()
])

Combining with VStack ​

Build complex grid-like layouts:

csharp
ctx.VStack(v => [
    v.HStack(h => [
        h.Text("A"),
        h.Text("B"),
        h.Text("C")
    ]),
    v.HStack(h => [
        h.Text("D"),
        h.Text("E"),
        h.Text("F")
    ])
])

Performance Considerations ​

  • HStack measures all children during layout
  • Content-sized children are measured with unbounded width constraints
  • Fill children are measured after fixed space is allocated
  • Large numbers of children may impact layout performance

Released under the MIT License.