aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/assistant/Card.qml
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/assistant/Card.qml')
-rw-r--r--desktop/assistant/Card.qml136
1 files changed, 100 insertions, 36 deletions
diff --git a/desktop/assistant/Card.qml b/desktop/assistant/Card.qml
index 02ee476..069d3a0 100644
--- a/desktop/assistant/Card.qml
+++ b/desktop/assistant/Card.qml
@@ -205,7 +205,87 @@ Rectangle {
// Ask: canned questions matching the staged kinds (Presets.js). A click
// sends one; Shift+click puts it in the input to edit first.
- Flow {
+ // One action as a tile: its icon over its label, the tiles of a group
+ // sharing the card's width equally.
+ component Tile: Rectangle {
+ id: tile
+ property string icon
+ property string label
+ property bool on: true
+ signal picked(var mouse)
+ implicitHeight: 62
+ radius: 8
+ opacity: on ? 1 : 0.4
+ color: area.pressed && on ? Qt.alpha(Theme.accent, 0.35)
+ : area.containsMouse && on ? Qt.alpha(Theme.accent, 0.18)
+ : Qt.alpha(Theme.surface, 0.8)
+ border.width: 1
+ border.color: Qt.alpha(Theme.text, 0.12)
+ Glow { anchors.fill: parent; z: -1; radius: parent.radius; on: area.containsMouse && tile.on }
+ Column {
+ anchors.centerIn: parent
+ width: parent.width - 12
+ spacing: 4
+ Text {
+ width: parent.width
+ horizontalAlignment: Text.AlignHCenter
+ text: tile.icon
+ color: Theme.accent
+ font { family: Theme.iconFamily; pixelSize: Theme.fontSize + 2 }
+ }
+ Text {
+ width: parent.width
+ horizontalAlignment: Text.AlignHCenter
+ text: tile.label
+ elide: Text.ElideRight
+ color: Theme.text
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
+ }
+ }
+ MouseArea {
+ id: area
+ anchors.fill: parent
+ hoverEnabled: true
+ cursorShape: tile.on ? Qt.PointingHandCursor : Qt.ArrowCursor
+ onClicked: mouse => { if (tile.on) tile.picked(mouse); }
+ }
+ }
+
+ // A captioned grid of tiles; columns follow the width, about 150 px each.
+ component Group: Column {
+ id: group
+ property string caption
+ property var items: []
+ property bool on: true
+ signal picked(var item, var mouse)
+ spacing: 6
+ Text {
+ text: group.caption
+ color: Theme.subtext
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 5; capitalization: Font.AllUppercase }
+ }
+ Grid {
+ id: grid
+ width: group.width
+ spacing: 8
+ columns: Math.max(1, Math.floor((width + spacing) / (150 + spacing)))
+ Repeater {
+ model: group.items
+ Tile {
+ required property var modelData
+ width: (grid.width - (grid.columns - 1) * grid.spacing) / grid.columns
+ icon: modelData.icon ?? Presets.FALLBACK
+ label: modelData.label
+ on: group.on && (modelData.op !== "md" || card.canvas.connected)
+ onPicked: mouse => group.picked(modelData, mouse)
+ }
+ }
+ }
+ }
+
+ // Ask: canned questions matching the staged kinds (Presets.js). A click
+ // sends one; Shift+click puts it in the input to edit first.
+ Group {
id: asks
readonly property var offered: Presets.offered(Presets.list(Prefs.presets), card.canvas.staged)
visible: card.mode === "file" && offered.length > 0 && !card.canvas.opRunning
@@ -213,57 +293,41 @@ Rectangle {
bottom: dos.visible ? dos.top : status.visible ? status.top : actions.top; bottomMargin: 10
left: parent.left; right: parent.right; leftMargin: 14; rightMargin: 14
}
- spacing: 8
-
- Repeater {
- model: asks.offered
- Button {
- required property var modelData
- text: modelData.label
- enabled: card.canvas.connected && !card.canvas.blocked
- onClicked: mouse => {
- if (mouse && (mouse.modifiers & Qt.ShiftModifier)) {
- input.text = Presets.prompt(modelData);
- input.cursorPosition = input.text.length;
- input.forceActiveFocus();
- } else {
- card.canvas.ask(Presets.prompt(modelData));
- }
- }
+ caption: "Ask"
+ items: offered
+ on: card.canvas.connected && !card.canvas.blocked
+ onPicked: (p, mouse) => {
+ if (mouse.modifiers & Qt.ShiftModifier) {
+ input.text = Presets.prompt(p);
+ input.cursorPosition = input.text.length;
+ input.forceActiveFocus();
+ } else {
+ card.canvas.ask(Presets.prompt(p));
}
}
}
// Do: local operations (Ops.js, run by fileops or the daemon's write).
- // Compress… swaps the row for its formats. Hidden while one runs.
+ // Compress… swaps the group for its formats. Hidden while one runs.
property bool compressing: false
Connections { target: card.canvas; function onStagedChanged() { card.compressing = false; } }
- Flow {
+ Group {
id: dos
readonly property var offered: card.compressing
- ? Ops.COMPRESS.concat([{ label: "Back", op: "back" }])
+ ? Ops.COMPRESS.concat([{ label: "Back", op: "back", icon: "" }])
: Ops.offered(card.canvas.staged)
visible: card.mode === "file" && offered.length > 0 && !card.canvas.opRunning
anchors {
bottom: status.visible ? status.top : actions.top; bottomMargin: 10
left: parent.left; right: parent.right; leftMargin: 14; rightMargin: 14
}
- spacing: 8
-
- Repeater {
- model: dos.offered
- Button {
- required property var modelData
- text: modelData.label
- // Local operations need no daemon; To .md does.
- enabled: modelData.op !== "md" || card.canvas.connected
- onClicked: {
- if (modelData.op === "compress") card.compressing = true;
- else if (modelData.op === "back") card.compressing = false;
- else { card.compressing = false; card.canvas.runOp(modelData); }
- }
- }
+ caption: card.compressing ? "Compress as" : "Do"
+ items: offered
+ onPicked: o => {
+ if (o.op === "compress") card.compressing = true;
+ else if (o.op === "back") card.compressing = false;
+ else { card.compressing = false; card.canvas.runOp(o); }
}
}