mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
498 lines
13 KiB
QML
498 lines
13 KiB
QML
import QtQuick
|
|
import QtQuick.Effects
|
|
import Quickshell
|
|
import Quickshell.Wayland
|
|
import qs.Commons
|
|
import qs.Services.UI
|
|
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)
|
|
}
|
|
|
|
// 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: 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) && (PanelService.openedPanel.screen === screen)
|
|
property bool isPanelClosing: (PanelService.openedPanel !== null) && PanelService.openedPanel.isClosing
|
|
|
|
color: {
|
|
if (dimDesktop && isPanelOpen) {
|
|
return Qt.alpha(Color.mShadow, 0.8)
|
|
}
|
|
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 && !isPanelClosing ? root.width : 0
|
|
height: root.isPanelOpen && !isPanelClosing ? root.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
|
|
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
|
onClicked: mouse => {
|
|
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)
|
|
}
|
|
}
|
|
|
|
BatteryPanel {
|
|
id: batteryPanel
|
|
screen: root.screen
|
|
z: 50
|
|
|
|
Component.onCompleted: {
|
|
objectName = "batteryPanel-" + (screen?.name || "unknown")
|
|
PanelService.registerPanel(batteryPanel)
|
|
}
|
|
}
|
|
|
|
BluetoothPanel {
|
|
id: bluetoothPanel
|
|
screen: root.screen
|
|
z: 50
|
|
|
|
Component.onCompleted: {
|
|
objectName = "bluetoothPanel-" + (screen?.name || "unknown")
|
|
PanelService.registerPanel(bluetoothPanel)
|
|
}
|
|
}
|
|
|
|
ControlCenterPanel {
|
|
id: controlCenterPanel
|
|
screen: root.screen
|
|
z: 50
|
|
|
|
Component.onCompleted: {
|
|
objectName = "controlCenterPanel-" + (screen?.name || "unknown")
|
|
PanelService.registerPanel(controlCenterPanel)
|
|
}
|
|
}
|
|
|
|
CalendarPanel {
|
|
id: calendarPanel
|
|
screen: root.screen
|
|
z: 50
|
|
|
|
Component.onCompleted: {
|
|
objectName = "calendarPanel-" + (screen?.name || "unknown")
|
|
PanelService.registerPanel(calendarPanel)
|
|
}
|
|
}
|
|
|
|
Launcher {
|
|
id: launcherPanel
|
|
screen: root.screen
|
|
z: 50
|
|
|
|
Component.onCompleted: {
|
|
objectName = "launcherPanel-" + (screen?.name || "unknown")
|
|
PanelService.registerPanel(launcherPanel)
|
|
}
|
|
}
|
|
|
|
NotificationHistoryPanel {
|
|
id: notificationHistoryPanel
|
|
screen: root.screen
|
|
z: 50
|
|
|
|
Component.onCompleted: {
|
|
objectName = "notificationHistoryPanel-" + (screen?.name || "unknown")
|
|
PanelService.registerPanel(notificationHistoryPanel)
|
|
}
|
|
}
|
|
|
|
SessionMenu {
|
|
id: sessionMenuPanel
|
|
screen: root.screen
|
|
z: 50
|
|
|
|
Component.onCompleted: {
|
|
objectName = "sessionMenuPanel-" + (screen?.name || "unknown")
|
|
PanelService.registerPanel(sessionMenuPanel)
|
|
}
|
|
}
|
|
|
|
SettingsPanel {
|
|
id: settingsPanel
|
|
screen: root.screen
|
|
z: 50
|
|
|
|
Component.onCompleted: {
|
|
objectName = "settingsPanel-" + (screen?.name || "unknown")
|
|
PanelService.registerPanel(settingsPanel)
|
|
}
|
|
}
|
|
|
|
SetupWizard {
|
|
id: setupWizardPanel
|
|
screen: root.screen
|
|
z: 50
|
|
|
|
Component.onCompleted: {
|
|
objectName = "setupWizardPanel-" + (screen?.name || "unknown")
|
|
PanelService.registerPanel(setupWizardPanel)
|
|
}
|
|
}
|
|
|
|
TrayDrawerPanel {
|
|
id: trayDrawerPanel
|
|
screen: root.screen
|
|
z: 50
|
|
|
|
Component.onCompleted: {
|
|
objectName = "trayDrawerPanel-" + (screen?.name || "unknown")
|
|
PanelService.registerPanel(trayDrawerPanel)
|
|
}
|
|
}
|
|
|
|
TrayMenuPanel {
|
|
id: trayMenuPanel
|
|
screen: root.screen
|
|
z: 50
|
|
|
|
Component.onCompleted: {
|
|
objectName = "trayMenuPanel-" + (screen?.name || "unknown")
|
|
PanelService.registerPanel(trayMenuPanel)
|
|
}
|
|
}
|
|
|
|
WallpaperPanel {
|
|
id: wallpaperPanel
|
|
screen: root.screen
|
|
z: 50
|
|
|
|
Component.onCompleted: {
|
|
objectName = "wallpaperPanel-" + (screen?.name || "unknown")
|
|
PanelService.registerPanel(wallpaperPanel)
|
|
}
|
|
}
|
|
|
|
WiFiPanel {
|
|
id: wifiPanel
|
|
screen: root.screen
|
|
z: 50
|
|
|
|
Component.onCompleted: {
|
|
objectName = "wifiPanel-" + (screen?.name || "unknown")
|
|
PanelService.registerPanel(wifiPanel)
|
|
}
|
|
}
|
|
|
|
// 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: {
|
|
if (item) {
|
|
Logger.d("MainScreen", "Bar loaded with screen", item.screen?.name)
|
|
// Bind screen to bar component (use binding for reactivity)
|
|
item.screen = Qt.binding(function () {
|
|
return barLoader.screen
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Screen Corners
|
|
*/
|
|
ScreenCorners {}
|
|
}
|
|
|
|
// 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()
|
|
}
|
|
}
|
|
}
|
|
}
|