Preserve relative order of transient siblings

If a constraint indicates that window A must be below window B but it's
not the case at the moment, the workspace will move window A right after
window B.

This can invert the relative order of transient siblings, for example
let's say that there are three constraints

- A <- B (window A must be below window B)
- A <- C
- A <- D

and the unconstrained stacking order looks as follows: [B, C, D, A]. The
final constrained stacking order is expected to look as [A, B, C, D],
but currently it's [A, D, C, B] instead:

- starting stacking order: [B, C, D, A]
- apply A <- B constraint: [C, D, A, B]
- apply A <- C constraint: [D, A, C, B]
- apply A <- D constraint: [A, D, C, B]

In order to fix this issue, this patch makes the workspace traverse the
constraint graph in the reverse order. In addition to that, it ensures
that the relative order of transient siblings in unconstrained stacking
order is preserved in the constrained one.

BUG: 477262
This commit is contained in:
Vlad Zahorodnii 2023-11-21 11:22:55 +02:00
parent 41df430aa8
commit 15b8fbe604

View file

@ -82,7 +82,6 @@
#include <array>
#include <QDebug>
#include <QQueue>
namespace KWin
{
@ -541,22 +540,28 @@ QList<Window *> Workspace::constrainedStackingOrder()
// Apply the stacking order constraints. First, we enqueue the root constraints, i.e.
// the ones that are not affected by other constraints.
QQueue<Constraint *> constraints;
QList<Constraint *> constraints;
constraints.reserve(m_constraints.count());
for (Constraint *constraint : std::as_const(m_constraints)) {
if (constraint->parents.isEmpty()) {
constraint->enqueued = true;
constraints.enqueue(constraint);
constraints.append(constraint);
} else {
constraint->enqueued = false;
}
}
// Preserve the relative order of transient siblings in the unconstrained stacking order.
auto constraintComparator = [&stacking](Constraint *a, Constraint *b) {
return stacking.indexOf(a->above) > stacking.indexOf(b->above);
};
std::sort(constraints.begin(), constraints.end(), constraintComparator);
// Once we've enqueued all the root constraints, we traverse the constraints tree in
// the breadth-first search fashion. A constraint is applied only if its condition is
// the reverse breadth-first search fashion. A constraint is applied only if its condition is
// not met.
while (!constraints.isEmpty()) {
Constraint *constraint = constraints.dequeue();
Constraint *constraint = constraints.takeFirst();
const int belowIndex = stacking.indexOf(constraint->below);
const int aboveIndex = stacking.indexOf(constraint->above);
@ -567,10 +572,14 @@ QList<Window *> Workspace::constrainedStackingOrder()
stacking.insert(belowIndex, constraint->above);
}
for (Constraint *child : std::as_const(constraint->children)) {
// Preserve the relative order of transient siblings in the unconstrained stacking order.
QList<Constraint *> children = constraint->children;
std::sort(children.begin(), children.end(), constraintComparator);
for (Constraint *child : std::as_const(children)) {
if (!child->enqueued) {
child->enqueued = true;
constraints.enqueue(child);
constraints.append(child);
}
}
}