Watch
1
0
Fork
You've already forked souveraine
0
souveraine/surfaces/quickshell/modules/souveraine/selection/SelectionAction.qml
Fimeg 0bebc779c6 Selection menu surface: chip that expands to actions (TASK-18)
Overlay layer following DialHost, keyboardFocus None throughout, and an input
mask limited to the card so taps elsewhere reach the app underneath. Anchors to
the pointer hint above the touch point, clamped on screen.

Chip shows a character count, never a preview: the surface floats over the app
that owns the selection and the content may be a password, so it stays ambient
and discloses nothing. Read Aloud is live via Speech; agent and reference
actions render with the reason they cannot act yet.

Adds DeviceEvidence, reporting input to the state machine's existing
Request::Input with an intent label, so a new input surface is not another
isolated actor per DEVICE-STATE-MACHINE 1. Opt-in: nothing loads or watches
until Config.options.selection.enable.
2026-07-29 17:47:20 -04:00

79 lines
2.4 KiB
QML

// One row in the selection menu.
//
// A disabled row still renders and still explains itself — the same choice
// RadialDial makes with its `reason` in the middle of the ring. A greyed-out
// entry teaches nothing; a row that says "Needs the side-shoot seam in Ai.qml"
// tells you what is missing.
import QtQuick
import QtQuick.Layouts
import qs.services
import qs.modules.common
import qs.modules.common.widgets
Item {
id: root
property string icon: "radio_button_unchecked"
property string label: ""
property string sublabel: ""
/** LockContentPolicy tier this action's effect belongs to. */
property string tier: LockContentPolicy.ambient
/** Why this is unavailable. Shown in place of the sublabel when disabled. */
property string reason: ""
// `enabled` is the Item property; a disabled row is visible but inert.
signal triggered()
Layout.fillWidth: true
implicitHeight: root.sublabel.length > 0 || (!root.enabled && root.reason.length > 0)
? 56 : 44
MouseArea {
anchors.fill: parent
enabled: root.enabled
onClicked: {
Haptics.trigger("button-pressed");
root.triggered();
}
}
RowLayout {
anchors.fill: parent
anchors.leftMargin: 14
anchors.rightMargin: 14
spacing: 12
MaterialSymbol {
text: root.icon
iconSize: 20
opacity: root.enabled ? 1.0 : 0.4
color: Appearance.colors.colOnLayer1
}
ColumnLayout {
Layout.fillWidth: true
spacing: 1
StyledText {
Layout.fillWidth: true
text: root.label
elide: Text.ElideRight
opacity: root.enabled ? 1.0 : 0.4
color: Appearance.colors.colOnLayer1
font.pixelSize: Appearance.font.pixelSize.small
}
StyledText {
Layout.fillWidth: true
visible: text.length > 0
// The refusal replaces the description: when a row cannot act,
// what it would have done is less useful than why it cannot.
text: root.enabled ? root.sublabel : root.reason
elide: Text.ElideRight
wrapMode: Text.NoWrap
color: Appearance.colors.colSubtext
font.pixelSize: Appearance.font.pixelSize.smaller
}
}
}
}