Carry hierarchy metadata into multiphase planning
This commit is contained in:
parent
a712786ecf
commit
90c00bcdf3
4 changed files with 276 additions and 16 deletions
|
|
@ -229,6 +229,10 @@ Current pure planner status:
|
||||||
- Live layout batches are partitioned by overlapping motion bounds, so unrelated
|
- Live layout batches are partitioned by overlapping motion bounds, so unrelated
|
||||||
groups can still use multiphase animation when another group falls back to
|
groups can still use multiphase animation when another group falls back to
|
||||||
linear motion.
|
linear motion.
|
||||||
|
- Planner requests now carry per-window hierarchy metadata for source/target
|
||||||
|
parent, depth, sibling index, split axis, mono state, and transition kind.
|
||||||
|
The current planner records this information but does not yet use it to order
|
||||||
|
nested-container phases.
|
||||||
|
|
||||||
Tests:
|
Tests:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,104 @@ pub struct MultiphaseWindow {
|
||||||
pub node_id: NodeId,
|
pub node_id: NodeId,
|
||||||
pub from: Rect,
|
pub from: Rect,
|
||||||
pub to: Rect,
|
pub to: Rect,
|
||||||
|
pub hierarchy: MultiphaseWindowHierarchy,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MultiphaseWindow {
|
||||||
|
pub fn new(node_id: NodeId, from: Rect, to: Rect) -> Self {
|
||||||
|
Self {
|
||||||
|
node_id,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
hierarchy: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_hierarchy(
|
||||||
|
node_id: NodeId,
|
||||||
|
from: Rect,
|
||||||
|
to: Rect,
|
||||||
|
hierarchy: MultiphaseWindowHierarchy,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
node_id,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
hierarchy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct MultiphaseWindowHierarchy {
|
||||||
|
pub source: MultiphaseHierarchyPosition,
|
||||||
|
pub target: MultiphaseHierarchyPosition,
|
||||||
|
pub transition: MultiphaseHierarchyTransition,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MultiphaseWindowHierarchy {
|
||||||
|
pub fn new(source: MultiphaseHierarchyPosition, target: MultiphaseHierarchyPosition) -> Self {
|
||||||
|
let transition = if !source.parent_is_mono && target.parent_is_mono {
|
||||||
|
MultiphaseHierarchyTransition::EnteringMono
|
||||||
|
} else if source.parent_is_mono && !target.parent_is_mono {
|
||||||
|
MultiphaseHierarchyTransition::ExitingMono
|
||||||
|
} else if source.parent.is_none() || target.parent.is_none() {
|
||||||
|
MultiphaseHierarchyTransition::Unknown
|
||||||
|
} else if target.depth < source.depth {
|
||||||
|
MultiphaseHierarchyTransition::Ascending
|
||||||
|
} else if target.depth > source.depth {
|
||||||
|
MultiphaseHierarchyTransition::Descending
|
||||||
|
} else {
|
||||||
|
MultiphaseHierarchyTransition::SameLevel
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
source,
|
||||||
|
target,
|
||||||
|
transition,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reversed(self) -> Self {
|
||||||
|
Self {
|
||||||
|
source: self.target,
|
||||||
|
target: self.source,
|
||||||
|
transition: self.transition.reversed(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct MultiphaseHierarchyPosition {
|
||||||
|
pub parent: Option<NodeId>,
|
||||||
|
pub depth: u16,
|
||||||
|
pub sibling_index: Option<u16>,
|
||||||
|
pub split_axis: Option<PhaseAxis>,
|
||||||
|
pub parent_is_mono: bool,
|
||||||
|
pub mono_active: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
pub enum MultiphaseHierarchyTransition {
|
||||||
|
#[default]
|
||||||
|
Unknown,
|
||||||
|
SameLevel,
|
||||||
|
Ascending,
|
||||||
|
Descending,
|
||||||
|
EnteringMono,
|
||||||
|
ExitingMono,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MultiphaseHierarchyTransition {
|
||||||
|
fn reversed(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Unknown => Self::Unknown,
|
||||||
|
Self::SameLevel => Self::SameLevel,
|
||||||
|
Self::Ascending => Self::Descending,
|
||||||
|
Self::Descending => Self::Ascending,
|
||||||
|
Self::EnteringMono => Self::ExitingMono,
|
||||||
|
Self::ExitingMono => Self::EnteringMono,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
|
@ -519,6 +617,7 @@ fn reverse_request(request: &MultiphaseRequest) -> MultiphaseRequest {
|
||||||
node_id: window.node_id,
|
node_id: window.node_id,
|
||||||
from: window.to,
|
from: window.to,
|
||||||
to: window.from,
|
to: window.from,
|
||||||
|
hierarchy: window.hierarchy.reversed(),
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
|
|
@ -628,6 +727,10 @@ mod tests {
|
||||||
Rect::new_saturating(x1, y1, x2, y2)
|
Rect::new_saturating(x1, y1, x2, y2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn window(raw: u32, from: Rect, to: Rect) -> MultiphaseWindow {
|
||||||
|
MultiphaseWindow::new(id(raw), from, to)
|
||||||
|
}
|
||||||
|
|
||||||
fn request(windows: Vec<MultiphaseWindow>) -> MultiphaseRequest {
|
fn request(windows: Vec<MultiphaseWindow>) -> MultiphaseRequest {
|
||||||
let bounds = windows
|
let bounds = windows
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -653,15 +756,12 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn horizontal_swap_shrinks_moves_then_grows_without_overlap() {
|
fn horizontal_swap_shrinks_moves_then_grows_without_overlap() {
|
||||||
let req = request(vec![
|
let req = request(vec![
|
||||||
MultiphaseWindow {
|
window(1, rect(0, 0, 100, 100), rect(100, 0, 200, 100)),
|
||||||
node_id: id(1),
|
|
||||||
from: rect(0, 0, 100, 100),
|
|
||||||
to: rect(100, 0, 200, 100),
|
|
||||||
},
|
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(2),
|
node_id: id(2),
|
||||||
from: rect(100, 0, 200, 100),
|
from: rect(100, 0, 200, 100),
|
||||||
to: rect(0, 0, 100, 100),
|
to: rect(0, 0, 100, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
let plan = plan_no_overlap(&req).unwrap();
|
let plan = plan_no_overlap(&req).unwrap();
|
||||||
|
|
@ -694,11 +794,13 @@ mod tests {
|
||||||
node_id: id(1),
|
node_id: id(1),
|
||||||
from: rect(100, 0, 200, 100),
|
from: rect(100, 0, 200, 100),
|
||||||
to: rect(0, 0, 100, 100),
|
to: rect(0, 0, 100, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(2),
|
node_id: id(2),
|
||||||
from: rect(0, 0, 100, 100),
|
from: rect(0, 0, 100, 100),
|
||||||
to: rect(100, 0, 200, 100),
|
to: rect(100, 0, 200, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
let plan = plan_no_overlap(&req).unwrap();
|
let plan = plan_no_overlap(&req).unwrap();
|
||||||
|
|
@ -714,11 +816,13 @@ mod tests {
|
||||||
node_id: id(1),
|
node_id: id(1),
|
||||||
from: rect(100, 0, 200, 100),
|
from: rect(100, 0, 200, 100),
|
||||||
to: rect(0, 0, 100, 100),
|
to: rect(0, 0, 100, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(2),
|
node_id: id(2),
|
||||||
from: rect(0, 0, 100, 100),
|
from: rect(0, 0, 100, 100),
|
||||||
to: rect(100, 0, 200, 100),
|
to: rect(100, 0, 200, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
let plan = plan_no_overlap(&req).unwrap();
|
let plan = plan_no_overlap(&req).unwrap();
|
||||||
|
|
@ -734,11 +838,13 @@ mod tests {
|
||||||
node_id: id(1),
|
node_id: id(1),
|
||||||
from: rect(0, 100, 100, 200),
|
from: rect(0, 100, 100, 200),
|
||||||
to: rect(0, 0, 100, 100),
|
to: rect(0, 0, 100, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(2),
|
node_id: id(2),
|
||||||
from: rect(0, 0, 100, 100),
|
from: rect(0, 0, 100, 100),
|
||||||
to: rect(0, 100, 100, 200),
|
to: rect(0, 100, 100, 200),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
let plan = plan_no_overlap(&req).unwrap();
|
let plan = plan_no_overlap(&req).unwrap();
|
||||||
|
|
@ -754,16 +860,19 @@ mod tests {
|
||||||
node_id: id(1),
|
node_id: id(1),
|
||||||
from: rect(0, 0, 200, 100),
|
from: rect(0, 0, 200, 100),
|
||||||
to: rect(0, 0, 100, 100),
|
to: rect(0, 0, 100, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(2),
|
node_id: id(2),
|
||||||
from: rect(200, 0, 400, 50),
|
from: rect(200, 0, 400, 50),
|
||||||
to: rect(100, 0, 300, 100),
|
to: rect(100, 0, 300, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(3),
|
node_id: id(3),
|
||||||
from: rect(200, 50, 400, 100),
|
from: rect(200, 50, 400, 100),
|
||||||
to: rect(300, 0, 400, 100),
|
to: rect(300, 0, 400, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
let plan = plan_no_overlap(&req).unwrap();
|
let plan = plan_no_overlap(&req).unwrap();
|
||||||
|
|
@ -799,16 +908,19 @@ mod tests {
|
||||||
node_id: id(1),
|
node_id: id(1),
|
||||||
from: rect(0, 0, 100, 100),
|
from: rect(0, 0, 100, 100),
|
||||||
to: rect(0, 0, 200, 100),
|
to: rect(0, 0, 200, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(2),
|
node_id: id(2),
|
||||||
from: rect(100, 0, 300, 100),
|
from: rect(100, 0, 300, 100),
|
||||||
to: rect(200, 0, 400, 50),
|
to: rect(200, 0, 400, 50),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(3),
|
node_id: id(3),
|
||||||
from: rect(300, 0, 400, 100),
|
from: rect(300, 0, 400, 100),
|
||||||
to: rect(200, 50, 400, 100),
|
to: rect(200, 50, 400, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
let plan = plan_no_overlap(&req).unwrap();
|
let plan = plan_no_overlap(&req).unwrap();
|
||||||
|
|
@ -839,16 +951,19 @@ mod tests {
|
||||||
node_id: id(1),
|
node_id: id(1),
|
||||||
from: rect(0, 0, 100, 200),
|
from: rect(0, 0, 100, 200),
|
||||||
to: rect(0, 0, 100, 100),
|
to: rect(0, 0, 100, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(2),
|
node_id: id(2),
|
||||||
from: rect(0, 200, 50, 400),
|
from: rect(0, 200, 50, 400),
|
||||||
to: rect(0, 100, 100, 300),
|
to: rect(0, 100, 100, 300),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(3),
|
node_id: id(3),
|
||||||
from: rect(50, 200, 100, 400),
|
from: rect(50, 200, 100, 400),
|
||||||
to: rect(0, 300, 100, 400),
|
to: rect(0, 300, 100, 400),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
let plan = plan_no_overlap(&req).unwrap();
|
let plan = plan_no_overlap(&req).unwrap();
|
||||||
|
|
@ -884,16 +999,19 @@ mod tests {
|
||||||
node_id: id(1),
|
node_id: id(1),
|
||||||
from: rect(0, 0, 100, 100),
|
from: rect(0, 0, 100, 100),
|
||||||
to: rect(0, 0, 100, 200),
|
to: rect(0, 0, 100, 200),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(2),
|
node_id: id(2),
|
||||||
from: rect(0, 100, 100, 300),
|
from: rect(0, 100, 100, 300),
|
||||||
to: rect(0, 200, 50, 400),
|
to: rect(0, 200, 50, 400),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(3),
|
node_id: id(3),
|
||||||
from: rect(0, 300, 100, 400),
|
from: rect(0, 300, 100, 400),
|
||||||
to: rect(50, 200, 100, 400),
|
to: rect(50, 200, 100, 400),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
let plan = plan_no_overlap(&req).unwrap();
|
let plan = plan_no_overlap(&req).unwrap();
|
||||||
|
|
@ -923,10 +1041,50 @@ mod tests {
|
||||||
node_id: id(1),
|
node_id: id(1),
|
||||||
from: rect(0, 0, 100, 100),
|
from: rect(0, 0, 100, 100),
|
||||||
to: rect(100, 100, 200, 200),
|
to: rect(100, 100, 200, 200),
|
||||||
|
hierarchy: Default::default(),
|
||||||
}]);
|
}]);
|
||||||
assert_eq!(plan_no_overlap(&req), Err(MultiphaseError::NoPlan));
|
assert_eq!(plan_no_overlap(&req), Err(MultiphaseError::NoPlan));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hierarchy_metadata_classifies_depth_and_mono_transitions() {
|
||||||
|
let source = MultiphaseHierarchyPosition {
|
||||||
|
parent: Some(id(10)),
|
||||||
|
depth: 2,
|
||||||
|
sibling_index: Some(0),
|
||||||
|
split_axis: Some(PhaseAxis::Vertical),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let target = MultiphaseHierarchyPosition {
|
||||||
|
parent: Some(id(11)),
|
||||||
|
depth: 1,
|
||||||
|
sibling_index: Some(2),
|
||||||
|
split_axis: Some(PhaseAxis::Horizontal),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
MultiphaseWindowHierarchy::new(source, target).transition,
|
||||||
|
MultiphaseHierarchyTransition::Ascending
|
||||||
|
);
|
||||||
|
|
||||||
|
let entering_mono = MultiphaseWindowHierarchy::new(
|
||||||
|
source,
|
||||||
|
MultiphaseHierarchyPosition {
|
||||||
|
parent_is_mono: true,
|
||||||
|
mono_active: true,
|
||||||
|
..target
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
entering_mono.transition,
|
||||||
|
MultiphaseHierarchyTransition::EnteringMono
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
entering_mono.reversed().transition,
|
||||||
|
MultiphaseHierarchyTransition::ExitingMono
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn continuous_validation_rejects_narrow_mid_phase_overlap() {
|
fn continuous_validation_rejects_narrow_mid_phase_overlap() {
|
||||||
let req = request(vec![
|
let req = request(vec![
|
||||||
|
|
@ -934,11 +1092,13 @@ mod tests {
|
||||||
node_id: id(1),
|
node_id: id(1),
|
||||||
from: rect(0, 0, 10, 10),
|
from: rect(0, 0, 10, 10),
|
||||||
to: rect(100, 0, 110, 10),
|
to: rect(100, 0, 110, 10),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(2),
|
node_id: id(2),
|
||||||
from: rect(13, 0, 14, 10),
|
from: rect(13, 0, 14, 10),
|
||||||
to: rect(13, 0, 14, 10),
|
to: rect(13, 0, 14, 10),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
let plan = MultiphasePlan {
|
let plan = MultiphasePlan {
|
||||||
|
|
@ -965,11 +1125,13 @@ mod tests {
|
||||||
node_id: id(1),
|
node_id: id(1),
|
||||||
from: rect(0, 0, 10, 10),
|
from: rect(0, 0, 10, 10),
|
||||||
to: rect(10, 0, 20, 10),
|
to: rect(10, 0, 20, 10),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(2),
|
node_id: id(2),
|
||||||
from: rect(20, 0, 30, 10),
|
from: rect(20, 0, 30, 10),
|
||||||
to: rect(20, 0, 30, 10),
|
to: rect(20, 0, 30, 10),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
let plan = MultiphasePlan {
|
let plan = MultiphasePlan {
|
||||||
|
|
@ -995,6 +1157,7 @@ mod tests {
|
||||||
node_id: id(1),
|
node_id: id(1),
|
||||||
from: rect(0, 0, 10, 10),
|
from: rect(0, 0, 10, 10),
|
||||||
to: rect(20, 0, 30, 10),
|
to: rect(20, 0, 30, 10),
|
||||||
|
hierarchy: Default::default(),
|
||||||
}]);
|
}]);
|
||||||
let plan = MultiphasePlan {
|
let plan = MultiphasePlan {
|
||||||
phases: vec![MultiphasePhase {
|
phases: vec![MultiphasePhase {
|
||||||
|
|
@ -1020,16 +1183,19 @@ mod tests {
|
||||||
node_id: id(1),
|
node_id: id(1),
|
||||||
from: rect(0, 0, 100, 100),
|
from: rect(0, 0, 100, 100),
|
||||||
to: rect(100, 0, 200, 100),
|
to: rect(100, 0, 200, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(2),
|
node_id: id(2),
|
||||||
from: rect(100, 0, 200, 100),
|
from: rect(100, 0, 200, 100),
|
||||||
to: rect(0, 0, 100, 100),
|
to: rect(0, 0, 100, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(3),
|
node_id: id(3),
|
||||||
from: rect(300, 0, 400, 100),
|
from: rect(300, 0, 400, 100),
|
||||||
to: rect(400, 0, 500, 100),
|
to: rect(400, 0, 500, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
assert_eq!(partition_motion_groups(&windows), vec![vec![0, 1], vec![2]]);
|
assert_eq!(partition_motion_groups(&windows), vec![vec![0, 1], vec![2]]);
|
||||||
|
|
@ -1042,16 +1208,19 @@ mod tests {
|
||||||
node_id: id(1),
|
node_id: id(1),
|
||||||
from: rect(0, 0, 100, 100),
|
from: rect(0, 0, 100, 100),
|
||||||
to: rect(80, 0, 180, 100),
|
to: rect(80, 0, 180, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(2),
|
node_id: id(2),
|
||||||
from: rect(170, 0, 270, 100),
|
from: rect(170, 0, 270, 100),
|
||||||
to: rect(250, 0, 350, 100),
|
to: rect(250, 0, 350, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
MultiphaseWindow {
|
MultiphaseWindow {
|
||||||
node_id: id(3),
|
node_id: id(3),
|
||||||
from: rect(90, 0, 180, 100),
|
from: rect(90, 0, 180, 100),
|
||||||
to: rect(180, 0, 260, 100),
|
to: rect(180, 0, 260, 100),
|
||||||
|
hierarchy: Default::default(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
assert_eq!(partition_motion_groups(&windows), vec![vec![0, 1, 2]]);
|
assert_eq!(partition_motion_groups(&windows), vec![vec![0, 1, 2]]);
|
||||||
|
|
|
||||||
53
src/state.rs
53
src/state.rs
|
|
@ -6,7 +6,8 @@ use {
|
||||||
AnimationCurve, AnimationState, AnimationTick, RetainedExitLayer, RetainedToplevel,
|
AnimationCurve, AnimationState, AnimationTick, RetainedExitLayer, RetainedToplevel,
|
||||||
expand_damage_rect,
|
expand_damage_rect,
|
||||||
multiphase::{
|
multiphase::{
|
||||||
MultiphaseRequest, MultiphaseWindow, partition_motion_groups, plan_no_overlap,
|
MultiphaseRequest, MultiphaseWindow, MultiphaseWindowHierarchy,
|
||||||
|
partition_motion_groups, plan_no_overlap,
|
||||||
},
|
},
|
||||||
spawn_in_start_rect,
|
spawn_in_start_rect,
|
||||||
},
|
},
|
||||||
|
|
@ -168,6 +169,7 @@ pub(crate) struct LayoutAnimationCandidate {
|
||||||
new: Rect,
|
new: Rect,
|
||||||
retained: Option<Rc<RetainedToplevel>>,
|
retained: Option<Rc<RetainedToplevel>>,
|
||||||
curve: AnimationCurve,
|
curve: AnimationCurve,
|
||||||
|
hierarchy: MultiphaseWindowHierarchy,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
|
@ -1500,7 +1502,29 @@ impl State {
|
||||||
.layout_animation_curve_override
|
.layout_animation_curve_override
|
||||||
.get()
|
.get()
|
||||||
.unwrap_or_else(|| self.animations.curve.get());
|
.unwrap_or_else(|| self.animations.curve.get());
|
||||||
self.queue_layout_animation(node_id, old, new, retained, curve);
|
self.queue_layout_animation(
|
||||||
|
node_id,
|
||||||
|
old,
|
||||||
|
new,
|
||||||
|
retained,
|
||||||
|
curve,
|
||||||
|
MultiphaseWindowHierarchy::default(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn queue_tiled_animation_with_hierarchy(
|
||||||
|
self: &Rc<Self>,
|
||||||
|
node_id: NodeId,
|
||||||
|
old: Rect,
|
||||||
|
new: Rect,
|
||||||
|
retained: Option<Rc<RetainedToplevel>>,
|
||||||
|
hierarchy: MultiphaseWindowHierarchy,
|
||||||
|
) {
|
||||||
|
let curve = self
|
||||||
|
.layout_animation_curve_override
|
||||||
|
.get()
|
||||||
|
.unwrap_or_else(|| self.animations.curve.get());
|
||||||
|
self.queue_layout_animation(node_id, old, new, retained, curve, hierarchy);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn queue_linear_layout_animation(
|
pub fn queue_linear_layout_animation(
|
||||||
|
|
@ -1510,7 +1534,14 @@ impl State {
|
||||||
new: Rect,
|
new: Rect,
|
||||||
retained: Option<Rc<RetainedToplevel>>,
|
retained: Option<Rc<RetainedToplevel>>,
|
||||||
) {
|
) {
|
||||||
self.queue_layout_animation(node_id, old, new, retained, AnimationCurve::Linear);
|
self.queue_layout_animation(
|
||||||
|
node_id,
|
||||||
|
old,
|
||||||
|
new,
|
||||||
|
retained,
|
||||||
|
AnimationCurve::Linear,
|
||||||
|
MultiphaseWindowHierarchy::default(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn queue_layout_animation(
|
fn queue_layout_animation(
|
||||||
|
|
@ -1520,6 +1551,7 @@ impl State {
|
||||||
new: Rect,
|
new: Rect,
|
||||||
retained: Option<Rc<RetainedToplevel>>,
|
retained: Option<Rc<RetainedToplevel>>,
|
||||||
curve: AnimationCurve,
|
curve: AnimationCurve,
|
||||||
|
hierarchy: MultiphaseWindowHierarchy,
|
||||||
) {
|
) {
|
||||||
if !self.animations.enabled.get()
|
if !self.animations.enabled.get()
|
||||||
|| !self.layout_animations_active.get()
|
|| !self.layout_animations_active.get()
|
||||||
|
|
@ -1546,6 +1578,7 @@ impl State {
|
||||||
new,
|
new,
|
||||||
retained,
|
retained,
|
||||||
curve,
|
curve,
|
||||||
|
hierarchy,
|
||||||
};
|
};
|
||||||
if let Some(batch) = self.layout_animation_batch.borrow_mut().as_mut() {
|
if let Some(batch) = self.layout_animation_batch.borrow_mut().as_mut() {
|
||||||
batch.push(candidate);
|
batch.push(candidate);
|
||||||
|
|
@ -1590,12 +1623,14 @@ impl State {
|
||||||
let now = self.now_nsec();
|
let now = self.now_nsec();
|
||||||
let windows: Vec<_> = candidates
|
let windows: Vec<_> = candidates
|
||||||
.iter()
|
.iter()
|
||||||
.map(|candidate| MultiphaseWindow {
|
.map(|candidate| {
|
||||||
node_id: candidate.node_id,
|
MultiphaseWindow::with_hierarchy(
|
||||||
from: self
|
candidate.node_id,
|
||||||
.animations
|
self.animations
|
||||||
.visual_rect(candidate.node_id, candidate.old, now),
|
.visual_rect(candidate.node_id, candidate.old, now),
|
||||||
to: candidate.new,
|
candidate.new,
|
||||||
|
candidate.hierarchy,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
for group in partition_motion_groups(&windows) {
|
for group in partition_motion_groups(&windows) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
animation::{RetainedExitLayer, RetainedToplevel},
|
animation::{
|
||||||
|
RetainedExitLayer, RetainedToplevel,
|
||||||
|
multiphase::{MultiphaseHierarchyPosition, MultiphaseWindowHierarchy, PhaseAxis},
|
||||||
|
},
|
||||||
client::{Client, ClientId},
|
client::{Client, ClientId},
|
||||||
criteria::{
|
criteria::{
|
||||||
CritDestroyListener, CritMatcherId,
|
CritDestroyListener, CritMatcherId,
|
||||||
|
|
@ -186,6 +189,11 @@ impl<T: ToplevelNodeBase> ToplevelNode for T {
|
||||||
fn tl_change_extents(self: Rc<Self>, rect: &Rect) {
|
fn tl_change_extents(self: Rc<Self>, rect: &Rect) {
|
||||||
let data = self.tl_data();
|
let data = self.tl_data();
|
||||||
let prev = data.desired_extents.replace(*rect);
|
let prev = data.desired_extents.replace(*rect);
|
||||||
|
let target_hierarchy = self.tl_multiphase_hierarchy_position();
|
||||||
|
let hierarchy = MultiphaseWindowHierarchy::new(
|
||||||
|
data.layout_animation_position.replace(target_hierarchy),
|
||||||
|
target_hierarchy,
|
||||||
|
);
|
||||||
let spawn_in_pending = data.spawn_in_pending.get();
|
let spawn_in_pending = data.spawn_in_pending.get();
|
||||||
let parent_is_mono = data
|
let parent_is_mono = data
|
||||||
.parent
|
.parent
|
||||||
|
|
@ -200,11 +208,12 @@ impl<T: ToplevelNodeBase> ToplevelNode for T {
|
||||||
&& !self.node_is_container()
|
&& !self.node_is_container()
|
||||||
&& !parent_is_mono
|
&& !parent_is_mono
|
||||||
{
|
{
|
||||||
data.state.clone().queue_tiled_animation(
|
data.state.clone().queue_tiled_animation_with_hierarchy(
|
||||||
data.node_id,
|
data.node_id,
|
||||||
prev,
|
prev,
|
||||||
*rect,
|
*rect,
|
||||||
self.tl_animation_snapshot(),
|
self.tl_animation_snapshot(),
|
||||||
|
hierarchy,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if spawn_in_pending
|
if spawn_in_pending
|
||||||
|
|
@ -314,6 +323,35 @@ pub trait ToplevelNodeBase: Node {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tl_multiphase_hierarchy_position(&self) -> MultiphaseHierarchyPosition {
|
||||||
|
let data = self.tl_data();
|
||||||
|
let Some(parent) = data.parent.get() else {
|
||||||
|
return Default::default();
|
||||||
|
};
|
||||||
|
let mut position = MultiphaseHierarchyPosition {
|
||||||
|
parent: Some(parent.node_id()),
|
||||||
|
depth: multiphase_parent_depth(Some(parent.clone())),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
if let Some(container) = parent.node_into_container() {
|
||||||
|
position.split_axis = Some(match container.split.get() {
|
||||||
|
ContainerSplit::Horizontal => PhaseAxis::Horizontal,
|
||||||
|
ContainerSplit::Vertical => PhaseAxis::Vertical,
|
||||||
|
});
|
||||||
|
if let Some(mono) = container.mono_child.get() {
|
||||||
|
position.parent_is_mono = true;
|
||||||
|
position.mono_active = mono.node.node_id() == data.node_id;
|
||||||
|
}
|
||||||
|
for (idx, child) in container.children.iter().enumerate() {
|
||||||
|
if child.node.node_id() == data.node_id {
|
||||||
|
position.sibling_index = Some(idx.min(u16::MAX as usize) as u16);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
position
|
||||||
|
}
|
||||||
|
|
||||||
fn tl_set_active(&self, active: bool) {
|
fn tl_set_active(&self, active: bool) {
|
||||||
let _ = active;
|
let _ = active;
|
||||||
}
|
}
|
||||||
|
|
@ -383,6 +421,18 @@ pub trait ToplevelNodeBase: Node {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn multiphase_parent_depth(mut parent: Option<Rc<dyn ContainingNode>>) -> u16 {
|
||||||
|
let mut depth = 0u16;
|
||||||
|
while let Some(node) = parent {
|
||||||
|
let Some(toplevel) = node.node_into_toplevel() else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
depth = depth.saturating_add(1);
|
||||||
|
parent = toplevel.tl_data().parent.get();
|
||||||
|
}
|
||||||
|
depth
|
||||||
|
}
|
||||||
|
|
||||||
pub struct FullscreenedData {
|
pub struct FullscreenedData {
|
||||||
pub placeholder: Rc<PlaceholderNode>,
|
pub placeholder: Rc<PlaceholderNode>,
|
||||||
pub workspace: Rc<WorkspaceNode>,
|
pub workspace: Rc<WorkspaceNode>,
|
||||||
|
|
@ -453,6 +503,7 @@ pub struct ToplevelData {
|
||||||
pub spawn_in_pending: Cell<bool>,
|
pub spawn_in_pending: Cell<bool>,
|
||||||
pub pos: Cell<Rect>,
|
pub pos: Cell<Rect>,
|
||||||
pub desired_extents: Cell<Rect>,
|
pub desired_extents: Cell<Rect>,
|
||||||
|
pub layout_animation_position: Cell<MultiphaseHierarchyPosition>,
|
||||||
pub seat_state: NodeSeatState,
|
pub seat_state: NodeSeatState,
|
||||||
pub wants_attention: Cell<bool>,
|
pub wants_attention: Cell<bool>,
|
||||||
pub requested_attention: Cell<bool>,
|
pub requested_attention: Cell<bool>,
|
||||||
|
|
@ -517,6 +568,7 @@ impl ToplevelData {
|
||||||
spawn_in_pending: Cell::new(false),
|
spawn_in_pending: Cell::new(false),
|
||||||
pos: Default::default(),
|
pos: Default::default(),
|
||||||
desired_extents: Default::default(),
|
desired_extents: Default::default(),
|
||||||
|
layout_animation_position: Default::default(),
|
||||||
seat_state: Default::default(),
|
seat_state: Default::default(),
|
||||||
wants_attention: Cell::new(false),
|
wants_attention: Cell::new(false),
|
||||||
requested_attention: Cell::new(false),
|
requested_attention: Cell::new(false),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue