Nested Transforms
Transforms compose through the widget hierarchy. A child inherits and builds upon its parent’s transform.
How Nesting Works
When a parent has a transform, children are affected:
extern crate guido;
use guido::prelude::*;
fn main() {
container()
.rotate(20.0) // Parent rotated
.child(
container()
.scale(0.8) // Child scaled within rotated parent
.child(text("Nested"))
)
;
}
The child appears both rotated (from parent) and scaled (its own).
Transform Order
Parent transforms apply first, then child transforms:
World Space
↓
Parent Transform (rotate 20°)
↓
Child Space (already rotated)
↓
Child Transform (scale 0.8)
↓
Final Position
Example: Rotated Cards
extern crate guido;
use guido::prelude::*;
fn card(_label: &str) -> Container { container() }
fn main() {
container()
.rotate(15.0) // Tilt the whole group
.layout(Flex::row().spacing(10.0))
.children([
// Each card inherits the rotation
card("One"),
card("Two"),
card("Three"),
])
;
}
All cards appear tilted by 15°.
Example: Scaled Child with Rotation
extern crate guido;
use guido::prelude::*;
fn main() {
container()
.width(100.0)
.height(100.0)
.background(Color::rgb(0.3, 0.3, 0.4))
.rotate(30.0)
.child(
container()
.width(50.0)
.height(50.0)
.background(Color::rgb(0.5, 0.7, 0.9))
.scale(1.2) // Child is 20% larger within rotated parent
)
;
}
Hit Testing with Nested Transforms
Guido properly handles hit testing through nested transforms. A click on a nested, transformed element correctly detects the widget.
extern crate guido;
use guido::prelude::*;
fn main() {
container()
.rotate(45.0)
.child(
container()
.scale(0.8)
.on_click(|| println!("Clicked!")) // Works correctly
)
;
}
Transform Independence
Each container has its own transform that doesn’t affect siblings:
// not compiled: `...` stands in for each child; this block is about the
// transforms beside them.
extern crate guido;
use guido::prelude::*;
fn main() {
container()
.layout(Flex::row().spacing(20.0))
.children([
// Each has independent transform
container().rotate(15.0).child(...),
container().rotate(-15.0).child(...),
container().scale(0.9).child(...),
])
;
}
Complete Example
extern crate guido;
use guido::prelude::*;
fn nested_transforms_demo() -> impl Widget {
container()
.padding(40.0)
.child(
// Outer container with rotation
container()
.width(200.0)
.height(200.0)
.background(Color::rgb(0.2, 0.2, 0.3))
.corners(16.0)
.rotate(10.0)
.layout(Flex::column().spacing(16.0).center())
.children([
container().child(text("Parent (rotated 10°)").font_size(12.0).color(Color::WHITE)),
// Inner container with scale
container()
.width(120.0)
.height(80.0)
.background(Color::rgb(0.3, 0.5, 0.8))
.corners(8.0)
.scale(0.9)
.when_hovered(|s| s.lighter(0.1))
.when_pressed(|s| s.ripple())
.layout(Flex::column().center())
.child(
container().child(text("Child (scaled 0.9)").font_size(10.0).color(Color::WHITE))
),
])
)
}
fn main() {}
Caveats
Transform Origin Applies Locally
A child’s transform origin is relative to its own bounds, not the parent’s:
extern crate guido;
use guido::prelude::*;
fn main() {
container()
.rotate(45.0) // Rotates around its own center
.child(
container()
.rotate(30.0)
.pivot(Pivot::TOP_LEFT) // Relative to child's top-left
)
;
}
Performance
Deep nesting with many transforms is fine for typical UIs. The transform matrices multiply efficiently.
Tips
- Keep it simple - Deep transform nesting can be hard to reason about
- Use for grouping - Apply a transform to a parent to affect all children
- Independent animations - Each level can have its own animated transform
// not compiled: `...` stands in for each child; this block is about the
// animations beside them.
extern crate guido;
use guido::prelude::*;
fn main() {
// Group animation
container()
.rotate(group_rotation.transition(Transition::new(300.0, TimingFunction::EaseOut)))
.children([
// Individual children can have their own animations
container()
.scale(child_scale.transition(Transition::spring(SpringConfig::BOUNCY)))
.child(...),
])
;
}