Circular Dependencies in Makepad Live DSL Layout System
Understanding Layout Circular Dependencies
Layout circular dependencies occur when components' size calculations create mutual dependencies, preventing the layout engine from determining final dimensions. This typically happens in these scenarios:
- Parent-child component size interdependencies
- Sibling component size interdependencies
- Complex Fit layout nesting
Let's understand these situations through specific examples:
Parent-Child Circular Dependency
1// Incorrect example: Parent-child circular dependency
2ParentView = <View> {
3 walk: {
4 width: Fill,
5 height: Fit // Parent height depends on child
6 },
7 layout: {
8 flow: Down
9 },
10
11 <ChildView> {
12 walk: {
13 width: Fill,
14 height: Percentage(0.5) // Child height depends on parent
15 }
16 }
17}
In this example:
- Parent component's height is set to
Fit
, waiting for child component height calculation
- Child component's height is set to 50% of parent height, waiting for parent height calculation
- Creates a cycle: parent waiting for child, child waiting for parent
Parent-child circular dependency diagram
1┌─ Parent (height: Fit) ─┐
2│ ? │
3│ ┌─ Child ─┐ │
4│ │ height:│ │
5│ │ 50% │ ? │
6│ │ of ? │ │
7│ └─────────┘ │
8└────────────────────────┘
Sibling Component Circular Dependency
1// Incorrect example: Sibling component circular dependency
2Container = <View> {
3 layout: {
4 flow: Right
5 },
6
7 <LeftView> {
8 walk: {
9 width: Fill, // Left component fills remaining space
10 height: Fit
11 }
12 }
13
14 <RightView> {
15 walk: {
16 width: Fit, // Right component width adapts to content
17 height: Fit
18 },
19 layout: {
20 flow: Down
21 }
22 }
23}
In this example:
- Left component wants to fill space excluding right component
- Right component's width depends on its content
- Layout engine cannot determine which component to calculate first
How to Avoid Circular Dependencies
1. Explicit Size Constraints
The safest approach is to provide explicit constraints for critical dimensions:
1// Correct example: Explicit size constraints
2ParentView = <View> {
3 walk: {
4 width: Fill,
5 height: Fixed(500) // Explicit parent height
6 },
7
8 <ChildView> {
9 walk: {
10 width: Fill,
11 height: Percentage(0.5) // Now calculatable
12 }
13 }
14}
2. Using Fixed or Max/Min Constraints
1Container = <View> {
2 layout: {
3 flow: Right
4 },
5
6 <LeftView> {
7 walk: {
8 width: Percentage(0.7), // Fixed ratio
9 height: Fit
10 }
11 }
12
13 <RightView> {
14 walk: {
15 width: Fixed(200), // Fixed width
16 height: Fit
17 }
18 }
19}
3. Layout Directional Principles
Establishing correct layout direction can avoid many circular dependencies:
1// Recommended layout direction
2GoodLayout = <View> {
3 layout: {
4 flow: Down // Vertical layout makes size calculation more straightforward
5 },
6
7 <Header> {
8 walk: {
9 width: Fill,
10 height: Fixed(60) // Fixed height header
11 }
12 }
13
14 <Content> {
15 walk: {
16 width: Fill,
17 height: Fill // Fill remaining space
18 }
19 }
20
21 <Footer> {
22 walk: {
23 width: Fill,
24 height: Fit // Footer height adapts to content
25 }
26 }
27}
Layout Engine's Resolution Strategy
When encountering potential circular dependencies, the Makepad layout engine employs these strategies:
1. Priority Order
- Fixed sizes calculate first
- Fill calculations second
- Fit calculations last
2. Breaking Cycles
- Uses default or minimum values for unresolvable cycles
- Issues warnings in development mode
3. Caching Mechanism
- Caches calculated layout results
- Avoids recalculating identical layouts