even more deltarune

This commit is contained in:
2026-03-24 20:27:19 +02:00
parent 64797aedd4
commit 1deeda7f1a
6 changed files with 327 additions and 26 deletions

View File

@@ -17,9 +17,9 @@ PanelWindow {
right: theme.panelMarginRight
}
// Top layer keeps notifications above normal windows while still usually
// below fullscreen overlays. Overlay would be too aggressive here.
WlrLayershell.layer: WlrLayer.Top
// Match the media status overlay behavior so notifications stay visible
// even when an app is fullscreen.
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.focusable: false
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
WlrLayershell.namespace: "deltarune-quickshell-notifications"

View File

@@ -2,6 +2,7 @@ import QtQuick
import Quickshell
import Quickshell.Wayland
import Quickshell.Services.Mpris
import ".."
PanelWindow {
id: overlay
@@ -22,7 +23,8 @@ PanelWindow {
color: '#00ffffff'
visible: true
readonly property bool overlayEnabled: Boolean(ShellStateManager.global("shell.mprisOverlayEnabled", true))
visible: overlayEnabled
Repeater {
model: Mpris.players

View File

@@ -19,9 +19,16 @@ QtObject {
property bool appUsageLoaded: false
property var windowRequests: ({})
property var quickSettingsPayload: ({})
property var globals: ({
"notifications.stackPreviewEnabled": true
property var defaultGlobals: ({
"notifications.stackPreviewEnabled": true,
"shell.mprisOverlayEnabled": true
})
property var globals: Object.assign({}, defaultGlobals)
property string globalsPath: {
var homeDir = Quickshell.env("HOME");
return homeDir ? homeDir + "/.local/share/deltarunequickshell/globals.json" : "";
}
property bool globalsLoaded: false
signal shellOpened
signal shellClosed
@@ -175,7 +182,17 @@ QtObject {
appUsageFile.writeAdapter();
}
function persistGlobals() {
if (!globalsLoaded)
return;
if (!globalsFile || !globalsAdapter)
return;
globalsAdapter.values = globals || ({});
globalsFile.writeAdapter();
}
onAppUsageCountsChanged: persistAppUsage()
onGlobalsChanged: persistGlobals()
property FileView appUsageFile: FileView {
id: appUsageFile
@@ -199,6 +216,28 @@ QtObject {
}
}
property FileView globalsFile: FileView {
id: globalsFile
path: manager.globalsPath
watchChanges: true
onFileChanged: reload()
onLoaded: {
var loaded = globalsAdapter && globalsAdapter.values ? globalsAdapter.values : ({});
manager.globals = Object.assign({}, manager.defaultGlobals, loaded);
manager.globalsLoaded = true;
}
onLoadFailed: {
manager.globals = Object.assign({}, manager.defaultGlobals);
manager.globalsLoaded = true;
manager.persistGlobals();
}
JsonAdapter {
id: globalsAdapter
property var values: ({})
}
}
function setGlobal(key, value) {
var updated = Object.assign({}, globals);
updated[key] = value;
@@ -206,7 +245,7 @@ QtObject {
}
function global(key, defaultValue) {
return globals.hasOwnProperty(key) ? globals[key] : defaultValue;
return Object.prototype.hasOwnProperty.call(globals, key) ? globals[key] : defaultValue;
}
function notificationStackPreviewEnabled() {
@@ -220,4 +259,16 @@ QtObject {
function toggleNotificationStackPreviewEnabled() {
setNotificationStackPreviewEnabled(!notificationStackPreviewEnabled());
}
function mprisOverlayEnabled() {
return Boolean(global("shell.mprisOverlayEnabled", true));
}
function setMprisOverlayEnabled(enabled) {
setGlobal("shell.mprisOverlayEnabled", Boolean(enabled));
}
function toggleMprisOverlayEnabled() {
setMprisOverlayEnabled(!mprisOverlayEnabled());
}
}

View File

@@ -36,7 +36,7 @@ Item {
property int activeSelection: 0
property bool inBluetoothMenu: false
property bool inWallpaperMenu: false
property bool inNotificationsMenu: false
property bool inShellMenu: false
property bool inPowerMenu: false
property bool inPowerConfirm: false
@@ -84,7 +84,7 @@ Item {
}
function currentAction() {
if (root.inBluetoothMenu || root.inWallpaperMenu || root.inNotificationsMenu || root.inPowerMenu)
if (root.inBluetoothMenu || root.inWallpaperMenu || root.inShellMenu || root.inPowerMenu)
return null;
return menuLength() > 0 ? menuAt(activeSelection) : null;
}
@@ -198,7 +198,7 @@ Item {
property int bluetoothActionIndex: 1
property int wallpaperActionIndex: 2
property int notificationsActionIndex: 3
property int shellActionIndex: 3
property int powerActionIndex: 4
property int pendingPowerActionIndex: 0
property var pendingPowerCommand: []
@@ -225,7 +225,7 @@ Item {
ent: function () {
root.inBluetoothMenu = true;
root.inWallpaperMenu = false;
root.inNotificationsMenu = false;
root.inShellMenu = false;
root.isSelected = false;
root.activeSelection = 0;
root.clampSelection();
@@ -239,7 +239,7 @@ Item {
ent: function () {
root.inWallpaperMenu = true;
root.inBluetoothMenu = false;
root.inNotificationsMenu = false;
root.inShellMenu = false;
root.isSelected = false;
var currentIndex = root.currentWallpaperIndex();
root.activeSelection = currentIndex >= 0 ? currentIndex : 0;
@@ -252,9 +252,9 @@ Item {
}
},
{
name: "Notifications",
name: "Shell",
ent: function () {
root.inNotificationsMenu = true;
root.inShellMenu = true;
root.inBluetoothMenu = false;
root.inWallpaperMenu = false;
root.inPowerMenu = false;
@@ -274,7 +274,7 @@ Item {
root.inPowerConfirm = false;
root.inBluetoothMenu = false;
root.inWallpaperMenu = false;
root.inNotificationsMenu = false;
root.inShellMenu = false;
root.isSelected = false;
root.activeSelection = 0;
root.pendingPowerActionIndex = 0;
@@ -315,7 +315,7 @@ Item {
}
]
property var notificationActions: [
property var shellActions: [
{
name: "Stack Preview",
ent: function () {
@@ -327,10 +327,22 @@ Item {
return "ON";
return manager.notificationStackPreviewEnabled() ? "ON" : "OFF";
}
},
{
name: "Media Overlay",
ent: function () {
if (manager && manager.toggleMprisOverlayEnabled)
manager.toggleMprisOverlayEnabled();
},
getState: function () {
if (!manager || !manager.mprisOverlayEnabled)
return "ON";
return manager.mprisOverlayEnabled() ? "ON" : "OFF";
}
}
]
property var menuModel: root.inBluetoothMenu ? Bluetooth.devices : (root.inWallpaperMenu ? wallpaperFolderModel : (root.inNotificationsMenu ? notificationActions : (root.inPowerMenu ? (root.inPowerConfirm ? powerConfirmActions : powerActions) : actions)))
property var menuModel: root.inBluetoothMenu ? Bluetooth.devices : (root.inWallpaperMenu ? wallpaperFolderModel : (root.inShellMenu ? shellActions : (root.inPowerMenu ? (root.inPowerConfirm ? powerConfirmActions : powerActions) : actions)))
FolderListModel {
id: wallpaperFolderModel
@@ -427,7 +439,7 @@ Item {
}
Text {
text: root.inPowerConfirm ? "CONFIRM" : (root.inPowerMenu ? "POWER" : (root.inBluetoothMenu ? "BLUETOOTH" : (root.inWallpaperMenu ? "WALLPAPER" : (root.inNotificationsMenu ? "NOTIFICATIONS" : "CONFIG"))))
text: root.inPowerConfirm ? "CONFIRM" : (root.inPowerMenu ? "POWER" : (root.inBluetoothMenu ? "BLUETOOTH" : (root.inWallpaperMenu ? "WALLPAPER" : (root.inShellMenu ? "SHELL" : "CONFIG"))))
font.family: "8bitoperator JVE"
font.pixelSize: 71
renderType: Text.NativeRendering
@@ -561,10 +573,10 @@ Item {
const wallpaperPath = wallpaperPathAt(activeSelection);
if (wallpaperPath && wallpaperPath.length > 0)
root.applyWallpaper(wallpaperPath);
} else if (root.inNotificationsMenu) {
const notificationAction = menuAt(activeSelection);
if (notificationAction && notificationAction.ent)
notificationAction.ent();
} else if (root.inShellMenu) {
const shellAction = menuAt(activeSelection);
if (shellAction && shellAction.ent)
shellAction.ent();
} else if (root.inPowerConfirm) {
const confirmAction = menuAt(activeSelection);
if (confirmAction && confirmAction.name === "Yes") {
@@ -615,10 +627,10 @@ Item {
root.pendingPowerCommand = [];
root.activeSelection = root.powerActionIndex;
root.clampSelection();
} else if (root.inNotificationsMenu) {
root.inNotificationsMenu = false;
} else if (root.inShellMenu) {
root.inShellMenu = false;
root.isSelected = false;
root.activeSelection = root.notificationsActionIndex;
root.activeSelection = root.shellActionIndex;
root.clampSelection();
} else if (root.inBluetoothMenu) {
root.inBluetoothMenu = false;
@@ -642,7 +654,7 @@ Item {
root.isSelected = false;
root.inBluetoothMenu = false;
root.inWallpaperMenu = false;
root.inNotificationsMenu = false;
root.inShellMenu = false;
root.inPowerMenu = false;
root.inPowerConfirm = false;
root.pendingPowerActionIndex = 0;

BIN
Startup/deltarune.webm Normal file

Binary file not shown.

236
Startup/shell.qml Normal file
View File

@@ -0,0 +1,236 @@
import Quickshell
import Quickshell.Bluetooth
import Quickshell.Wayland
import Quickshell.Hyprland
import QtQuick
import QtMultimedia
PanelWindow {
id: overlay
anchors {
top: true
left: true
right: true
bottom: true
}
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.focusable: true
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
WlrLayershell.namespace: "deltarune-startup-overlay"
color: "#000000"
exclusionMode: ExclusionMode.Ignore
aboveWindows: true
focusable: true
property string bluetoothTargetName: "JBL Tune 525BT"
property int bluetoothTimeoutMs: 5000
property bool waitingForBluetooth: true
property bool animationLaunchScheduled: false
property bool animationStarted: false
function bluetoothModelCount() {
if (!Bluetooth.devices)
return 0;
if (typeof Bluetooth.devices.count === "function")
return Bluetooth.devices.count();
if (Bluetooth.devices.count !== undefined)
return Bluetooth.devices.count;
if (Bluetooth.devices.length !== undefined)
return Bluetooth.devices.length;
return 0;
}
function bluetoothModelGet(index) {
if (!Bluetooth.devices || index < 0 || index >= bluetoothModelCount())
return null;
if (Bluetooth.devices.get)
return Bluetooth.devices.get(index);
if (Bluetooth.devices.length !== undefined)
return Bluetooth.devices[index];
return null;
}
function bluetoothDeviceName(device) {
if (!device)
return "";
if (device.name && device.name.length > 0)
return String(device.name);
if (device.deviceName && device.deviceName.length > 0)
return String(device.deviceName);
return "";
}
function findTargetBluetoothDevice() {
for (var i = 0; i < bluetoothModelCount(); i++) {
var device = bluetoothModelGet(i);
if (bluetoothDeviceName(device) === bluetoothTargetName)
return device;
}
return null;
}
function beginAnimation() {
if (animationStarted || animationLaunchScheduled)
return;
waitingForBluetooth = false;
bluetoothRetryTimer.stop();
bluetoothTimeoutTimer.stop();
animationLaunchScheduled = true;
startupDelayTimer.restart();
}
function handlePrimaryAction() {
if (!animationStarted)
beginAnimation();
else
finishStartup();
}
function finishStartup() {
if (player.playbackState !== MediaPlayer.StoppedState)
player.stop();
else
Qt.quit();
}
HyprlandFocusGrab {
active: true
windows: [overlay]
}
FocusScope {
id: overlayFocus
anchors.fill: parent
focus: true
Component.onCompleted: overlayFocus.forceActiveFocus()
onVisibleChanged: if (visible)
overlayFocus.forceActiveFocus()
onFocusChanged: if (focus)
overlayFocus.forceActiveFocus()
Keys.onReleased: function (event) {
switch (event.key) {
case Qt.Key_Z:
case Qt.Key_Return:
case Qt.Key_Enter:
event.accepted = true;
overlay.handlePrimaryAction();
return;
}
}
}
Connections {
target: Bluetooth.devices
ignoreUnknownSignals: true
function onCountChanged() {
if (overlay.waitingForBluetooth)
bluetoothRetryTimer.restart();
}
function onModelReset() {
if (overlay.waitingForBluetooth)
bluetoothRetryTimer.restart();
}
function onRowsInserted() {
if (overlay.waitingForBluetooth)
bluetoothRetryTimer.restart();
}
}
MediaPlayer {
id: player
source: "deltarune.webm" // Local file or URL
autoPlay: false
videoOutput: videoOutput
playbackRate: 1
audioOutput: AudioOutput {}
onPlaybackStateChanged: a => {
if (player.playbackState === MediaPlayer.StoppedState)
overlay.finishStartup();
}
}
Timer {
id: startupDelayTimer
interval: 1000
repeat: false
onTriggered: {
overlay.animationLaunchScheduled = false;
overlay.animationStarted = true;
player.play();
}
}
Timer {
id: bluetoothRetryTimer
interval: 250
running: true
repeat: true
onTriggered: {
if (!overlay.waitingForBluetooth) {
stop();
return;
}
const device = overlay.findTargetBluetoothDevice();
if (!device)
return;
if (device.connected) {
overlay.beginAnimation();
return;
}
device.connect();
}
}
Timer {
id: bluetoothTimeoutTimer
interval: overlay.bluetoothTimeoutMs
running: true
repeat: false
onTriggered: overlay.beginAnimation()
}
VideoOutput {
id: videoOutput
anchors.fill: parent
}
Text {
visible: overlay.waitingForBluetooth
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
color: "#ffffff"
font.family: "Determination Mono"
font.pixelSize: 28
text: "CONNECTING TO " + overlay.bluetoothTargetName.toUpperCase()
z: 1
}
Text {
visible: !overlay.animationStarted
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 56
color: "#ffffff"
font.family: "Determination Mono"
font.pixelSize: 28
text: overlay.waitingForBluetooth ? "PRESS Z OR ENTER TO SKIP WAIT" : "STARTING..."
z: 1
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
hoverEnabled: true
cursorShape: Qt.BlankCursor
}
}