fix(nosignal): hardware-test round 3 — notif Clear + Game Mode → DeckShift

F2: NsNotifications.qml "Clear" mirrors the service (Notifs.list.slice() →
n.close()) instead of n.notification?.dismiss() — cards now actually leave the
list, and it works for disk-restored notifications (whose notification is null).

C2: Toggles.qml gameMode delegate launches the guarded switch-to-gaming via
Quickshell.execDetached (debounced + disabled + 5s timer so a double-click can't
fire two SDDM restarts), and the tile is hidden entirely when DeckShift isn't
installed (Process probe for the gamescope session file).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
28allday 2026-06-20 06:34:08 +01:00
parent 44b257ce85
commit 2cc089b41f
2 changed files with 55 additions and 4 deletions

View file

@ -71,8 +71,16 @@ NsPanel {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
for (const n of Notifs.notClosed) // Mirror the service's own clear() / clearNotifs shortcut:
n.notification?.dismiss(); // NotifData.close() sets `closed` (drops it from notClosed so the
// card disappears), removes it from Notifs.list, dismisses the
// server notification and destroys it. Calling
// `notification?.dismiss()` directly cleared nothing it never
// touched the list, and is a no-op for notifications restored from
// disk (their `notification` is null). Iterate a slice() copy so
// mutating the list mid-loop is safe.
for (const n of Notifs.list.slice())
n.close();
} }
} }
} }

View file

@ -2,6 +2,8 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Bluetooth import Quickshell.Bluetooth
import Caelestia.Components import Caelestia.Components
import Caelestia.Config import Caelestia.Config
@ -17,6 +19,18 @@ StyledRect {
required property DrawerVisibilities visibilities required property DrawerVisibilities visibilities
required property BarPopouts.Wrapper popouts required property BarPopouts.Wrapper popouts
// NoSignal: the gamepad "Game Mode" toggle launches DeckShift gaming. Hide it
// entirely when DeckShift isn't installed (no gamescope session file) checked
// once at load by deckshiftProbe below.
property bool deckshiftInstalled: false
Process {
id: deckshiftProbe
running: true
command: ["test", "-f", "/usr/share/wayland-sessions/gamescope-session-steam-nm.desktop"]
onExited: (exitCode, exitStatus) => root.deckshiftInstalled = exitCode === 0
}
readonly property var quickToggles: { readonly property var quickToggles: {
const seenIds = new Set(); const seenIds = new Set();
@ -32,6 +46,11 @@ StyledRect {
return GlobalConfig.utilities.vpn.provider.some(p => typeof p === "object" ? (p.enabled === true) : false); return GlobalConfig.utilities.vpn.provider.some(p => typeof p === "object" ? (p.enabled === true) : false);
} }
// NoSignal: drop the Game Mode tile unless DeckShift is installed.
if (item.id === "gameMode") {
return root.deckshiftInstalled;
}
seenIds.add(item.id); seenIds.add(item.id);
return true; return true;
}); });
@ -126,9 +145,33 @@ StyledRect {
DelegateChoice { DelegateChoice {
roleValue: "gameMode" roleValue: "gameMode"
delegate: Toggle { delegate: Toggle {
id: gmTog
// NoSignal: launch the DeckShift gaming session if installed;
// otherwise do nothing. (Was caelestia's cosmetic Game Mode.)
// Kept styled as an off-toggle (muted, outline icon) the
// gamepad is a momentary LAUNCHER, not a stateful toggle.
// DEBOUNCED: a fast double-click must not fire two
// `switch-to-gaming` runs two SDDM restarts race the
// one-shot autologin and drop you at the password greeter.
// Guard mirrors the Super+Shift+S bind + the deckshift-login
// install-check, so it no-ops if DeckShift is absent.
property bool launching: false
icon: "gamepad" icon: "gamepad"
checked: GameMode.enabled disabled: launching
onClicked: GameMode.enabled = !GameMode.enabled onClicked: {
if (gmTog.launching)
return;
gmTog.launching = true;
gmTog.internalChecked = false; // don't latch "on"
relockTimer.start();
Quickshell.execDetached(["sh", "-c", "[ -x /usr/local/bin/switch-to-gaming ] && [ -f /usr/share/wayland-sessions/gamescope-session-steam-nm.desktop ] && exec /usr/local/bin/switch-to-gaming"]);
}
Timer {
id: relockTimer
interval: 5000
onTriggered: gmTog.launching = false
}
} }
} }
DelegateChoice { DelegateChoice {