This commit is contained in:
Alejandro Alonso 2026-04-21 10:45:12 +02:00
parent 2a7c8dea42
commit a70272c3f0
2 changed files with 62 additions and 0 deletions

View File

@ -2072,6 +2072,17 @@ impl RenderState {
self.drag_layers.viewport_rect = viewport_rect;
// Sort layers by global render order so the compositing loop paints
// the one visually behind first and the one in front last. Without
// this, a drag over several selected shapes would swap their
// stacking whenever the frontend sent `ids` in a different order.
if self.drag_layers.layers.len() > 1 {
let order = tree.render_order_indices(&selected_set);
self.drag_layers
.layers
.sort_by_key(|layer| order.get(&layer.shape_id).copied().unwrap_or(usize::MAX));
}
// Restore the workspace render state exactly like `render_shape_pixels`
// does. Without this the next workspace render could observe stale
// render_area / focus / pending_nodes left over from our snapshots.

View File

@ -267,6 +267,57 @@ impl ShapesPoolImpl {
above
}
/// Walk the tree in render order (DFS pre-order, paint-order among
/// siblings: first child = behind, last child = in front) and assign a
/// monotonically increasing index to every uuid in `targets`. The map
/// returned only contains entries for shapes that actually appear in
/// the traversal; unknown or detached ids are skipped.
///
/// Used by the drag-layer pipeline to blit the per-shape snapshots in
/// the same stacking order the workspace would have used, regardless
/// of the order in which the frontend sent the selection ids.
pub fn render_order_indices(&self, targets: &HashSet<Uuid>) -> HashMap<Uuid, usize> {
let mut order = HashMap::with_capacity(targets.len());
if targets.is_empty() {
return order;
}
let Some(root) = self.get(&Uuid::nil()) else {
return order;
};
// DFS with an explicit stack. Push children in reverse so the
// iteration pops them in paint order (behind first).
// `children_ids_iter_forward` returns a boxed trait object, which
// is not `DoubleEndedIterator`, so we collect first and reverse
// over the owned `Vec`.
let mut stack: Vec<Uuid> = {
let children: Vec<Uuid> = root.children_ids_iter_forward(true).copied().collect();
children.into_iter().rev().collect()
};
let mut counter: usize = 0;
while let Some(id) = stack.pop() {
if targets.contains(&id) {
order.insert(id, counter);
if order.len() == targets.len() {
break;
}
}
counter += 1;
if let Some(shape) = self.get(&id) {
let children: Vec<Uuid> =
shape.children_ids_iter_forward(true).copied().collect();
for child in children.into_iter().rev() {
stack.push(child);
}
}
}
order
}
/// Returns the raw modifier matrix currently applied to `id`, if any.
///
/// The `get` method above already returns a shape with modifiers baked in,