Watch
1
0
Fork
You've already forked souveraine
0

Selection: size the window to the chip instead of masking a fullscreen shield

The masked-fullscreen version was completely untappable on device. Rather than
keep guessing at the mask, the window is now sized to the card and moved by
margins, so every pixel of it is the chip. Also stops an invisible fullscreen
surface sitting over the app's own selection UI.
This commit is contained in:
Fimeg 2026-07-29 18:13:02 -04:00
commit 147f7f28b1

View file

@ -58,7 +58,18 @@ Scope {
required property var modelData
screen: win.modelData
anchors { top: true; left: true; right: true; bottom: true }
// Sized to the card and positioned with margins, NOT a fullscreen
// shield with an input mask. The masked-fullscreen version shipped
// first and the chip was completely untappable on device; rather
// than keep guessing at the mask, this removes the question — every
// pixel of the window IS the chip, so there is nothing to mask and
// no invisible surface over the rest of the screen to fight the
// app's own selection UI (Firefox's popup, notably).
anchors { top: true; left: true }
margins { left: Math.round(win.targetX); top: Math.round(win.targetY) }
implicitWidth: card.implicitWidth
implicitHeight: card.implicitHeight
color: "transparent"
visible: Selection.hasSelection
WlrLayershell.namespace: "souveraine:selection"
@ -66,30 +77,30 @@ Scope {
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
exclusionMode: ExclusionMode.Ignore
// Only the card is interactive; the rest of the screen is not ours.
mask: Region { item: card }
// Anchor. Selection gives a pointer hint, which is where the finger
// lifted; -1 means it could not be resolved, and centre-bottom is
// the thumb-reachable fallback. Clamped so the card is never partly
// offscreen regardless of where the touch was.
// Selection gives a pointer hint — where the finger lifted. -1 means
// it could not be resolved, so fall back to thumb-reachable centre.
// Clamped against the screen, not the window: the window is now the
// card's size, so its own width/height cannot bound the placement.
readonly property int pad: 12
readonly property int hintX: Selection.anchorX
readonly property int hintY: Selection.anchorY
readonly property int scrW: win.screen ? win.screen.width : 1080
readonly property int scrH: win.screen ? win.screen.height : 2160
readonly property real targetX: {
if (win.hintX < 0) return (win.width - card.width) / 2;
if (win.hintX < 0) return Math.max(win.pad, (win.scrW - card.implicitWidth) / 2);
return Math.max(win.pad,
Math.min(win.hintX - card.width / 2,
win.width - card.width - win.pad));
Math.min(win.hintX - card.implicitWidth / 2,
win.scrW - card.implicitWidth - win.pad));
}
readonly property real targetY: {
if (win.hintY < 0) return win.height * 0.72;
// Prefer above the touch point so the finger does not cover the
// menu; flip below when there is no room up there.
const above = win.hintY - card.height - 16;
if (win.hintY < 0) return win.scrH * 0.72;
// Prefer above the touch point so the finger does not cover it;
// flip below when there is no room up there.
const above = win.hintY - card.implicitHeight - 16;
if (above >= win.pad) return above;
return Math.min(win.hintY + 16, win.height - card.height - win.pad);
return Math.min(win.hintY + 16,
win.scrH - card.implicitHeight - win.pad);
}
// `card` takes its size from the layout's implicit size, and the
@ -100,26 +111,13 @@ Scope {
// chip painted but nothing could be tapped. The background stays a
// plain Item child, because anchors on a layout-managed item are
// undefined behavior.
// The card fills the window now; the window's margins do the moving,
// so there is no x/y to animate here.
Item {
id: card
x: win.targetX
y: win.targetY
anchors.fill: parent
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
width: implicitWidth
height: implicitHeight
// Position eases so a re-selection slides rather than teleports,
// but only once mapped — animating from 0,0 on first show reads
// as the menu flying in from the corner.
Behavior on x { enabled: card.opacity > 0; animation: Appearance.animation.elementMove.numberAnimation.createObject(this) }
Behavior on y { enabled: card.opacity > 0; animation: Appearance.animation.elementMove.numberAnimation.createObject(this) }
opacity: Selection.hasSelection ? 1 : 0
scale: Selection.hasSelection ? 1 : 0.88
transformOrigin: Item.Bottom
Behavior on opacity { NumberAnimation { duration: 120; easing.type: Easing.OutCubic } }
Behavior on scale { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) }
Rectangle {
anchors.fill: parent