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
|
||||
groups can still use multiphase animation when another group falls back to
|
||||
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:
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,104 @@ pub struct MultiphaseWindow {
|
|||
pub node_id: NodeId,
|
||||
pub from: 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)]
|
||||
|
|
@ -519,6 +617,7 @@ fn reverse_request(request: &MultiphaseRequest) -> MultiphaseRequest {
|
|||
node_id: window.node_id,
|
||||
from: window.to,
|
||||
to: window.from,
|
||||
hierarchy: window.hierarchy.reversed(),
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
|
|
@ -628,6 +727,10 @@ mod tests {
|
|||
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 {
|
||||
let bounds = windows
|
||||
.iter()
|
||||
|
|
@ -653,15 +756,12 @@ mod tests {
|
|||
#[test]
|
||||
fn horizontal_swap_shrinks_moves_then_grows_without_overlap() {
|
||||
let req = request(vec![
|
||||
MultiphaseWindow {
|
||||
node_id: id(1),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(100, 0, 200, 100),
|
||||
},
|
||||
window(1, rect(0, 0, 100, 100), rect(100, 0, 200, 100)),
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(100, 0, 200, 100),
|
||||
to: rect(0, 0, 100, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = plan_no_overlap(&req).unwrap();
|
||||
|
|
@ -694,11 +794,13 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(100, 0, 200, 100),
|
||||
to: rect(0, 0, 100, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(100, 0, 200, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = plan_no_overlap(&req).unwrap();
|
||||
|
|
@ -714,11 +816,13 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(100, 0, 200, 100),
|
||||
to: rect(0, 0, 100, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(100, 0, 200, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = plan_no_overlap(&req).unwrap();
|
||||
|
|
@ -734,11 +838,13 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 100, 100, 200),
|
||||
to: rect(0, 0, 100, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(0, 100, 100, 200),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = plan_no_overlap(&req).unwrap();
|
||||
|
|
@ -754,16 +860,19 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 200, 100),
|
||||
to: rect(0, 0, 100, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(200, 0, 400, 50),
|
||||
to: rect(100, 0, 300, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(3),
|
||||
from: rect(200, 50, 400, 100),
|
||||
to: rect(300, 0, 400, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = plan_no_overlap(&req).unwrap();
|
||||
|
|
@ -799,16 +908,19 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(0, 0, 200, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(100, 0, 300, 100),
|
||||
to: rect(200, 0, 400, 50),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(3),
|
||||
from: rect(300, 0, 400, 100),
|
||||
to: rect(200, 50, 400, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = plan_no_overlap(&req).unwrap();
|
||||
|
|
@ -839,16 +951,19 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 100, 200),
|
||||
to: rect(0, 0, 100, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(0, 200, 50, 400),
|
||||
to: rect(0, 100, 100, 300),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(3),
|
||||
from: rect(50, 200, 100, 400),
|
||||
to: rect(0, 300, 100, 400),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = plan_no_overlap(&req).unwrap();
|
||||
|
|
@ -884,16 +999,19 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(0, 0, 100, 200),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(0, 100, 100, 300),
|
||||
to: rect(0, 200, 50, 400),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(3),
|
||||
from: rect(0, 300, 100, 400),
|
||||
to: rect(50, 200, 100, 400),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = plan_no_overlap(&req).unwrap();
|
||||
|
|
@ -923,10 +1041,50 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(100, 100, 200, 200),
|
||||
hierarchy: Default::default(),
|
||||
}]);
|
||||
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]
|
||||
fn continuous_validation_rejects_narrow_mid_phase_overlap() {
|
||||
let req = request(vec![
|
||||
|
|
@ -934,11 +1092,13 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 10, 10),
|
||||
to: rect(100, 0, 110, 10),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(13, 0, 14, 10),
|
||||
to: rect(13, 0, 14, 10),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = MultiphasePlan {
|
||||
|
|
@ -965,11 +1125,13 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 10, 10),
|
||||
to: rect(10, 0, 20, 10),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(20, 0, 30, 10),
|
||||
to: rect(20, 0, 30, 10),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
]);
|
||||
let plan = MultiphasePlan {
|
||||
|
|
@ -995,6 +1157,7 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 10, 10),
|
||||
to: rect(20, 0, 30, 10),
|
||||
hierarchy: Default::default(),
|
||||
}]);
|
||||
let plan = MultiphasePlan {
|
||||
phases: vec![MultiphasePhase {
|
||||
|
|
@ -1020,16 +1183,19 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(100, 0, 200, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(100, 0, 200, 100),
|
||||
to: rect(0, 0, 100, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(3),
|
||||
from: rect(300, 0, 400, 100),
|
||||
to: rect(400, 0, 500, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
];
|
||||
assert_eq!(partition_motion_groups(&windows), vec![vec![0, 1], vec![2]]);
|
||||
|
|
@ -1042,16 +1208,19 @@ mod tests {
|
|||
node_id: id(1),
|
||||
from: rect(0, 0, 100, 100),
|
||||
to: rect(80, 0, 180, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(2),
|
||||
from: rect(170, 0, 270, 100),
|
||||
to: rect(250, 0, 350, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
MultiphaseWindow {
|
||||
node_id: id(3),
|
||||
from: rect(90, 0, 180, 100),
|
||||
to: rect(180, 0, 260, 100),
|
||||
hierarchy: Default::default(),
|
||||
},
|
||||
];
|
||||
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,
|
||||
expand_damage_rect,
|
||||
multiphase::{
|
||||
MultiphaseRequest, MultiphaseWindow, partition_motion_groups, plan_no_overlap,
|
||||
MultiphaseRequest, MultiphaseWindow, MultiphaseWindowHierarchy,
|
||||
partition_motion_groups, plan_no_overlap,
|
||||
},
|
||||
spawn_in_start_rect,
|
||||
},
|
||||
|
|
@ -168,6 +169,7 @@ pub(crate) struct LayoutAnimationCandidate {
|
|||
new: Rect,
|
||||
retained: Option<Rc<RetainedToplevel>>,
|
||||
curve: AnimationCurve,
|
||||
hierarchy: MultiphaseWindowHierarchy,
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
|
|
@ -1500,7 +1502,29 @@ impl State {
|
|||
.layout_animation_curve_override
|
||||
.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(
|
||||
|
|
@ -1510,7 +1534,14 @@ impl State {
|
|||
new: Rect,
|
||||
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(
|
||||
|
|
@ -1520,6 +1551,7 @@ impl State {
|
|||
new: Rect,
|
||||
retained: Option<Rc<RetainedToplevel>>,
|
||||
curve: AnimationCurve,
|
||||
hierarchy: MultiphaseWindowHierarchy,
|
||||
) {
|
||||
if !self.animations.enabled.get()
|
||||
|| !self.layout_animations_active.get()
|
||||
|
|
@ -1546,6 +1578,7 @@ impl State {
|
|||
new,
|
||||
retained,
|
||||
curve,
|
||||
hierarchy,
|
||||
};
|
||||
if let Some(batch) = self.layout_animation_batch.borrow_mut().as_mut() {
|
||||
batch.push(candidate);
|
||||
|
|
@ -1590,12 +1623,14 @@ impl State {
|
|||
let now = self.now_nsec();
|
||||
let windows: Vec<_> = candidates
|
||||
.iter()
|
||||
.map(|candidate| MultiphaseWindow {
|
||||
node_id: candidate.node_id,
|
||||
from: self
|
||||
.animations
|
||||
.visual_rect(candidate.node_id, candidate.old, now),
|
||||
to: candidate.new,
|
||||
.map(|candidate| {
|
||||
MultiphaseWindow::with_hierarchy(
|
||||
candidate.node_id,
|
||||
self.animations
|
||||
.visual_rect(candidate.node_id, candidate.old, now),
|
||||
candidate.new,
|
||||
candidate.hierarchy,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
for group in partition_motion_groups(&windows) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
use {
|
||||
crate::{
|
||||
animation::{RetainedExitLayer, RetainedToplevel},
|
||||
animation::{
|
||||
RetainedExitLayer, RetainedToplevel,
|
||||
multiphase::{MultiphaseHierarchyPosition, MultiphaseWindowHierarchy, PhaseAxis},
|
||||
},
|
||||
client::{Client, ClientId},
|
||||
criteria::{
|
||||
CritDestroyListener, CritMatcherId,
|
||||
|
|
@ -186,6 +189,11 @@ impl<T: ToplevelNodeBase> ToplevelNode for T {
|
|||
fn tl_change_extents(self: Rc<Self>, rect: &Rect) {
|
||||
let data = self.tl_data();
|
||||
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 parent_is_mono = data
|
||||
.parent
|
||||
|
|
@ -200,11 +208,12 @@ impl<T: ToplevelNodeBase> ToplevelNode for T {
|
|||
&& !self.node_is_container()
|
||||
&& !parent_is_mono
|
||||
{
|
||||
data.state.clone().queue_tiled_animation(
|
||||
data.state.clone().queue_tiled_animation_with_hierarchy(
|
||||
data.node_id,
|
||||
prev,
|
||||
*rect,
|
||||
self.tl_animation_snapshot(),
|
||||
hierarchy,
|
||||
);
|
||||
}
|
||||
if spawn_in_pending
|
||||
|
|
@ -314,6 +323,35 @@ pub trait ToplevelNodeBase: Node {
|
|||
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) {
|
||||
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 placeholder: Rc<PlaceholderNode>,
|
||||
pub workspace: Rc<WorkspaceNode>,
|
||||
|
|
@ -453,6 +503,7 @@ pub struct ToplevelData {
|
|||
pub spawn_in_pending: Cell<bool>,
|
||||
pub pos: Cell<Rect>,
|
||||
pub desired_extents: Cell<Rect>,
|
||||
pub layout_animation_position: Cell<MultiphaseHierarchyPosition>,
|
||||
pub seat_state: NodeSeatState,
|
||||
pub wants_attention: Cell<bool>,
|
||||
pub requested_attention: Cell<bool>,
|
||||
|
|
@ -517,6 +568,7 @@ impl ToplevelData {
|
|||
spawn_in_pending: Cell::new(false),
|
||||
pos: Default::default(),
|
||||
desired_extents: Default::default(),
|
||||
layout_animation_position: Default::default(),
|
||||
seat_state: Default::default(),
|
||||
wants_attention: Cell::new(false),
|
||||
requested_attention: Cell::new(false),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue