Effects as a list per rule, minimal seed rules, README tidy

Each effect on a rule is a row with its settings at a glance, a button
to open it and one to drop it; an Add effect button unfolds the rest.
A fresh install seeds only the deliveroo rule. Preview updated.
This commit is contained in:
2026-09-08 17:14:47 +01:00
parent 87854969e2
commit bd752d0968
4 changed files with 128 additions and 14 deletions
-2
View File
@@ -9,8 +9,6 @@ https://github.com/user-attachments/assets/d727da98-3df0-40a8-bbba-cf08dc0aaf6e
![Attention Required](preview.png) ![Attention Required](preview.png)
> Video of the demo: coming soon.
> Tested on **Omarchy 4** (Arch Linux, Hyprland with the Lua config, omarchy-shell). > Tested on **Omarchy 4** (Arch Linux, Hyprland with the Lua config, omarchy-shell).
## Install ## Install
+127 -7
View File
@@ -489,37 +489,70 @@ Panel {
onCommitted: function(v) { if (root.svc) root.svc.updateRule(root.current, { apps: v }) } onCommitted: function(v) { if (root.svc) root.svc.updateRule(root.current, { apps: v }) }
} }
} }
// The rule's effects, one row each with its settings at a glance, a
// button to open it and one to drop it; then a button that unfolds
// the effects not yet on the rule.
EditorRow { EditorRow {
label: "Effects" label: "Effects"
Column { Column {
id: effectsBox
width: parent.width - root.trailInset width: parent.width - root.trailInset
spacing: Style.space(6) spacing: Style.space(6)
property bool adding: false
readonly property var active: root.effectNames(root.current)
readonly property var available: root.catalog.filter(function(e) { return effectsBox.active.indexOf(e.type) === -1 })
Repeater {
model: effectsBox.active
delegate: EffectLine {
required property string modelData
width: effectsBox.width
type: modelData
}
}
Note {
visible: effectsBox.active.length === 0
text: "empty: the rule matches but nothing happens yet"
}
Button {
text: effectsBox.adding ? "Pick an effect below" : "Add effect"
iconText: effectsBox.adding ? "󰅀" : "󰐕"
bordered: true
focusable: true
enabled: effectsBox.available.length > 0
foreground: root.fg
fontFamily: root.fontFamily
fontSize: Style.font.bodySmall
onClicked: effectsBox.adding = !effectsBox.adding
}
Flow { Flow {
visible: effectsBox.adding
width: parent.width width: parent.width
spacing: Style.spacing.md spacing: Style.spacing.md
Repeater { Repeater {
model: root.catalog model: effectsBox.available
delegate: Button { delegate: Button {
required property var modelData required property var modelData
readonly property bool on: root.effectNames(root.current).indexOf(modelData.type) !== -1
text: modelData.label text: modelData.label
iconText: on ? "󰄬" : (modelData.icon || "") iconText: modelData.icon || ""
selected: on
bordered: true bordered: true
focusable: true focusable: true
foreground: root.fg foreground: root.fg
fontFamily: root.fontFamily fontFamily: root.fontFamily
fontSize: Style.font.bodySmall fontSize: Style.font.bodySmall
tooltipText: modelData.subtitle + (on ? " · click to set up" : " · click to turn on and set up") tooltipText: modelData.subtitle
onClicked: { onClicked: {
if (!root.svc) return if (!root.svc) return
if (!on) root.svc.addRuleEffect(root.current, modelData.type) effectsBox.adding = false
root.svc.addRuleEffect(root.current, modelData.type)
root.openEffect(modelData.type) root.openEffect(modelData.type)
} }
} }
} }
} }
Note { text: root.effectNames(root.current).length ? "Click an effect to set it up. Empty: the rule matches but nothing happens." : "empty: nothing happens yet · click an effect to turn it on and set it up" }
} }
} }
EditorRow { EditorRow {
@@ -998,6 +1031,93 @@ Panel {
} }
} }
// One effect on the rule: name, its settings in a line, open and remove.
component EffectLine: Rectangle {
id: line
property string type: ""
readonly property var def: Catalog.find(type)
height: Style.space(34)
radius: Style.space(6)
color: lineArea.containsMouse ? Util.alpha(root.fg, 0.08) : Util.alpha(root.fg, 0.04)
function summary() {
if (!def || !root.svc) return ""
var parts = []
for (var i = 0; i < def.rows.length && parts.length < 3; i++) {
var r = def.rows[i]
parts.push(root.fmt(root.svc.ruleEffectOption(root.current, line.type, r.key, r.fallback), r.step, r.unit))
}
return parts.join(" · ")
}
MouseArea {
id: lineArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.openEffect(line.type)
}
Text {
id: lineIcon
anchors.left: parent.left
anchors.leftMargin: Style.space(10)
anchors.verticalCenter: parent.verticalCenter
text: line.def && line.def.icon ? line.def.icon : "󰄬"
color: root.fg
font.family: root.fontFamily
font.pixelSize: Style.font.body
width: Style.space(18)
}
Text {
id: lineName
anchors.left: lineIcon.right
anchors.leftMargin: Style.space(6)
anchors.verticalCenter: parent.verticalCenter
text: line.def ? line.def.label : line.type
color: root.fg
font.family: root.fontFamily
font.pixelSize: Style.font.body
font.weight: Font.DemiBold
}
Text {
anchors.left: lineName.right
anchors.leftMargin: Style.space(10)
anchors.right: openButton.left
anchors.rightMargin: Style.space(6)
anchors.verticalCenter: parent.verticalCenter
text: line.summary()
color: root.dim
font.family: root.fontFamily
font.pixelSize: Style.font.bodySmall
elide: Text.ElideRight
}
PanelActionButton {
id: openButton
anchors.right: removeButton.left
anchors.rightMargin: Style.space(2)
anchors.verticalCenter: parent.verticalCenter
iconText: "󰒓"
tooltipText: "Set up"
focusable: true
foreground: root.fg
fontFamily: root.fontFamily
onClicked: root.openEffect(line.type)
}
PanelActionButton {
id: removeButton
anchors.right: parent.right
anchors.rightMargin: Style.space(4)
anchors.verticalCenter: parent.verticalCenter
iconText: "󰅖"
tooltipText: "Remove from this rule"
focusable: true
foreground: root.fg
hoverColor: Color.urgent
fontFamily: root.fontFamily
onClicked: if (root.svc) root.svc.removeRuleEffect(root.current, line.type)
}
}
component EditorRow: Item { component EditorRow: Item {
property string label: "" property string label: ""
default property alias content: slot.data default property alias content: slot.data
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

+1 -5
View File
@@ -1,11 +1,7 @@
{ {
"version": 1, "version": 1,
"whileDnd": true, "whileDnd": true,
"defaults": { "letThrough": true,
"nudge": { "duration": 1, "intensity": 1.5, "speed": 200 },
"flash": { "duration": 1, "intensity": 0.9, "speed": 3, "thickness": 64, "color": "accent" },
"sound": { "intensity": 1, "speed": 1 }
},
"rules": [ "rules": [
{ {
"name": "deliveroo", "name": "deliveroo",