Everything in Ply is an element. This single building block handles sizing, layout, color, borders, overflow, and nesting.
🔗The element builder
Create an element with ui.element(), configure it with chained methods, then
finalize with .children() or .empty():
ui.element()
.width(grow!())
.height(fixed!(200.0))
.background_color(0x262220)
.children(|ui| {
ui.text("Hello!", |t| t.font_size(24).color(0xFFFFFF));
});🔗Sizing
Every element has a width and height, set with four sizing macros:
| Macro | Behavior |
|---|---|
grow!() | Fill all available space |
fit!() | Shrink to fit content |
fixed!(200.0) | Exactly 200 pixels |
percent!(0.5) | 50% of parent's size |
grow! and fit! accept optional min/max bounds:
fit!(100.0): fit content, but don't shrink below 100pxgrow!(100.0, 500.0): grow within 100-500px range
🔗Layout
The .layout() closure configures how children are arranged:
.layout(|l| l
.direction(LeftToRight) // or TopToBottom (default)
.gap(16) // pixels between children
.padding(12) // inner padding on all sides
.align(CenterX, CenterY) // child alignment
)🔗Direction
| Value | Behavior |
|---|---|
LeftToRight | Children flow horizontally (default) |
TopToBottom | Children stack vertically |
🔗Alignment
Alignment has two axes:
| X axis | Y axis |
|---|---|
Left | Top |
CenterX | CenterY |
Right | Bottom |
The alignment positions children within the available space.
🔗Padding
Padding adds space inside the element, between its border and its children:
// Same on all sides
.layout(|l| l.padding(16))
// Per-side: (top, right, bottom, left), CSS order
.layout(|l| l.padding((12, 16, 12, 16)))🔗Background color
.background_color(0xFF654D) // hex integer
.background_color((1.0, 0.4, 0.3)) // RGB floats
.background_color((1.0, 0.4, 0.3, 1.0)) // RGBA floats🔗Corner radius
Round the corners with a single value or per-corner tuple:
// All corners: 12px
.corner_radius(12.0)
// Per-corner: (top-left, top-right, bottom-right, bottom-left), CSS order
.corner_radius((12.0, 12.0, 0.0, 0.0))🔗Borders
Configure per-side border widths, color, and child dividers:
.border(|b| b
.all(2) // 2px on all sides
.color(0x4A4440) // border color
.between_children(1) // 1px dividers between children
)
Individual sides: .left(2), .right(2), .top(2), .bottom(2).
🔗Overflow
By default, children can overflow their parent. Use .overflow() to control clipping
and scrolling:
// Clip both axes (no scrolling)
.overflow(|o| o.clip())
// Clip horizontal only
.overflow(|o| o.clip_x())
// Scroll vertically (implies clip on that axis)
.overflow(|o| o.scroll_y())
// Scroll both axes
.overflow(|o| o.scroll())
Clipping hides any content that extends beyond the element's bounds. Scrolling enables drag/wheel interaction so the user can pan through the overflow.
🔗Aspect ratio
Lock an element to a specific aspect ratio:
.aspect_ratio(16.0 / 9.0)
The engine fills in whichever dimension is unset. If both height and width are explicitly sized, aspect ratio has no effect.
🔗Passing Ui into functions
Pass Ui to functions for modular UI:
fn nav_item(ui: &mut Ui, label: &str, active: bool) {
let bg = if active { 0x3A3533 } else { 0x262220 };
ui.element()
.width(grow!())
.height(fixed!(36.0))
.background_color(bg)
.corner_radius(6.0)
.layout(|l| l.padding(8).align(Left, CenterY))
.children(|ui| {
ui.text(label, |t| t.font_size(14).color(0xE8E0DC));
});
}
Then call it wherever you need it: