Rebuild Displays around the canvas, and let the transaction keep color
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -31,8 +31,106 @@ for contract in \
|
||||
done
|
||||
rg -Fq 'DisplayArrangement {' "$page" \
|
||||
|| fail 'Displays page does not expose the arrangement canvas'
|
||||
rg -Fq 'visible: Displays.monitors.length > 1' "$page" \
|
||||
|| fail 'arrangement is shown for a single display'
|
||||
|
||||
# Flipped by the displays redesign, deliberately and in this direction.
|
||||
#
|
||||
# The canvas used to be hidden below two displays. That left a laptop opening
|
||||
# its Displays page on a picker offering a list of one, and made the page's
|
||||
# most legible surface the one thing a single-display machine never saw. It is
|
||||
# the hero of the page now: a solo display is rendered in it, and only dragging
|
||||
# goes away, because there is nothing to arrange that display against.
|
||||
# The bindings of one QML element, found by matching its braces. A fixed-size
|
||||
# window around the element would either miss a binding or catch a neighbour's,
|
||||
# and both of those are the wrong answer here: the page's selector chips and its
|
||||
# Workspaces card are legitimately gated on having two displays.
|
||||
element_binding() { # file, element, pattern, present|absent
|
||||
python3 - "$@" <<'PY'
|
||||
import re
|
||||
import sys
|
||||
|
||||
path, opener, pattern, expectation = sys.argv[1:5]
|
||||
text = open(path, encoding="utf-8").read()
|
||||
start = text.find(opener)
|
||||
if start < 0:
|
||||
raise SystemExit(1)
|
||||
depth = 0
|
||||
end = len(text)
|
||||
for index in range(start + len(opener) - 1, len(text)):
|
||||
if text[index] == "{":
|
||||
depth += 1
|
||||
elif text[index] == "}":
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
end = index
|
||||
break
|
||||
found = re.search(pattern, text[start:end]) is not None
|
||||
raise SystemExit(0 if found == (expectation == "present") else 1)
|
||||
PY
|
||||
}
|
||||
|
||||
python3 - "$page" <<'PY' \
|
||||
|| fail 'the arrangement canvas is gated on a monitor count again -- on the canvas itself or on the card holding it -- so a single display sees no canvas'
|
||||
import re
|
||||
import sys
|
||||
|
||||
text = open(sys.argv[1], encoding="utf-8").read()
|
||||
start = text.find("DisplayArrangement {")
|
||||
if start < 0:
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def own_bindings(open_brace, stop):
|
||||
"""The block's own bindings, with nested elements left out: a sibling
|
||||
card's gate is its business, and this is only about the canvas."""
|
||||
kept = []
|
||||
depth = 0
|
||||
for char in text[open_brace + 1:stop]:
|
||||
if char == "{":
|
||||
depth += 1
|
||||
elif char == "}":
|
||||
depth -= 1
|
||||
if depth < 0:
|
||||
break
|
||||
elif depth == 0:
|
||||
kept.append(char)
|
||||
return "".join(kept)
|
||||
|
||||
|
||||
# The canvas's own bindings, and those of whatever element holds it: a gate on
|
||||
# either one is a canvas a single display never sees. This lived on the card
|
||||
# rather than on the canvas before the redesign, which is exactly why the
|
||||
# element alone is not enough to look at.
|
||||
stack = []
|
||||
for index, char in enumerate(text[:start]):
|
||||
if char == "{":
|
||||
stack.append(index)
|
||||
elif char == "}" and stack:
|
||||
stack.pop()
|
||||
if not stack:
|
||||
raise SystemExit(1)
|
||||
|
||||
regions = [own_bindings(start + len("DisplayArrangement {") - 1, len(text)),
|
||||
own_bindings(stack[-1], start)]
|
||||
gate = re.compile(r"visible:.*monitors\.length")
|
||||
raise SystemExit(1 if any(gate.search(region) for region in regions) else 0)
|
||||
PY
|
||||
|
||||
for contract in \
|
||||
'One display connected' \
|
||||
'draggable'; do
|
||||
rg -Fq "$contract" "$component" \
|
||||
|| fail "a solo display is not explained or not protected from dragging: $contract"
|
||||
done
|
||||
element_binding "$component" 'DragHandler {' 'enabled:' present \
|
||||
|| fail 'the drag handler is unconditional, so a solo display can be dragged around a canvas with nothing to arrange against'
|
||||
|
||||
# Mirroring has no position of its own: the compositor stacks a mirrored output
|
||||
# on its target. The canvas says which display it is mirroring rather than
|
||||
# drawing it wherever its stale coordinates happen to point.
|
||||
rg -Fq 'Mirrors ' "$component" \
|
||||
|| fail 'a mirrored display is drawn with no badge saying what it mirrors'
|
||||
rg -Fq 'mirrorOf' "$component" \
|
||||
|| fail 'the canvas does not read the mirror flag off its rects'
|
||||
[[ -f "$identify" ]] || fail 'DisplayIdentify.qml is missing'
|
||||
for contract in \
|
||||
'model: Quickshell.screens' \
|
||||
@@ -94,6 +192,17 @@ keyboard="$(ipc keyboardFixture)"
|
||||
jq -e '.afterArrow == 2990 and .afterShiftArrow == 2890' <<<"$keyboard" >/dev/null \
|
||||
|| fail "keyboard movement did not use 10/100 logical-pixel steps: $keyboard"
|
||||
|
||||
solo="$(ipc soloFixture)"
|
||||
jq -e '(.rects | length) == 1 and .scale > 0 and .draggable == false' <<<"$solo" >/dev/null \
|
||||
|| fail "a single display did not render solo with dragging disabled: $solo"
|
||||
|
||||
mirror="$(ipc mirrorFixture)"
|
||||
jq -e '(.rects | length) == 2
|
||||
and (.rects[1].mirrorOf == "DP-2")
|
||||
and (.rects[1].x == .rects[0].x) and (.rects[1].y == .rects[0].y)' \
|
||||
<<<"$mirror" >/dev/null \
|
||||
|| fail "a mirrored display was not stacked on its target and flagged: $mirror"
|
||||
|
||||
primary="$(ipc primaryFixture)"
|
||||
jq -e '. == [
|
||||
{"name":"DP-2","x":-3000,"y":0,"primary":false},
|
||||
|
||||
Reference in New Issue
Block a user