Files
DeltaruneQuickshell/Shell/Windows/QuickSettings/QuickSettingsApp.qml
2026-02-05 16:41:27 +02:00

313 lines
9.1 KiB
QML

import QtQuick
import Quickshell.Bluetooth
import Quickshell.Services.Pipewire
import "../.."
Item {
id: root
width: parent ? parent.width : 1280
height: parent ? parent.height : 820
focus: true
/* ------------------------------
PIXEL CONSTANTS (DO NOT TOUCH)
------------------------------ */
property int menuLeft: 64
property int menuTop: 140
property int lineHeight: 38 + 40 + 1
property int nameFontSize: 32
property int stateFontSize: 28
property int stateColumnX: 824
property int soulOffsetX: -36 - 32
property int soulOffsetY: -26
/* ------------------------------ */
property ShellStateManager manager: null
property int activeSelection: 0
property bool inBluetoothMenu: false
property bool isSelected: false
function clamp(v, lo, hi) {
return Math.max(lo, Math.min(hi, v));
}
function menuLength() {
if (!menuModel)
return 0;
if (menuModel.count !== undefined)
return menuModel.count;
if (menuModel.length !== undefined)
return menuModel.length;
return 0;
}
function menuAt(index) {
if (!menuModel)
return null;
if (menuModel.get)
return menuModel.get(index);
if (menuModel.length !== undefined)
return menuModel[index];
return null;
}
function wrapIndex(i) {
const length = menuLength();
if (length === 0)
return 0;
return (i + length) % length;
}
function currentAction() {
if (root.inBluetoothMenu)
return null;
return menuLength() > 0 ? menuAt(activeSelection) : null;
}
function clampSelection() {
const length = menuLength();
if (length === 0) {
activeSelection = 0;
return;
}
activeSelection = clamp(activeSelection, 0, length - 1);
}
function bluetoothDisplayName(device) {
if (!device)
return "";
if (device.name && device.name.length > 0)
return device.name;
return device.deviceName || "";
}
function bluetoothDisplayState(device) {
if (!device)
return "";
// dont change symbols
return device.connected ? "✓" : "";
}
function bluetoothDeviceAt(index) {
const item = bluetoothRepeater.itemAt(index);
if (!item)
return null;
return item.device || null;
}
PwObjectTracker {
objects: [Pipewire.defaultAudioSink]
}
property int bluetoothActionIndex: 1
property var actions: [
{
name: "Master Volume",
arr: function (dir) {
const sink = Pipewire.defaultAudioSink;
if (!sink || !sink.audio)
return;
const step = 0.05;
sink.audio.muted = false;
sink.audio.volume = clamp(sink.audio.volume + dir * step, 0, 1);
},
getState: function () {
const sink = Pipewire.defaultAudioSink;
if (!sink || !sink.audio)
return "—";
return Math.round(sink.audio.volume * 100) + "%";
}
},
{
name: "Bluetooth",
ent: function () {
root.inBluetoothMenu = true;
root.isSelected = false;
root.activeSelection = 0;
root.clampSelection();
},
getState: function () {
return "";
}
}
]
property var menuModel: root.inBluetoothMenu ? Bluetooth.devices : actions
Connections {
target: Bluetooth.devices
ignoreUnknownSignals: true
function onCountChanged() {
if (root.inBluetoothMenu)
root.clampSelection();
}
function onModelReset() {
if (root.inBluetoothMenu)
root.clampSelection();
}
function onRowsInserted() {
if (root.inBluetoothMenu)
root.clampSelection();
}
function onRowsRemoved() {
if (root.inBluetoothMenu)
root.clampSelection();
}
}
Text {
text: root.inBluetoothMenu ? "BLUETOOTH" : "CONFIG"
font.family: "8bitoperator JVE"
font.pixelSize: 71
renderType: Text.NativeRendering
font.hintingPreference: Font.PreferNoHinting
smooth: false
antialiasing: false
anchors.horizontalCenter: parent.horizontalCenter
color: "#ffffff"
y: 32
}
Repeater {
id: bluetoothRepeater
model: root.menuModel
delegate: Item {
width: root.width
height: lineHeight
x: 0
y: menuTop + index * lineHeight
property var device: root.inBluetoothMenu ? modelData : null
Image {
source: "./soul.png"
width: 36
height: 36
x: 182
y: 8 + 14
visible: root.activeSelection == index
}
Text {
x: 239
y: 0
text: root.inBluetoothMenu ? root.bluetoothDisplayName(modelData) : modelData.name
font.family: "8bitoperator JVE"
font.pixelSize: 71
font.letterSpacing: 1
renderType: Text.NativeRendering
font.hintingPreference: Font.PreferNoHinting
smooth: false
antialiasing: false
color: (root.activeSelection == index && root.isSelected == true) ? "#fefe00" : "#ffffff"
}
// Option state
Text {
x: menuLeft + stateColumnX
y: 4
text: root.inBluetoothMenu ? root.bluetoothDisplayState(modelData) : (modelData.getState ? modelData.getState() : "")
font.family: "8bitoperator JVE"
font.pixelSize: 71
font.letterSpacing: 1
renderType: Text.NativeRendering
font.hintingPreference: Font.PreferNoHinting
smooth: false
antialiasing: false
color: (root.activeSelection == index && root.isSelected == true) ? "#fefe00" : "#ffffff"
}
}
}
/* ------------------------------
INPUT HANDLING
------------------------------ */
function handleKey(key) {
switch (key) {
case Qt.Key_Up:
if (root.inBluetoothMenu || root.isSelected === false)
activeSelection = wrapIndex(activeSelection - 1);
return true;
case Qt.Key_Down:
if (root.inBluetoothMenu || root.isSelected === false)
activeSelection = wrapIndex(activeSelection + 1);
return true;
case Qt.Key_Left:
{
const a = currentAction();
if (a && a.arr && root.isSelected === true)
a.arr(-1);
return true;
}
case Qt.Key_Right:
{
const a = currentAction();
if (a && a.arr && root.isSelected === true)
a.arr(1);
return true;
}
case Qt.Key_Z:
case Qt.Key_Return:
case Qt.Key_Enter:
{
if (root.inBluetoothMenu) {
const device = bluetoothDeviceAt(activeSelection);
console.log(root.inBluetoothMenu, activeSelection, device);
if (device) {
if (!device.connected) {
device.connect();
} else {
device.disconnect();
}
// device.connected = !device.connected;
}
} else {
const a = currentAction();
if (a && a.ent) {
a.ent();
} else {
root.isSelected = !root.isSelected;
}
}
return true;
}
case Qt.Key_X:
case Qt.Key_Shift:
case Qt.Key_Escape:
if (root.inBluetoothMenu) {
root.inBluetoothMenu = false;
root.isSelected = false;
root.activeSelection = root.bluetoothActionIndex;
root.clampSelection();
} else if (root.isSelected === true) {
root.isSelected = false;
} else {
if (manager && manager.closeQuickSettings) {
manager.closeQuickSettings();
}
}
return true;
}
return false;
}
Component.onCompleted: {
root.activeSelection = 0;
root.isSelected = false;
root.inBluetoothMenu = false;
ShellInputManager.registerHandler("quickSettings", handleKey);
}
Component.onDestruction: {
ShellInputManager.unregisterHandler("quickSettings");
}
}