// One shortcut in the shortcuts browser. // // There are around a hundred and thirty of these on the page at once, so the // row is deliberately plain: no Loader for the row itself, no animations, and // the Change/Reset buttons are ordinary children that hide when the pointer is // elsewhere. Only the capture field is loaded on demand -- it is a FocusScope // nobody needs until they ask to rebind, and instantiating one per row is a // hundred and thirty focus scopes for a single edit. // // The row reports what was pressed and never decides anything: conflicts, // refusals and the write itself belong to the page, which is where the one // capture at a time is tracked. import QtQuick import qs.config import qs.services Item { id: root required property var bind property bool capturing: false property bool divider: true // Shown inside the capture field -- the page puts a refused chord here. property string message: "" signal changeRequested signal resetRequested signal captured(string chord) signal canceled readonly property bool overridden: Keybinds.isOverridden(root.bind.luaChord) width: parent ? parent.width : 620 implicitHeight: 42 Rectangle { anchors.fill: parent anchors.bottomMargin: 2 radius: 9 visible: hover.containsMouse || root.capturing color: root.capturing ? Theme.alpha(Theme.accent, 0.10) : Theme.alpha(Theme.fg, 0.05) } MouseArea { id: hover anchors.fill: parent hoverEnabled: true // Hover only. Accepting buttons here would put a click target over the // whole row and swallow presses meant for Change and Reset. acceptedButtons: Qt.NoButton } Text { anchors.left: parent.left anchors.leftMargin: 6 anchors.right: trailing.left anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: String(root.bind.description ?? "") color: Theme.fg font.family: Theme.fontFamily font.pixelSize: Theme.fontSize elide: Text.ElideRight } Row { id: trailing anchors.right: parent.right anchors.rightMargin: 6 anchors.verticalCenter: parent.verticalCenter visible: !root.capturing spacing: 8 // Where it used to be. Only while the row is under the pointer: at rest // the badge says a shortcut moved, and this says where from, which is // the question the badge raises and nothing else answers. Text { anchors.verticalCenter: parent.verticalCenter visible: root.overridden && hover.containsMouse text: "was " + Keybinds.shippedChordFor(root.bind.luaChord) color: Theme.fgMuted font.family: Theme.fontMono font.pixelSize: Theme.fontSizeSmall } StatusBadge { anchors.verticalCenter: parent.verticalCenter visible: root.overridden text: "Changed" tone: Theme.warn } SettingsButton { anchors.verticalCenter: parent.verticalCenter visible: hover.containsMouse text: "Change" // A scroll or click bind has no key to capture. enabled: !Keybinds.reloading && !root.bind.mouse onClicked: root.changeRequested() } SettingsButton { anchors.verticalCenter: parent.verticalCenter visible: hover.containsMouse && root.overridden text: "Reset" enabled: !Keybinds.reloading onClicked: root.resetRequested() } KeycapChord { anchors.verticalCenter: parent.verticalCenter chord: String(root.bind.chord ?? "") } } Loader { anchors.right: parent.right anchors.rightMargin: 6 anchors.verticalCenter: parent.verticalCenter width: 240 height: 30 active: root.capturing // Focus has to travel through the Loader for the capture inside it to // ever see a key press. focus: root.capturing sourceComponent: ShortcutCapture { focus: true message: root.message onCaptured: chord => root.captured(chord) onCanceled: root.canceled() } } Rectangle { anchors.left: parent.left anchors.leftMargin: 6 anchors.right: parent.right anchors.rightMargin: 6 anchors.bottom: parent.bottom height: 1 visible: root.divider color: Theme.alpha(Theme.fg, 0.05) } }