From 49ca79081fe4fa5573888ff2fc25266efd920e1e Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 29 May 2026 12:57:43 +0200 Subject: [PATCH] Extract confirmation dialog --- shell/Ui/ConfirmDialog.qml | 125 ++++++++++++++++++++++++++++ shell/Ui/qmldir | 1 + shell/plugins/launcher/Launcher.qml | 115 +++++-------------------- 3 files changed, 145 insertions(+), 96 deletions(-) create mode 100644 shell/Ui/ConfirmDialog.qml diff --git a/shell/Ui/ConfirmDialog.qml b/shell/Ui/ConfirmDialog.qml new file mode 100644 index 00000000..21554331 --- /dev/null +++ b/shell/Ui/ConfirmDialog.qml @@ -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() + } + } + } + } + } + } + } + } +} diff --git a/shell/Ui/qmldir b/shell/Ui/qmldir index aa97bd45..e3d9dabe 100644 --- a/shell/Ui/qmldir +++ b/shell/Ui/qmldir @@ -4,6 +4,7 @@ BarIndicator 1.0 BarIndicator.qml BarWidget 1.0 BarWidget.qml Button 1.0 Button.qml ButtonGroup 1.0 ButtonGroup.qml +ConfirmDialog 1.0 ConfirmDialog.qml CursorSurface 1.0 CursorSurface.qml Dropdown 1.0 Dropdown.qml KeyboardPanel 1.0 KeyboardPanel.qml diff --git a/shell/plugins/launcher/Launcher.qml b/shell/plugins/launcher/Launcher.qml index 56aac851..bde9b097 100644 --- a/shell/plugins/launcher/Launcher.qml +++ b/shell/plugins/launcher/Launcher.qml @@ -4,6 +4,7 @@ import Quickshell.Wayland import Quickshell.Widgets import QtQuick import qs.Commons +import qs.Ui import "LauncherSearch.js" as LauncherSearch Item { @@ -29,7 +30,6 @@ Item { property var desktopHiddenEntryIds: ({}) property bool deleteConfirmOpen: false property var deleteEntry: null - property int deleteConfirmIndex: 1 // Bound to the central [launcher] section in shell.toml via Color.qml. // Each color already includes its alpha companion (composed in the @@ -213,14 +213,14 @@ Item { var entry = root.filteredEntries[index] if (!entry) return root.deleteEntry = entry - root.deleteConfirmIndex = 1 + deleteConfirm.selectedIndex = 1 root.deleteConfirmOpen = true } function cancelDelete() { root.deleteConfirmOpen = false root.deleteEntry = null - root.deleteConfirmIndex = 1 + deleteConfirm.selectedIndex = 1 Qt.callLater(function() { keyCatcher.forceActiveFocus() }) } @@ -364,17 +364,7 @@ Item { Keys.priority: Keys.BeforeItem Keys.onPressed: function(event) { if (root.deleteConfirmOpen) { - if (event.key === Qt.Key_Escape) { - 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 - } + if (deleteConfirm.handleKey(event)) event.accepted = true return } @@ -423,90 +413,23 @@ Item { } } - Rectangle { + ConfirmDialog { + id: deleteConfirm + anchors.fill: parent - visible: root.deleteConfirmOpen + opened: root.deleteConfirmOpen z: 10 - color: root.scrim - - MouseArea { anchors.fill: parent; onClicked: root.cancelDelete() } - - Rectangle { - width: Math.min(parent.width - 96, 370) - height: 132 - anchors.centerIn: parent - color: root.background - border.color: root.selectedText - border.width: 1 - 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() - } - } - } - } - } - } - } + message: "Do you want to uninstall " + root.entryName(root.deleteEntry) + "?" + confirmText: "Uninstall" + background: root.background + foreground: root.foreground + scrim: root.scrim + selectedBackground: root.selectedBackground + selectedText: root.selectedText + fontFamily: root.fontFamily + cornerRadius: Style.cornerRadius + onCanceled: root.cancelDelete() + onConfirmed: root.confirmDelete() } }