Mise en page

August 16, 2026 · View on GitHub

Les widgets s'empilent verticalement par défaut. Tout le reste est opt-in.


Gap – Mettez de l'espace entre les choses

La fonction de mise en page la plus utilisée. Insère un espace vertical vide.

proton.H4(ctx, "Section Title")
proton.Gap(ctx, 8)
proton.Label(ctx, "Section content.")
proton.Gap(ctx, 24)
proton.H4(ctx, "Next Section")
proton.Gap(ctx proton.Context, dp float32)

8dp est un petit écart. 16dp est moyen. 24dp est grand. Ces trois couvrent la plupart des cas.


Rangée — Côte à côte

Place les enfants horizontalement, de gauche à droite.

proton.Row(ctx,
    func(ctx proton.Context) { proton.Label(ctx, "Name:") },
    func(ctx proton.Context) { proton.Gap(ctx, 8) },
    func(ctx proton.Context) { proton.Label(ctx, "Alice") },
)

Chaque enfant est un « func(proton.Context) ». Appelez les widgets que vous voulez à l'intérieur.

proton.Row(ctx proton.Context, widgets ...func(proton.Context))

Colonne ## — Groupe vertical explicite

Empile les enfants verticalement en tant que groupe nommé. Rarement nécessaire au plus haut niveau (les widgets s'empilent automatiquement), mais utile à l'intérieur de Row ou Split lorsque le le côté droit doit être composé de plusieurs éléments empilés.

proton.Row(ctx,
    func(ctx proton.Context) {
        proton.Label(ctx, "Left side")
    },
    func(ctx proton.Context) { proton.Gap(ctx, 16) },
    func(ctx proton.Context) {
        proton.Column(ctx,
            func(ctx proton.Context) { proton.Label(ctx, "Right top") },
            func(ctx proton.Context) { proton.Gap(ctx, 4) },
            func(ctx proton.Context) { proton.Muted(ctx, "Right bottom") },
        )
    },
)
proton.Column(ctx proton.Context, widgets ...func(proton.Context))

RowSpread — Espace entre

Comme Row mais met un espace horizontal restant entre les enfants, en poussant le premier vers le bord gauche et le dernier vers la droite.

// title on the left, version on the right
proton.RowSpread(ctx,
    func(ctx proton.Context) { proton.H5(ctx, "My App") },
    func(ctx proton.Context) { proton.Caption(ctx, "v1.2.0") },
)
proton.RowSpread(ctx proton.Context, widgets ...func(proton.Context))

RowEnd — Tout à droite

Pousse tous les enfants vers le bord droit.

proton.RowEnd(ctx,
    func(ctx proton.Context) {
        proton.Pad(ctx, 4, func(ctx proton.Context) {
            if proton.OutlineButton(ctx, &u.cancel, "Cancel") { handleCancel() }
        })
    },
    func(ctx proton.Context) { proton.Gap(ctx, 8) },
    func(ctx proton.Context) {
        proton.Pad(ctx, 4, func(ctx proton.Context) {
            if proton.Button(ctx, &u.save, "Save") { handleSave() }
        })
    },
)
proton.RowEnd(ctx proton.Context, widgets ...func(proton.Context))

GrowRow et GrowColumn — Dispositions extensibles

Lorsqu'un enfant doit remplir tout l'espace restant et que les autres restent leur taille naturelle, utilisez GrowRow (horizontal) ou GrowColumn (vertical) avec « GrowItem » et « FixedItem ».

// search bar: label fixed, input stretches, button fixed
proton.GrowRow(ctx,
    proton.FixedItem(ctx, func(ctx proton.Context) {
        proton.Label(ctx, "Search:")
    }),
    proton.GrowItem(ctx, func(ctx proton.Context) {
        proton.Input(ctx, &u.search, "Type to search...")
    }),
    proton.FixedItem(ctx, func(ctx proton.Context) {
        proton.Pad(ctx, 4, func(ctx proton.Context) {
            if proton.Button(ctx, &u.searchBtn, "Go") { doSearch() }
        })
    }),
)

GrowItem prend tout l'espace restant. FixedItem ne prend que ce dont il a besoin. Plusieurs « GrowItem » répartissent l'espace restant de manière égale.

proton.GrowRow(ctx proton.Context, children ...proton.FlexItem)
proton.GrowColumn(ctx proton.Context, children ...proton.FlexItem)
proton.GrowItem(ctx proton.Context, fn func(proton.Context)) proton.FlexItem
proton.FixedItem(ctx proton.Context, fn func(proton.Context)) proton.FlexItem

FlexSpacer — Séparez les frères et sœurs

Un espace vide extensible. Mettez-le entre les FixedItem pour les pousser en face bords sans utiliser RowSpread.

proton.GrowRow(ctx,
    proton.FixedItem(ctx, func(ctx proton.Context) { proton.Caption(ctx, "left") }),
    proton.FlexSpacer(),
    proton.FixedItem(ctx, func(ctx proton.Context) { proton.Caption(ctx, "right") }),
)
proton.FlexSpacer() proton.FlexItem

Split — Volets côte à côte

Divise la largeur disponible entre deux sections. leftFraction est la proportion le volet de gauche obtient, de 0,0 à 1,0.

proton.Split(ctx, 0.35,
    func(ctx proton.Context) {
        // sidebar — gets 35% of the width
        proton.Label(ctx, "Sidebar")
    },
    func(ctx proton.Context) {
        // content — gets the remaining 65%
        proton.Label(ctx, "Content")
    },
)
proton.Split(ctx proton.Context, leftFraction float32, left func(proton.Context), right func(proton.Context))

HSplit — Haut et bas

Même idée mais vertical.

proton.HSplit(ctx, 0.7,
    func(ctx proton.Context) { proton.Label(ctx, "Main content") },
    func(ctx proton.Context) { proton.Label(ctx, "Status bar") },
)
proton.HSplit(ctx proton.Context, topFraction float32, top func(proton.Context), bottom func(proton.Context))

ResizeSplit — L'utilisateur peut faire glisser le séparateur

Comme Split mais l'utilisateur peut faire glisser la poignée entre les deux volets pour redimensionnez-les. Le defaultFraction est la position initiale.

type UI struct {
    split proton.ResizeSplitState
}

proton.ResizeSplit(ctx, &u.split, 0.30, leftFn, rightFn)

ResizeSplitState.Fraction commence à 0 et est défini sur defaultFraction sur la première image. Après cela, la position de déplacement de l'utilisateur est mémorisée.

proton.ResizeSplit(ctx proton.Context, state *proton.ResizeSplitState, defaultFraction float32, left func(proton.Context), right func(proton.Context))
proton.ResizeHSplit(ctx proton.Context, state *proton.ResizeSplitState, defaultFraction float32, top func(proton.Context), bottom func(proton.Context))

Centre

Place le contenu au centre de l'espace disponible. Idéal pour les états vides et écrans de chargement.

proton.Center(ctx, func(ctx proton.Context) {
    proton.Muted(ctx, "Nothing here yet.")
})
proton.Center(ctx proton.Context, fn func(proton.Context))

Rembourrage

Pad — Les quatre côtés

proton.Pad(ctx, 16, func(ctx proton.Context) {
    proton.Label(ctx, "16dp of breathing room on all sides")
})

PadH — Gauche et droite uniquement

proton.PadH(ctx, 24, func(ctx proton.Context) {
    proton.Label(ctx, "horizontal padding only")
})

PadV — Haut et bas uniquement

proton.PadV(ctx, 12, func(ctx proton.Context) {
    proton.Label(ctx, "vertical padding only")
})

PadSides — Chaque bord individuellement

Les arguments sont en haut, à droite, en bas, à gauche – dans le même ordre que la marge/remplissage CSS.

proton.PadSides(ctx, 8, 16, 8, 16, func(ctx proton.Context) {
    proton.Label(ctx, "8dp top/bottom, 16dp left/right")
})
proton.Pad(ctx proton.Context, dp float32, fn func(proton.Context))
proton.PadH(ctx proton.Context, dp float32, fn func(proton.Context))
proton.PadV(ctx proton.Context, dp float32, fn func(proton.Context))
proton.PadSides(ctx proton.Context, top, right, bottom, left float32, fn func(proton.Context))

Grid — Grille à colonnes fixes

Dispose les enfants dans une grille avec un nombre fixe de colonnes. Chaque cellule obtient une part égale de la largeur disponible.

proton.Grid(ctx, 3, 8,   // 3 columns, 8dp gap
    func(ctx proton.Context) { proton.Label(ctx, "one") },
    func(ctx proton.Context) { proton.Label(ctx, "two") },
    func(ctx proton.Context) { proton.Label(ctx, "three") },
    func(ctx proton.Context) { proton.Label(ctx, "four") },
    func(ctx proton.Context) { proton.Label(ctx, "five") },
)

Les cellules s'enroulent automatiquement sur de nouvelles lignes. Si la dernière ligne contient moins de cellules cols, les emplacements restants sont vides.

proton.Grid(ctx proton.Context, cols int, gapDp float32, cells ...func(proton.Context))

ZStack — Dessinez des objets les uns sur les autres

Superpose plusieurs widgets à la même position. Le premier enfant est sur le en bas ; le dernier est au dessus.

proton.ZStack(ctx,
    func(ctx proton.Context) {
        // bottom layer — a background shape
        proton.RoundRect(ctx, proton.RGB(0x1e1e2e), 0, 100, 12)
    },
    func(ctx proton.Context) {
        // top layer — text floating over the shape
        proton.Center(ctx, func(ctx proton.Context) {
            proton.Label(ctx, "Text on top")
        })
    },
)
proton.ZStack(ctx proton.Context, layers ...func(proton.Context))

MinSize et MaxWidth — Contraintes de taille

// at least 200dp wide and 48dp tall
proton.MinSize(ctx, 200, 48, func(ctx proton.Context) {
    if proton.Button(ctx, &u.btn, "OK") { handleOK() }
})

// no wider than 420dp — keeps forms readable on wide windows
proton.MaxWidth(ctx, 420, func(ctx proton.Context) {
    proton.Input(ctx, &u.email, "Email address")
    proton.Gap(ctx, 8)
    proton.Input(ctx, &u.password, "Password")
})
proton.MinSize(ctx proton.Context, widthDp, heightDp float32, fn func(proton.Context))
proton.MaxWidth(ctx proton.Context, widthDp float32, fn func(proton.Context))

Passez 0 pour l'une ou l'autre dimension de « MinSize » pour laisser cet axe sans contrainte.


Une application typique à deux colonnes

func draw(ctx proton.Context, u *UI) {
    // header
    proton.PadSides(ctx, 0, 0, 12, 0, func(ctx proton.Context) {
        proton.RowSpread(ctx,
            func(ctx proton.Context) { proton.H5(ctx, "My App") },
            func(ctx proton.Context) { proton.Caption(ctx, "v1.0") },
        )
    })
    proton.Divider(ctx)
    proton.Gap(ctx, 16)

    // body
    proton.ResizeSplit(ctx, &u.split, 0.28,
        func(ctx proton.Context) {
            drawSidebar(ctx, u)
        },
        func(ctx proton.Context) {
            proton.PadH(ctx, 16, func(ctx proton.Context) {
                drawContent(ctx, u)
            })
        },
    )
}