import QtQuick import QtQuick.Effects import Quickshell import Quickshell.Wayland import qs.Commons import qs.Services import "Backgrounds" as Backgrounds // All panels import qs.Modules.Bar import qs.Modules.Panels.Audio import qs.Modules.Panels.Battery import qs.Modules.Panels.Bluetooth import qs.Modules.Panels.Calendar import qs.Modules.Panels.ControlCenter import qs.Modules.Panels.Launcher import qs.Modules.Panels.NotificationHistory import qs.Modules.Panels.SessionMenu import qs.Modules.Panels.Settings import qs.Modules.Panels.SetupWizard import qs.Modules.Panels.Tray import qs.Modules.Panels.Wallpaper import qs.Modules.Panels.WiFi /** * MainScreen - Single PanelWindow per screen that manages all panels and the bar */ PanelWindow { id: root // Expose panels as readonly property aliases readonly property alias audioPanel: audioPanel readonly property alias batteryPanel: batteryPanel readonly property alias bluetoothPanel: bluetoothPanel readonly property alias calendarPanel: calendarPanel readonly property alias controlCenterPanel: controlCenterPanel readonly property alias launcherPanel: launcherPanel readonly property alias notificationHistoryPanel: notificationHistoryPanel readonly property alias sessionMenuPanel: sessionMenuPanel readonly property alias settingsPanel: settingsPanel readonly property alias setupWizardPanel: setupWizardPanel readonly property alias trayDrawerPanel: trayDrawerPanel readonly property alias trayMenuPanel: trayMenuPanel readonly property alias wallpaperPanel: wallpaperPanel readonly property alias wifiPanel: wifiPanel Component.onCompleted: { Logger.d("MainScreen", "Initialized for screen:", screen?.name, "- Dimensions:", screen?.width, "x", screen?.height, "- Position:", screen?.x, ",", screen?.y) } // Debug: Log mask region changes onMaskChanged: { Logger.d("MainScreen", "Mask changed!") Logger.d("MainScreen", " Bar region:", barLoader.item?.barRegion) Logger.d("MainScreen", " Panel count:", panelsRepeater.count) for (var i = 0; i < panelsRepeater.count; i++) { var panelItem = panelsRepeater.itemAt(i)?.item Logger.d("MainScreen", " Panel", i, "- open:", panelItem?.isPanelOpen, "- region:", panelItem?.panelRegion) } } // Wayland // Always use Exclusive keyboard focus when a panel is open // This ensures all keyboard shortcuts work reliably (Escape, etc.) // The centralized shortcuts in this window handle delegation to panels WlrLayershell.keyboardFocus: root.isPanelOpen ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None WlrLayershell.layer: Settings.data.ui.panelsOverlayLayer ? WlrLayer.Overlay : WlrLayer.Top WlrLayershell.namespace: "noctalia-screen-" + (screen?.name || "unknown") WlrLayershell.exclusionMode: ExclusionMode.Ignore // Don't reserve space - BarExclusionZone handles that anchors { top: true bottom: true left: true right: true } // Desktop dimming when panels are open property bool dimDesktop: Settings.data.general.dimDesktop property bool isPanelOpen: PanelService.openedPanel !== null color: { if (dimDesktop && isPanelOpen) { return Qt.alpha(Color.mSurfaceVariant, Style.opacityHeavy) } return Color.transparent } Behavior on color { ColorAnimation { duration: Style.animationNormal easing.type: Easing.OutQuad } } // Fully reactive mask system, make everything click-through except bar and open panels mask: Region { id: clickableMask // Cover entire window (everything is masked/click-through) x: 0 y: 0 width: root.width height: root.height intersection: Intersection.Xor // Direct binding to Variants.instances plus additional regions regions: panelRegions.instances.concat([barMaskRegion, backgroundMaskRegion]) // Bar region - subtract bar area from mask Region { id: barMaskRegion property var barRegion: barLoader.item?.barRegion x: barRegion?.x ?? 0 y: barRegion?.y ?? 0 width: barRegion?.width ?? 0 height: barRegion?.height ?? 0 intersection: Intersection.Subtract } // Background region for click-to-close - reactive sizing Region { id: backgroundMaskRegion x: 0 y: 0 width: root.isPanelOpen ? root.width : 0 height: root.isPanelOpen ? root.height : 0 intersection: Intersection.Subtract } } // Variants for panel regions Variants { id: panelRegions // Model is the list of open panels (filters out closed ones) model: [audioPanel, controlCenterPanel, calendarPanel].filter(function (p) { return p && p.isPanelOpen }) Region { required property var modelData property var panelBg: modelData.panelRegion // Direct bindings x: panelBg?.x ?? 0 y: panelBg?.y ?? 0 width: panelBg?.width ?? 0 height: panelBg?.height ?? 0 intersection: Intersection.Subtract } } // Container for all UI elements Item { id: container width: root.width height: root.height // Unified backgrounds container / unified shadow system // Renders all bar and panel backgrounds as ShapePaths within a single Shape // This allows the shadow effect to apply to all backgrounds in one render pass Backgrounds.AllBackgrounds { id: unifiedBackgrounds anchors.fill: parent bar: barLoader.item?.barItem || null windowRoot: root z: 0 // Behind all content } // Background MouseArea for closing panels when clicking outside // Active whenever a panel is open - the mask ensures it only receives clicks when panel is open MouseArea { anchors.fill: parent enabled: root.isPanelOpen onClicked: { if (PanelService.openedPanel) { PanelService.openedPanel.close() } } z: 0 // Behind panels and bar } // --------------------------------------- // All panels always exist // --------------------------------------- AudioPanel { id: audioPanel screen: root.screen z: 50 Component.onCompleted: { objectName = "audioPanel-" + (screen?.name || "unknown") PanelService.registerPanel(audioPanel) Logger.d("MainScreen", "Panel registered:", objectName, "on screen:", screen?.name) } } BatteryPanel { id: batteryPanel screen: root.screen z: 50 Component.onCompleted: { objectName = "batteryPanel-" + (screen?.name || "unknown") PanelService.registerPanel(batteryPanel) Logger.d("MainScreen", "Panel registered:", objectName, "on screen:", screen?.name) } } BluetoothPanel { id: bluetoothPanel screen: root.screen z: 50 Component.onCompleted: { objectName = "bluetoothPanel-" + (screen?.name || "unknown") PanelService.registerPanel(bluetoothPanel) Logger.d("MainScreen", "Panel registered:", objectName, "on screen:", screen?.name) } } ControlCenterPanel { id: controlCenterPanel screen: root.screen z: 50 Component.onCompleted: { objectName = "controlCenterPanel-" + (screen?.name || "unknown") PanelService.registerPanel(controlCenterPanel) Logger.d("MainScreen", "Panel registered:", objectName, "on screen:", screen?.name) } } CalendarPanel { id: calendarPanel screen: root.screen z: 50 Component.onCompleted: { objectName = "calendarPanel-" + (screen?.name || "unknown") PanelService.registerPanel(calendarPanel) Logger.d("MainScreen", "Panel registered:", objectName, "on screen:", screen?.name) } } Launcher { id: launcherPanel screen: root.screen z: 50 Component.onCompleted: { objectName = "launcherPanel-" + (screen?.name || "unknown") PanelService.registerPanel(launcherPanel) Logger.d("MainScreen", "Panel registered:", objectName, "on screen:", screen?.name) } } NotificationHistoryPanel { id: notificationHistoryPanel screen: root.screen z: 50 Component.onCompleted: { objectName = "notificationHistoryPanel-" + (screen?.name || "unknown") PanelService.registerPanel(notificationHistoryPanel) Logger.d("MainScreen", "Panel registered:", objectName, "on screen:", screen?.name) } } SessionMenu { id: sessionMenuPanel screen: root.screen z: 50 Component.onCompleted: { objectName = "sessionMenuPanel-" + (screen?.name || "unknown") PanelService.registerPanel(sessionMenuPanel) Logger.d("MainScreen", "Panel registered:", objectName, "on screen:", screen?.name) } } SettingsPanel { id: settingsPanel screen: root.screen z: 50 Component.onCompleted: { objectName = "settingsPanel-" + (screen?.name || "unknown") PanelService.registerPanel(settingsPanel) Logger.d("MainScreen", "Panel registered:", objectName, "on screen:", screen?.name) } } SetupWizard { id: setupWizardPanel screen: root.screen z: 50 Component.onCompleted: { objectName = "setupWizardPanel-" + (screen?.name || "unknown") PanelService.registerPanel(setupWizardPanel) Logger.d("MainScreen", "Panel registered:", objectName, "on screen:", screen?.name) } } TrayDropdownPanel { id: trayDrawerPanel screen: root.screen z: 50 Component.onCompleted: { objectName = "trayDrawerPanel-" + (screen?.name || "unknown") PanelService.registerPanel(trayDrawerPanel) Logger.d("MainScreen", "Panel registered:", objectName, "on screen:", screen?.name) } } TrayMenuPanel { id: trayMenuPanel screen: root.screen z: 50 Component.onCompleted: { objectName = "trayMenuPanel-" + (screen?.name || "unknown") PanelService.registerPanel(trayMenuPanel) Logger.d("MainScreen", "Panel registered:", objectName, "on screen:", screen?.name) } } WallpaperPanel { id: wallpaperPanel screen: root.screen z: 50 Component.onCompleted: { objectName = "wallpaperPanel-" + (screen?.name || "unknown") PanelService.registerPanel(wallpaperPanel) Logger.d("MainScreen", "Panel registered:", objectName, "on screen:", screen?.name) } } WiFiPanel { id: wifiPanel screen: root.screen z: 50 Component.onCompleted: { objectName = "wifiPanel-" + (screen?.name || "unknown") PanelService.registerPanel(wifiPanel) Logger.d("MainScreen", "Panel registered:", objectName, "on screen:", screen?.name) } } // Bar (always on top - rendered last in tree, so naturally on top) Loader { id: barLoader asynchronous: false sourceComponent: Bar {} // Keep bar loaded but hide it when BarService.isVisible is false // This allows panels to remain accessible via IPC visible: BarService.isVisible // Fill parent to provide dimensions for Bar to reference anchors.fill: parent property ShellScreen screen: root.screen onLoaded: { Logger.d("MainScreen", "Bar loaded:", item !== null) if (item) { Logger.d("MainScreen", "Bar screen", item.screen?.name, "size:", item.width, "x", item.height) // Bind screen to bar component (use binding for reactivity) item.screen = Qt.binding(function () { return barLoader.screen }) } } } } // Centralized keyboard shortcuts - delegate to opened panel // This ensures shortcuts work regardless of panel focus state Shortcut { sequence: "Escape" enabled: root.isPanelOpen onActivated: { if (PanelService.openedPanel && PanelService.openedPanel.onEscapePressed) { PanelService.openedPanel.onEscapePressed() } else if (PanelService.openedPanel) { PanelService.openedPanel.close() } } } Shortcut { sequence: "Tab" enabled: root.isPanelOpen onActivated: { if (PanelService.openedPanel && PanelService.openedPanel.onTabPressed) { PanelService.openedPanel.onTabPressed() } } } Shortcut { sequence: "Shift+Tab" enabled: root.isPanelOpen onActivated: { if (PanelService.openedPanel && PanelService.openedPanel.onShiftTabPressed) { PanelService.openedPanel.onShiftTabPressed() } } } Shortcut { sequence: "Up" enabled: root.isPanelOpen onActivated: { if (PanelService.openedPanel && PanelService.openedPanel.onUpPressed) { PanelService.openedPanel.onUpPressed() } } } Shortcut { sequence: "Down" enabled: root.isPanelOpen onActivated: { if (PanelService.openedPanel && PanelService.openedPanel.onDownPressed) { PanelService.openedPanel.onDownPressed() } } } Shortcut { sequence: "Return" enabled: root.isPanelOpen onActivated: { if (PanelService.openedPanel && PanelService.openedPanel.onReturnPressed) { PanelService.openedPanel.onReturnPressed() } } } Shortcut { sequence: "Enter" enabled: root.isPanelOpen onActivated: { if (PanelService.openedPanel && PanelService.openedPanel.onReturnPressed) { PanelService.openedPanel.onReturnPressed() } } } Shortcut { sequence: "Home" enabled: root.isPanelOpen onActivated: { if (PanelService.openedPanel && PanelService.openedPanel.onHomePressed) { PanelService.openedPanel.onHomePressed() } } } Shortcut { sequence: "End" enabled: root.isPanelOpen onActivated: { if (PanelService.openedPanel && PanelService.openedPanel.onEndPressed) { PanelService.openedPanel.onEndPressed() } } } Shortcut { sequence: "PgUp" enabled: root.isPanelOpen onActivated: { if (PanelService.openedPanel && PanelService.openedPanel.onPageUpPressed) { PanelService.openedPanel.onPageUpPressed() } } } Shortcut { sequence: "PgDown" enabled: root.isPanelOpen onActivated: { if (PanelService.openedPanel && PanelService.openedPanel.onPageDownPressed) { PanelService.openedPanel.onPageDownPressed() } } } Shortcut { sequence: "Ctrl+J" enabled: root.isPanelOpen onActivated: { if (PanelService.openedPanel && PanelService.openedPanel.onCtrlJPressed) { PanelService.openedPanel.onCtrlJPressed() } } } Shortcut { sequence: "Ctrl+K" enabled: root.isPanelOpen onActivated: { if (PanelService.openedPanel && PanelService.openedPanel.onCtrlKPressed) { PanelService.openedPanel.onCtrlKPressed() } } } Shortcut { sequence: "Left" enabled: root.isPanelOpen onActivated: { if (PanelService.openedPanel && PanelService.openedPanel.onLeftPressed) { PanelService.openedPanel.onLeftPressed() } } } Shortcut { sequence: "Right" enabled: root.isPanelOpen onActivated: { if (PanelService.openedPanel && PanelService.openedPanel.onRightPressed) { PanelService.openedPanel.onRightPressed() } } } }