125 lines
4.2 KiB
QML
125 lines
4.2 KiB
QML
// A segmented choice that is NOT schema-bound.
|
|
//
|
|
// ChoiceRow reads its options from the preference schema. This one is handed
|
|
// them, for choices that describe system state rather than a Panama setting.
|
|
|
|
import QtQuick
|
|
import qs.config
|
|
|
|
SettingRow {
|
|
id: root
|
|
|
|
// [{ value, label }]
|
|
property var options: []
|
|
property var value: null
|
|
property bool enabled: true
|
|
|
|
signal selected(value: var)
|
|
|
|
controlWidth: Math.max(150, root.options.length * 92)
|
|
|
|
readonly property var currentOption:
|
|
root.options.find(option => option.value === root.value) ?? null
|
|
|
|
// One segment along, never wrapping -- the same neighbour a click would
|
|
// have picked, asked for in the same way.
|
|
function stepChoice(delta: int): void {
|
|
if (!root.enabled || root.options.length === 0)
|
|
return;
|
|
const at = root.options.findIndex(option => option.value === root.value);
|
|
const from = at < 0 ? 0 : at;
|
|
const next = Math.max(0, Math.min(root.options.length - 1, from + delta));
|
|
if (next === at)
|
|
return;
|
|
root.selected(root.options[next].value);
|
|
}
|
|
|
|
// Sibling of the strip rather than a child of it: a Rectangle inside a Row
|
|
// would be laid out as one more segment.
|
|
Rectangle {
|
|
anchors.fill: strip
|
|
anchors.margins: -3
|
|
radius: 12
|
|
z: -1
|
|
color: "transparent"
|
|
visible: strip.activeFocus
|
|
border.width: 2
|
|
border.color: Theme.accentSecondary
|
|
}
|
|
|
|
Row {
|
|
id: strip
|
|
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: 6
|
|
opacity: root.enabled ? 1 : 0.45
|
|
|
|
activeFocusOnTab: root.enabled
|
|
|
|
Accessible.role: Accessible.ComboBox
|
|
Accessible.name: root.label
|
|
Accessible.description: {
|
|
const chosen = root.currentOption
|
|
? String(root.currentOption.label ?? "")
|
|
: "nothing selected";
|
|
return root.detail === "" ? chosen : `${root.detail} — ${chosen}`;
|
|
}
|
|
Accessible.focusable: root.enabled
|
|
Accessible.focused: strip.activeFocus
|
|
|
|
Keys.onLeftPressed: root.stepChoice(-1)
|
|
Keys.onUpPressed: root.stepChoice(-1)
|
|
Keys.onRightPressed: root.stepChoice(1)
|
|
Keys.onDownPressed: root.stepChoice(1)
|
|
|
|
Repeater {
|
|
model: root.options
|
|
|
|
delegate: Rectangle {
|
|
id: segment
|
|
|
|
required property var modelData
|
|
|
|
readonly property bool current: root.value === segment.modelData.value
|
|
|
|
width: Math.max(84, segmentLabel.implicitWidth + 22)
|
|
height: 30
|
|
radius: 9
|
|
color: segment.current
|
|
? Theme.alpha(Theme.accent, 0.22)
|
|
: Theme.alpha(Theme.fg, segmentMouse.containsMouse ? 0.12 : 0.06)
|
|
border.width: 1
|
|
border.color: segment.current
|
|
? Theme.alpha(Theme.accent, 0.5)
|
|
: Theme.alpha(Theme.fg, 0.08)
|
|
|
|
// Named individually so the reader says which option it is on.
|
|
// Not a Tab stop: the strip is one stop and the arrows walk it.
|
|
Accessible.role: Accessible.RadioButton
|
|
Accessible.name: String(segment.modelData.label ?? "")
|
|
Accessible.checked: segment.current
|
|
|
|
Text {
|
|
id: segmentLabel
|
|
anchors.centerIn: parent
|
|
text: String(segment.modelData.label ?? "")
|
|
color: segment.current ? Theme.fg : Theme.fgDim
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
font.weight: segment.current ? Font.DemiBold : Font.Medium
|
|
}
|
|
|
|
MouseArea {
|
|
id: segmentMouse
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
enabled: root.enabled && !segment.current
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: root.selected(segment.modelData.value)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|