diff --git a/.config/quickshell/ii/killDialog.qml b/.config/quickshell/ii/killDialog.qml new file mode 100644 index 00000000..fdccfe80 --- /dev/null +++ b/.config/quickshell/ii/killDialog.qml @@ -0,0 +1,195 @@ +//@ pragma UseQApplication +//@ pragma Env QS_NO_RELOAD_POPUP=1 +//@ pragma Env QT_QUICK_CONTROLS_STYLE=Basic +//@ pragma Env QT_QUICK_FLICKABLE_WHEEL_DECELERATION=10000 + +// Adjust this to make the app smaller or larger +//@ pragma Env QT_SCALE_FACTOR=1 + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Window +import Quickshell +import Quickshell.Io +import Quickshell.Hyprland +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions + +ApplicationWindow { + id: root + property int conflictCount: 0 + onConflictCountChanged: { + if (conflictCount === 0) { + root.close(); + } + } + + property real contentPadding: 8 + visible: true + onClosing: { + Qt.quit() + } + title: Translation.tr("Shell conflicts killer") + + Component.onCompleted: { + Config.ready // Just read to force init + MaterialThemeLoader.reapplyTheme(); + } + + minimumWidth: 400 + minimumHeight: 300 + maximumWidth: 400 + maximumHeight: 300 + width: 400 + height: 300 + color: Appearance.m3colors.m3background + + component ConflictingProgramGroup: ColumnLayout { + id: conflictGroup + required property list programs + required property string description + visible: false + onVisibleChanged: { + conflictCount += visible ? 1 : -1 + } + + signal alwaysSelected() + + Process { + running: true + command: ["pidof", ...conflictGroup.programs] + onExited: (exitCode, exitStatus) => { + if (exitCode === 0) { + conflictGroup.visible = true + } + } + } + + StyledText { + text: conflictGroup.programs.join(", ") + font.pixelSize: Appearance.font.pixelSize.normal + } + StyledText { + font { + pixelSize: Appearance.font.pixelSize.smaller + italic: true + } + text: conflictGroup.description + color: Appearance.colors.colSubtext + } + RowLayout { + Layout.alignment: Qt.AlignRight + + RippleButton { + colBackground: Appearance.colors.colLayer2 + contentItem: StyledText { + text: Translation.tr("Always") + } + onClicked: { + Quickshell.execDetached(["killall", ...conflictGroup.programs]) + conflictGroup.visible = false + conflictGroup.alwaysSelected() + } + } + RippleButton { + colBackground: Appearance.colors.colLayer2 + contentItem: StyledText { + text: Translation.tr("Yes") + } + onClicked: { + Quickshell.execDetached(["killall", ...conflictGroup.programs]) + conflictGroup.visible = false + } + } + RippleButton { + colBackground: Appearance.colors.colLayer2 + contentItem: StyledText { + text: Translation.tr("No") + } + onClicked: conflictGroup.visible = false + } + } + } + + ColumnLayout { + anchors { + fill: parent + margins: contentPadding + } + + Item { + // Titlebar + visible: Config.options?.windows.showTitlebar + Layout.fillWidth: true + implicitHeight: Math.max(welcomeText.implicitHeight, windowControlsRow.implicitHeight) + StyledText { + id: welcomeText + anchors { + left: Config.options.windows.centerTitle ? undefined : parent.left + horizontalCenter: Config.options.windows.centerTitle ? parent.horizontalCenter : undefined + verticalCenter: parent.verticalCenter + leftMargin: 12 + } + color: Appearance.colors.colOnLayer0 + text: Translation.tr("Kill conflicting programs?") + font.pixelSize: Appearance.font.pixelSize.title + font.family: Appearance.font.family.title + } + RowLayout { // Window controls row + id: windowControlsRow + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + RippleButton { + buttonRadius: Appearance.rounding.full + implicitWidth: 35 + implicitHeight: 35 + onClicked: root.close() + contentItem: MaterialSymbol { + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + text: "close" + iconSize: 20 + } + } + } + } + Rectangle { + // Content container + color: Appearance.m3colors.m3surfaceContainerLow + radius: Appearance.rounding.windowRounding - root.contentPadding + implicitHeight: contentColumn.implicitHeight + implicitWidth: contentColumn.implicitWidth + Layout.fillWidth: true + Layout.fillHeight: true + + ColumnLayout { + id: contentColumn + anchors.fill: parent + spacing: 12 + + ConflictingProgramGroup { + id: kded6Group + Layout.alignment: Qt.AlignHCenter + Layout.fillHeight: false + programs: ["kded6"] + description: Translation.tr("Conflicts with the shell's system tray implementation") + onAlwaysSelected: Config.options.conflictKiller.autoKillTrays = true + } + + ConflictingProgramGroup { + id: notificationDaemons + Layout.alignment: Qt.AlignHCenter + Layout.fillHeight: false + programs: ["mako", "dunst"] + description: Translation.tr("Conflicts with the shell's notification implementation") + onAlwaysSelected: Config.options.conflictKiller.autoKillNotificationDaemons = true + } + + } + } + } +} diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml index 464fe9c4..afc75c27 100644 --- a/.config/quickshell/ii/modules/common/Config.qml +++ b/.config/quickshell/ii/modules/common/Config.qml @@ -183,6 +183,11 @@ Singleton { property int suspend: 3 } + property JsonObject conflictKiller: JsonObject { + property bool autoKillNotificationDaemons: false + property bool autoKillTrays: false + } + property JsonObject dock: JsonObject { property bool enable: false property bool monochromeIcons: true diff --git a/.config/quickshell/ii/modules/common/widgets/RippleButton.qml b/.config/quickshell/ii/modules/common/widgets/RippleButton.qml index 759bc043..0308fa97 100644 --- a/.config/quickshell/ii/modules/common/widgets/RippleButton.qml +++ b/.config/quickshell/ii/modules/common/widgets/RippleButton.qml @@ -133,7 +133,7 @@ Button { background: Rectangle { id: buttonBackground radius: root.buttonEffectiveRadius - implicitHeight: 50 + implicitHeight: 30 color: root.buttonColor Behavior on color { diff --git a/.config/quickshell/ii/services/ConflictKiller.qml b/.config/quickshell/ii/services/ConflictKiller.qml new file mode 100644 index 00000000..fc94e338 --- /dev/null +++ b/.config/quickshell/ii/services/ConflictKiller.qml @@ -0,0 +1,49 @@ +pragma Singleton + +import qs +import qs.modules.common +import qs.modules.common.functions +import QtQuick +import Quickshell +import Quickshell.Io + +Singleton { + id: root + + property string killDialogQmlPath: FileUtils.trimFileProtocol(Quickshell.shellPath("killDialog.qml")) + + function load() { + // dummy to force init + } + + Connections { + target: Config + function onReadyChanged() { + if (Config.ready) checkConflictsProc.running = true + } + } + + Process { + id: checkConflictsProc + command: ["bash", "-c", `echo "$(pidof kded6);$(pidof mako dunst)"`] + stdout: StdioCollector { + onStreamFinished: { + const output = this.text; + const conflictingTrays = output.split(";")[0].trim().length > 0; + const conflictingNotifications = output.split(";")[1].trim().length > 0; + var openDialog = false; + if (conflictingTrays) { + if (!Config.options.conflictKiller.autoKillTrays) openDialog = true; + else Quickshell.execDetached(["killall", "kded6"]) + } + if (conflictingNotifications) { + if (!Config.options.conflictKiller.autoKillNotificationDaemons) openDialog = true; + else Quickshell.execDetached(["killall", "mako", "dunst"]) + } + if (openDialog) { + Quickshell.execDetached(["qs", "-p", root.killDialogQmlPath]) + } + } + } + } +} diff --git a/.config/quickshell/ii/shell.qml b/.config/quickshell/ii/shell.qml index 8186d1a5..4877bd44 100644 --- a/.config/quickshell/ii/shell.qml +++ b/.config/quickshell/ii/shell.qml @@ -52,10 +52,11 @@ ShellRoot { // Force initialization of some singletons Component.onCompleted: { - Cliphist.refresh() - FirstRunExperience.load() - Hyprsunset.load() MaterialThemeLoader.reapplyTheme() + Hyprsunset.load() + FirstRunExperience.load() + ConflictKiller.load() + Cliphist.refresh() } LazyLoader { active: enableBar && Config.ready && !Config.options.bar.vertical; component: Bar {} }