133 lines
3.4 KiB
QML
133 lines
3.4 KiB
QML
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Services.Pipewire
|
|
import qs.Common
|
|
import qs.Services
|
|
import qs.Widgets
|
|
|
|
FocusScope {
|
|
id: root
|
|
|
|
property var overlayRef: null
|
|
|
|
readonly property var daemon: overlayRef ? overlayRef.daemon : null
|
|
readonly property var pluginData: (daemon && daemon.pluginData) ? daemon.pluginData : ({})
|
|
readonly property var targetScreen: overlayRef ? overlayRef.effectiveScreen : null
|
|
readonly property real edgeMargin: Theme.spacingM
|
|
readonly property real topLimit: topBar.y + topBar.height + Theme.spacingM
|
|
|
|
property int windowZCounter: 1
|
|
|
|
function nextWindowZ() {
|
|
root.windowZCounter = root.windowZCounter + 1;
|
|
return root.windowZCounter;
|
|
}
|
|
|
|
function requestClose() {
|
|
if (overlayRef)
|
|
overlayRef.requestClose();
|
|
}
|
|
|
|
function isWidgetVisible(widgetId) {
|
|
return daemon ? daemon.isWidgetVisible(widgetId) : false;
|
|
}
|
|
|
|
function toggleWidget(widgetId) {
|
|
if (daemon)
|
|
daemon.toggleWidget(widgetId);
|
|
}
|
|
|
|
function widgetPosition(widgetId) {
|
|
if (daemon)
|
|
return daemon.widgetPosition(root.targetScreen, widgetId);
|
|
|
|
return {
|
|
"x": 0.1,
|
|
"y": 0.15
|
|
};
|
|
}
|
|
|
|
function saveWidgetPosition(widgetId, fractionX, fractionY) {
|
|
if (daemon)
|
|
daemon.saveWidgetPosition(root.targetScreen, widgetId, fractionX, fractionY);
|
|
}
|
|
|
|
anchors.fill: parent
|
|
focus: true
|
|
|
|
// The modal's own Escape handler sits on a sibling focus scope, so the key
|
|
// never reaches it once this content holds focus - handle it here instead.
|
|
Keys.onEscapePressed: event => {
|
|
root.requestClose();
|
|
event.accepted = true;
|
|
}
|
|
|
|
Component.onCompleted: root.forceActiveFocus()
|
|
|
|
// Keeps the default sink/source properties live while the overlay is open.
|
|
PwObjectTracker {
|
|
objects: [AudioService.sink, AudioService.source].filter(node => !!node)
|
|
}
|
|
|
|
// Clicking empty space closes, like clicking outside a dialog.
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onClicked: root.requestClose()
|
|
}
|
|
|
|
GameBarTopBar {
|
|
id: topBar
|
|
|
|
panel: root
|
|
anchors.top: parent.top
|
|
anchors.topMargin: Theme.spacingL
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
}
|
|
|
|
Repeater {
|
|
model: root.daemon ? root.daemon.widgetDefs : []
|
|
|
|
delegate: GameBarWindow {
|
|
required property var modelData
|
|
|
|
panel: root
|
|
widgetId: modelData.id
|
|
title: modelData.title
|
|
icon: modelData.icon
|
|
widgetWidth: modelData.width
|
|
widgetHeight: modelData.height
|
|
minimumY: root.topLimit
|
|
shown: root.isWidgetVisible(modelData.id)
|
|
contentComponent: {
|
|
switch (modelData.id) {
|
|
case "audio":
|
|
return audioWidgetComponent;
|
|
case "apps":
|
|
return appsWidgetComponent;
|
|
case "system":
|
|
return systemWidgetComponent;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: audioWidgetComponent
|
|
|
|
GameBarAudioWidget {}
|
|
}
|
|
|
|
Component {
|
|
id: appsWidgetComponent
|
|
|
|
GameBarAppMixer {}
|
|
}
|
|
|
|
Component {
|
|
id: systemWidgetComponent
|
|
|
|
GameBarSystemWidget {}
|
|
}
|
|
}
|