The Layout Engine

At the heart of Paper is a powerful and flexible layout engine. It's what takes your simple, declarative code and translates it into a complex and responsive user interface.

Overview: Rows & Columns

The most fundamental layout elements in Paper are Rows and Columns. These containers dictate how their direct children are arranged.

  • Paper.Row() arranges its children horizontally, from left to right.

  • Paper.Column() arranges its children vertically, from top to bottom.

// A Row with three children
using (paper.Row("MyRow").Enter())
{
    paper.Box("Box1").Size(50); // Will be on the left
    paper.Box("Box2").Size(50); // In the middle
    paper.Box("Box3").Size(50); // On the right
}

// A Column with three children
using (paper.Column("MyColumn").Enter())
{
    paper.Box("BoxA").Size(50); // Will be at the top
    paper.Box("BoxB").Size(50); // In the middle
    paper.Box("BoxC").Size(50); // At the bottom
}

By nesting Rows and Columns, you can build almost any grid-like structure. This is the primary way you will construct your UIs.

using (paper.Row("MainLayout").Enter())
{
    // First child of the Row: a Column for a sidebar
    using (paper.Column("Sidebar").Width(200).Enter())
    {
        paper.Box("NavItem1").Height(40);
        paper.Box("NavItem2").Height(40);
    }

    // Second child of the Row: a Column for the main content
    using (paper.Column("Content").Enter())
    {
        paper.Box("Title").Height(60);
        paper.Box("Body");
    }
}

Sizing Units: The Building Blocks of Responsiveness

The real power of the layout engine comes from its flexible sizing units. You can define the width and height of any element using one of four distinct unit types.

1. Pixels (Paper.Pixels())

This is the most straightforward unit. It specifies a size in absolute screen pixels. It's predictable and useful for elements that should never change size, like icons or small buttons.

// This box will always be 150 pixels wide and 50 pixels tall
paper.Box("FixedSize").Width(paper.Pixels(150)).Height(paper.Pixels(50));

2. Percent (Paper.Percent())

This unit specifies a size as a percentage of the parent element's available space. This is essential for creating fluid layouts that adapt to different window sizes.

using (paper.Row("Container").Width(paper.Pixels(500)).Enter())
{
    // This box will be 50% of 500px = 250px wide
    paper.Box("Half").Width(paper.Percent(50));

    // This box will be 25% of 500px = 125px wide
    paper.Box("Quarter").Width(paper.Percent(25));
}

3. Stretch (Paper.Stretch())

This is arguably the most powerful unit. It tells an element to fill up any remaining available space in a container after all Pixels, Percent, and Auto elements have been calculated.

If multiple children use Stretch, the remaining space is divided between them based on their stretch factor.

using (paper.Row("Toolbar").Width(paper.Pixels(500)).Enter())
{
    // A fixed-size button on the left
    paper.Box("BackButton").Width(paper.Pixels(100));

    // The title bar takes up all the remaining space
    paper.Box("Title").Width(paper.Stretch(1.0));

    // A fixed-size button on the right
    paper.Box("MenuButton").Width(paper.Pixels(100));
}

In the example above, the "Title" box will be 300px wide (500 - 100 - 100).

Here's how stretch factors work:

using (paper.Row("SplitView").Enter())
{
    // Takes up 1/3 of the available space
    paper.Box("PanelA").Width(paper.Stretch(1.0));

    // Takes up 2/3 of the available space (twice the factor of PanelA)
    paper.Box("PanelB").Width(paper.Stretch(2.0));
}

4. Auto (Paper.Auto)

This unit tells the layout engine to automatically determine the size of an element based on its content. This is perfect for elements whose size you don't know in advance, like a text box or a container that wraps its children.

  • For an element with text, Auto will make it just large enough to fit the text.

  • For a container, Auto will make it just large enough to enclose all its children.

// This button's width will be calculated to fit its text exactly.
paper.Box("AutoButton")
    .Width(paper.Auto)
    .Height(40)
    .Text(Text.Center("A Button With Some Text", myFont, Color.Black));

// This Column's height will be exactly 150px (50 + 50 + 50).
using (paper.Column("AutoColumn").Height(paper.Auto).Enter())
{
    paper.Box("Child1").Height(50);
    paper.Box("Child2").Height(50);
    paper.Box("Child3").Height(50);
}

Positioning: In the Flow or On Its Own

By default, all elements are ParentDirected, meaning their position is determined by the layout container they are in (the Row or Column). However, you can take full control of an element's position.

PositionType.SelfDirected (Absolute Positioning)

Setting an element's PositionType to SelfDirected removes it from the normal layout flow. It no longer affects its siblings, and its position is now relative to its parent's top-left corner.

You can then use methods like .Left(), .Top(), .Right(), and .Bottom() to place it precisely.

using (paper.Box("Canvas")
    .Width(500).Height(300)
    .Enter())
{
    // This element is NOT part of the normal layout flow.
    // It will be placed 20px from the left and 30px from the top
    // of the "Canvas" container.
    paper.Box("Overlay")
        .PositionType(PositionType.SelfDirected)
        .Left(20)
        .Top(30)
        .Size(100);
}

Spacing: Margin and Padding

To add space around your elements, you use .Margin(). Margin pushes other elements away.

The .Margin() method is highly flexible. You can provide one, two, or four values:

  • Margin(10): 10px margin on all four sides.

  • Margin(10, 20): 10px on top/bottom, 20px on left/right.

  • Margin(10, 20, 30, 40): 10px top, 20px right, 30px bottom, 40px left.

You can also use sizing units like Stretch in your margins to center elements.

using (paper.Row("Container").Height(100).Enter())
{
    // By setting the left and right margin to Stretch(1),
    // the button will be perfectly centered horizontally.
    paper.Box("CenteredButton")
        .Width(150)
        .Margin(paper.Stretch(1.0), 0, paper.Stretch(1.0), 0);
}

Note: True "padding" (space inside an element's border) is achieved by nesting. Place a container with a margin inside another container to create an inset effect.

With these tools—Rows, Columns, Sizing Units, Positioning, and Spacing—you have a complete and expressive system for building any user interface you can design. The next page will cover Styling and Appearance.

Last updated