Extract confirmation dialog

This commit is contained in:
David Heinemeier Hansson
2026-05-29 12:57:43 +02:00
parent 5bdd264ce0
commit 49ca79081f
3 changed files with 145 additions and 96 deletions
+125
View File
@@ -0,0 +1,125 @@
import QtQuick
import qs.Commons
Item {
id: root
property bool opened: false
property string message: ""
property string cancelText: "Cancel"
property string confirmText: "Confirm"
property int selectedIndex: 1
property color background: Color.background
property color foreground: Color.foreground
property color scrim: Util.alpha(Color.background, 0.7)
property color selectedBackground: Util.alpha(Color.foreground, 0.08)
property color selectedText: Color.accent
property string fontFamily: Style.font.family
property int cornerRadius: Style.cornerRadius
signal canceled()
signal confirmed()
function handleKey(event) {
if (!root.opened) return false
if (event.key === Qt.Key_Escape) {
root.canceled()
return true
} else if (event.key === Qt.Key_Left || event.key === Qt.Key_Right || event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab) {
root.selectedIndex = root.selectedIndex === 0 ? 1 : 0
return true
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
if (root.selectedIndex === 0) root.canceled()
else root.confirmed()
return true
}
return false
}
visible: opened
Rectangle {
anchors.fill: parent
color: root.scrim
MouseArea { anchors.fill: parent; onClicked: root.canceled() }
Rectangle {
width: Math.min(parent.width - Style.space(96), Style.space(370))
height: Style.space(132)
anchors.centerIn: parent
color: root.background
border.color: root.selectedText
border.width: Style.normalBorderWidth
radius: root.cornerRadius
MouseArea { anchors.fill: parent; onClicked: {} }
Item {
anchors.fill: parent
anchors.margins: Style.space(18)
Text {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text: root.message
color: root.foreground
font.family: root.fontFamily
font.pixelSize: Style.font.title
wrapMode: Text.WordWrap
}
Row {
anchors.right: parent.right
anchors.bottom: parent.bottom
spacing: Style.space(10)
Repeater {
model: [root.cancelText, root.confirmText]
Rectangle {
required property int index
required property string modelData
readonly property bool selected: root.selectedIndex === index
readonly property bool destructive: index === 1
width: Style.space(88)
height: Style.space(34)
color: selected
? (destructive ? Util.alpha(Color.urgent, 0.22) : root.selectedBackground)
: "transparent"
border.color: destructive
? (selected ? Color.urgent : Util.alpha(Color.urgent, 0.56))
: (selected ? root.selectedText : Util.alpha(root.foreground, 0.38))
border.width: Style.normalBorderWidth
radius: 0
Text {
anchors.centerIn: parent
text: modelData
color: destructive ? (selected ? Color.urgent : root.foreground) : (selected ? root.selectedText : root.foreground)
font.family: root.fontFamily
font.pixelSize: Style.font.caption
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onEntered: root.selectedIndex = index
onClicked: {
if (index === 0) root.canceled()
else root.confirmed()
}
}
}
}
}
}
}
}
}
+1
View File
@@ -4,6 +4,7 @@ BarIndicator 1.0 BarIndicator.qml
BarWidget 1.0 BarWidget.qml BarWidget 1.0 BarWidget.qml
Button 1.0 Button.qml Button 1.0 Button.qml
ButtonGroup 1.0 ButtonGroup.qml ButtonGroup 1.0 ButtonGroup.qml
ConfirmDialog 1.0 ConfirmDialog.qml
CursorSurface 1.0 CursorSurface.qml CursorSurface 1.0 CursorSurface.qml
Dropdown 1.0 Dropdown.qml Dropdown 1.0 Dropdown.qml
KeyboardPanel 1.0 KeyboardPanel.qml KeyboardPanel 1.0 KeyboardPanel.qml
+19 -96
View File
@@ -4,6 +4,7 @@ import Quickshell.Wayland
import Quickshell.Widgets import Quickshell.Widgets
import QtQuick import QtQuick
import qs.Commons import qs.Commons
import qs.Ui
import "LauncherSearch.js" as LauncherSearch import "LauncherSearch.js" as LauncherSearch
Item { Item {
@@ -29,7 +30,6 @@ Item {
property var desktopHiddenEntryIds: ({}) property var desktopHiddenEntryIds: ({})
property bool deleteConfirmOpen: false property bool deleteConfirmOpen: false
property var deleteEntry: null property var deleteEntry: null
property int deleteConfirmIndex: 1
// Bound to the central [launcher] section in shell.toml via Color.qml. // Bound to the central [launcher] section in shell.toml via Color.qml.
// Each color already includes its alpha companion (composed in the // Each color already includes its alpha companion (composed in the
@@ -213,14 +213,14 @@ Item {
var entry = root.filteredEntries[index] var entry = root.filteredEntries[index]
if (!entry) return if (!entry) return
root.deleteEntry = entry root.deleteEntry = entry
root.deleteConfirmIndex = 1 deleteConfirm.selectedIndex = 1
root.deleteConfirmOpen = true root.deleteConfirmOpen = true
} }
function cancelDelete() { function cancelDelete() {
root.deleteConfirmOpen = false root.deleteConfirmOpen = false
root.deleteEntry = null root.deleteEntry = null
root.deleteConfirmIndex = 1 deleteConfirm.selectedIndex = 1
Qt.callLater(function() { keyCatcher.forceActiveFocus() }) Qt.callLater(function() { keyCatcher.forceActiveFocus() })
} }
@@ -364,17 +364,7 @@ Item {
Keys.priority: Keys.BeforeItem Keys.priority: Keys.BeforeItem
Keys.onPressed: function(event) { Keys.onPressed: function(event) {
if (root.deleteConfirmOpen) { if (root.deleteConfirmOpen) {
if (event.key === Qt.Key_Escape) { if (deleteConfirm.handleKey(event)) event.accepted = true
root.cancelDelete()
event.accepted = true
} else if (event.key === Qt.Key_Left || event.key === Qt.Key_Right || event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab) {
root.deleteConfirmIndex = root.deleteConfirmIndex === 0 ? 1 : 0
event.accepted = true
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
if (root.deleteConfirmIndex === 0) root.cancelDelete()
else root.confirmDelete()
event.accepted = true
}
return return
} }
@@ -423,90 +413,23 @@ Item {
} }
} }
Rectangle { ConfirmDialog {
id: deleteConfirm
anchors.fill: parent anchors.fill: parent
visible: root.deleteConfirmOpen opened: root.deleteConfirmOpen
z: 10 z: 10
color: root.scrim message: "Do you want to uninstall " + root.entryName(root.deleteEntry) + "?"
confirmText: "Uninstall"
MouseArea { anchors.fill: parent; onClicked: root.cancelDelete() } background: root.background
foreground: root.foreground
Rectangle { scrim: root.scrim
width: Math.min(parent.width - 96, 370) selectedBackground: root.selectedBackground
height: 132 selectedText: root.selectedText
anchors.centerIn: parent fontFamily: root.fontFamily
color: root.background cornerRadius: Style.cornerRadius
border.color: root.selectedText onCanceled: root.cancelDelete()
border.width: 1 onConfirmed: root.confirmDelete()
radius: Style.cornerRadius
MouseArea { anchors.fill: parent; onClicked: {} }
Item {
anchors.fill: parent
anchors.margins: 18
Text {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text: "Do you want to uninstall " + root.entryName(root.deleteEntry) + "?"
color: root.foreground
font.family: root.fontFamily
font.pixelSize: 16
wrapMode: Text.WordWrap
}
Row {
anchors.right: parent.right
anchors.bottom: parent.bottom
spacing: 10
Repeater {
model: ["Cancel", "Uninstall"]
Rectangle {
required property int index
required property string modelData
readonly property bool selected: root.deleteConfirmIndex === index
readonly property bool destructive: index === 1
width: 88
height: 34
color: selected
? (destructive ? Util.alpha(Color.urgent, 0.22) : root.selectedBackground)
: "transparent"
border.color: destructive
? (selected ? Color.urgent : Util.alpha(Color.urgent, 0.56))
: (selected ? root.selectedText : Util.alpha(root.foreground, 0.38))
border.width: 1
radius: 0
Text {
id: buttonText
anchors.centerIn: parent
text: modelData
color: destructive ? (selected ? Color.urgent : root.foreground) : (selected ? root.selectedText : root.foreground)
font.family: root.fontFamily
font.pixelSize: 14
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onEntered: root.deleteConfirmIndex = index
onClicked: {
if (index === 0) root.cancelDelete()
else root.confirmDelete()
}
}
}
}
}
}
}
} }
} }