mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2025-12-05 22:26:16 +00:00
- All cards now live in Modules/Cards - CalendarPanel is now called ClockPanel - Added a way to ease settings migration in separate QML files
589 lines
17 KiB
QML
589 lines
17 KiB
QML
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Effects
|
|
import Quickshell
|
|
import Quickshell.Wayland
|
|
import "Backgrounds" as Backgrounds
|
|
|
|
import qs.Commons
|
|
|
|
// All panels
|
|
import qs.Modules.Bar
|
|
import qs.Modules.Bar.Extras
|
|
import qs.Modules.Panels.Audio
|
|
import qs.Modules.Panels.Battery
|
|
import qs.Modules.Panels.Bluetooth
|
|
import qs.Modules.Panels.Brightness
|
|
import qs.Modules.Panels.Changelog
|
|
import qs.Modules.Panels.Clock
|
|
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
|
|
import qs.Services.Compositor
|
|
import qs.Services.UI
|
|
|
|
/**
|
|
* 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 brightnessPanel: brightnessPanel
|
|
readonly property alias clockPanel: clockPanel
|
|
readonly property alias changelogPanel: changelogPanel
|
|
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 wallpaperPanel: wallpaperPanel
|
|
readonly property alias wifiPanel: wifiPanel
|
|
|
|
// Expose panel backgrounds for AllBackgrounds
|
|
readonly property var audioPanelPlaceholder: audioPanel.panelRegion
|
|
readonly property var batteryPanelPlaceholder: batteryPanel.panelRegion
|
|
readonly property var bluetoothPanelPlaceholder: bluetoothPanel.panelRegion
|
|
readonly property var brightnessPanelPlaceholder: brightnessPanel.panelRegion
|
|
readonly property var clockPanelPlaceholder: clockPanel.panelRegion
|
|
readonly property var changelogPanelPlaceholder: changelogPanel.panelRegion
|
|
readonly property var controlCenterPanelPlaceholder: controlCenterPanel.panelRegion
|
|
readonly property var launcherPanelPlaceholder: launcherPanel.panelRegion
|
|
readonly property var notificationHistoryPanelPlaceholder: notificationHistoryPanel.panelRegion
|
|
readonly property var sessionMenuPanelPlaceholder: sessionMenuPanel.panelRegion
|
|
readonly property var settingsPanelPlaceholder: settingsPanel.panelRegion
|
|
readonly property var setupWizardPanelPlaceholder: setupWizardPanel.panelRegion
|
|
readonly property var trayDrawerPanelPlaceholder: trayDrawerPanel.panelRegion
|
|
readonly property var wallpaperPanelPlaceholder: wallpaperPanel.panelRegion
|
|
readonly property var wifiPanelPlaceholder: wifiPanel.panelRegion
|
|
|
|
Component.onCompleted: {
|
|
Logger.d("MainScreen", "Initialized for screen:", screen?.name, "- Dimensions:", screen?.width, "x", screen?.height, "- Position:", screen?.x, ",", screen?.y);
|
|
}
|
|
|
|
// Wayland
|
|
WlrLayershell.layer: WlrLayer.Top
|
|
WlrLayershell.namespace: "noctalia-background-" + (screen?.name || "unknown")
|
|
WlrLayershell.exclusionMode: ExclusionMode.Ignore // Don't reserve space - BarExclusionZone handles that
|
|
WlrLayershell.keyboardFocus: {
|
|
if (!root.isPanelOpen) {
|
|
return WlrKeyboardFocus.None;
|
|
}
|
|
return PanelService.openedPanel.exclusiveKeyboard ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.OnDemand;
|
|
}
|
|
|
|
anchors {
|
|
top: true
|
|
bottom: true
|
|
left: true
|
|
right: true
|
|
}
|
|
|
|
// Desktop dimming when panels are open
|
|
property real dimmerOpacity: Settings.data.general.dimmerOpacity ?? 0.8
|
|
property bool isPanelOpen: (PanelService.openedPanel !== null) && (PanelService.openedPanel.screen === screen)
|
|
property bool isPanelClosing: (PanelService.openedPanel !== null) && PanelService.openedPanel.isClosing
|
|
|
|
color: {
|
|
if (dimmerOpacity > 0 && isPanelOpen && !isPanelClosing) {
|
|
return Qt.alpha(Color.mShadow, dimmerOpacity);
|
|
}
|
|
return Color.transparent;
|
|
}
|
|
|
|
Behavior on color {
|
|
ColorAnimation {
|
|
duration: isPanelClosing ? Style.animationFaster : Style.animationNormal
|
|
easing.type: Easing.OutQuad
|
|
}
|
|
}
|
|
|
|
// Check if bar should be visible on this screen
|
|
readonly property bool barShouldShow: {
|
|
// Check global bar visibility
|
|
if (!BarService.isVisible)
|
|
return false;
|
|
|
|
// Check screen-specific configuration
|
|
var monitors = Settings.data.bar.monitors || [];
|
|
var screenName = screen?.name || "";
|
|
|
|
// If no monitors specified, show on all screens
|
|
// If monitors specified, only show if this screen is in the list
|
|
return monitors.length === 0 || monitors.includes(screenName);
|
|
}
|
|
|
|
// Make everything click-through except bar
|
|
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
|
|
|
|
// Only include regions that are actually needed
|
|
// panelRegions is handled by PanelService, bar is local to this screen
|
|
regions: [barMaskRegion, backgroundMaskRegion]
|
|
|
|
// Bar region - subtract bar area from mask (only if bar should be shown on this screen)
|
|
Region {
|
|
id: barMaskRegion
|
|
|
|
x: barPlaceholder.x
|
|
y: barPlaceholder.y
|
|
|
|
// Set width/height to 0 if bar shouldn't show on this screen (makes region empty)
|
|
width: root.barShouldShow ? barPlaceholder.width : 0
|
|
height: root.barShouldShow ? barPlaceholder.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: barPlaceholder.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
|
|
objectName: "audioPanel-" + (root.screen?.name || "unknown")
|
|
screen: root.screen
|
|
z: 50
|
|
}
|
|
|
|
BatteryPanel {
|
|
id: batteryPanel
|
|
objectName: "batteryPanel-" + (root.screen?.name || "unknown")
|
|
screen: root.screen
|
|
z: 50
|
|
}
|
|
|
|
BluetoothPanel {
|
|
id: bluetoothPanel
|
|
objectName: "bluetoothPanel-" + (root.screen?.name || "unknown")
|
|
screen: root.screen
|
|
z: 50
|
|
}
|
|
|
|
BrightnessPanel {
|
|
id: brightnessPanel
|
|
objectName: "brightnessPanel-" + (root.screen?.name || "unknown")
|
|
screen: root.screen
|
|
z: 50
|
|
}
|
|
|
|
ControlCenterPanel {
|
|
id: controlCenterPanel
|
|
objectName: "controlCenterPanel-" + (root.screen?.name || "unknown")
|
|
screen: root.screen
|
|
z: 50
|
|
}
|
|
|
|
ChangelogPanel {
|
|
id: changelogPanel
|
|
objectName: "changelogPanel-" + (root.screen?.name || "unknown")
|
|
screen: root.screen
|
|
z: 50
|
|
}
|
|
|
|
ClockPanel {
|
|
id: clockPanel
|
|
objectName: "clockPanel-" + (root.screen?.name || "unknown")
|
|
screen: root.screen
|
|
z: 50
|
|
}
|
|
|
|
Launcher {
|
|
id: launcherPanel
|
|
objectName: "launcherPanel-" + (root.screen?.name || "unknown")
|
|
screen: root.screen
|
|
z: 50
|
|
}
|
|
|
|
NotificationHistoryPanel {
|
|
id: notificationHistoryPanel
|
|
objectName: "notificationHistoryPanel-" + (root.screen?.name || "unknown")
|
|
screen: root.screen
|
|
z: 50
|
|
}
|
|
|
|
SessionMenu {
|
|
id: sessionMenuPanel
|
|
objectName: "sessionMenuPanel-" + (root.screen?.name || "unknown")
|
|
screen: root.screen
|
|
z: 50
|
|
}
|
|
|
|
SettingsPanel {
|
|
id: settingsPanel
|
|
objectName: "settingsPanel-" + (root.screen?.name || "unknown")
|
|
screen: root.screen
|
|
z: 50
|
|
}
|
|
|
|
SetupWizard {
|
|
id: setupWizardPanel
|
|
objectName: "setupWizardPanel-" + (root.screen?.name || "unknown")
|
|
screen: root.screen
|
|
z: 50
|
|
}
|
|
|
|
TrayDrawerPanel {
|
|
id: trayDrawerPanel
|
|
objectName: "trayDrawerPanel-" + (root.screen?.name || "unknown")
|
|
screen: root.screen
|
|
z: 50
|
|
}
|
|
|
|
WallpaperPanel {
|
|
id: wallpaperPanel
|
|
objectName: "wallpaperPanel-" + (root.screen?.name || "unknown")
|
|
screen: root.screen
|
|
z: 50
|
|
}
|
|
|
|
WiFiPanel {
|
|
id: wifiPanel
|
|
objectName: "wifiPanel-" + (root.screen?.name || "unknown")
|
|
screen: root.screen
|
|
z: 50
|
|
}
|
|
|
|
// ----------------------------------------------
|
|
// Bar background placeholder - just for background positioning (actual bar content is in BarContentWindow)
|
|
Item {
|
|
id: barPlaceholder
|
|
|
|
// Expose self as barItem for AllBackgrounds compatibility
|
|
readonly property var barItem: barPlaceholder
|
|
|
|
// Screen reference
|
|
property ShellScreen screen: root.screen
|
|
|
|
// Bar background positioning properties
|
|
readonly property string barPosition: Settings.data.bar.position || "top"
|
|
readonly property bool barIsVertical: barPosition === "left" || barPosition === "right"
|
|
readonly property bool barFloating: Settings.data.bar.floating || false
|
|
readonly property real barMarginH: barFloating ? Math.round(Settings.data.bar.marginHorizontal * Style.marginXL) : 0
|
|
readonly property real barMarginV: barFloating ? Math.round(Settings.data.bar.marginVertical * Style.marginXL) : 0
|
|
readonly property real attachmentOverlap: 1 // Attachment overlap to fix hairline gap with fractional scaling
|
|
|
|
// Expose bar dimensions directly on this Item for BarBackground
|
|
// Use screen dimensions directly
|
|
x: {
|
|
if (barPosition === "right")
|
|
return screen.width - Style.barHeight - barMarginH - attachmentOverlap; // Extend left towards panels
|
|
return barMarginH;
|
|
}
|
|
y: {
|
|
if (barPosition === "bottom")
|
|
return screen.height - Style.barHeight - barMarginV - attachmentOverlap;
|
|
return barMarginV;
|
|
}
|
|
width: {
|
|
if (barIsVertical) {
|
|
return Style.barHeight + attachmentOverlap;
|
|
}
|
|
return screen.width - barMarginH * 2;
|
|
}
|
|
height: {
|
|
if (barIsVertical) {
|
|
return screen.height - barMarginV * 2;
|
|
}
|
|
return Style.barHeight + attachmentOverlap;
|
|
}
|
|
|
|
// Corner states (same as Bar.qml)
|
|
readonly property int topLeftCornerState: {
|
|
if (barFloating)
|
|
return 0;
|
|
if (barPosition === "top")
|
|
return -1;
|
|
if (barPosition === "left")
|
|
return -1;
|
|
if (Settings.data.bar.outerCorners && (barPosition === "bottom" || barPosition === "right")) {
|
|
return barIsVertical ? 1 : 2;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
readonly property int topRightCornerState: {
|
|
if (barFloating)
|
|
return 0;
|
|
if (barPosition === "top")
|
|
return -1;
|
|
if (barPosition === "right")
|
|
return -1;
|
|
if (Settings.data.bar.outerCorners && (barPosition === "bottom" || barPosition === "left")) {
|
|
return barIsVertical ? 1 : 2;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
readonly property int bottomLeftCornerState: {
|
|
if (barFloating)
|
|
return 0;
|
|
if (barPosition === "bottom")
|
|
return -1;
|
|
if (barPosition === "left")
|
|
return -1;
|
|
if (Settings.data.bar.outerCorners && (barPosition === "top" || barPosition === "right")) {
|
|
return barIsVertical ? 1 : 2;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
readonly property int bottomRightCornerState: {
|
|
if (barFloating)
|
|
return 0;
|
|
if (barPosition === "bottom")
|
|
return -1;
|
|
if (barPosition === "right")
|
|
return -1;
|
|
if (Settings.data.bar.outerCorners && (barPosition === "top" || barPosition === "left")) {
|
|
return barIsVertical ? 1 : 2;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Screen Corners
|
|
*/
|
|
ScreenCorners {}
|
|
}
|
|
|
|
// ========================================
|
|
// Centralized Keyboard Shortcuts
|
|
// ========================================
|
|
// These shortcuts delegate to the opened panel's handler functions
|
|
// Panels can implement: onEscapePressed, onTabPressed, onShiftTabPressed,
|
|
// onUpPressed, onDownPressed, onReturnPressed
|
|
|
|
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: "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();
|
|
}
|
|
}
|
|
}
|
|
|
|
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: "Backtab"
|
|
enabled: root.isPanelOpen
|
|
onActivated: {
|
|
if (PanelService.openedPanel && PanelService.openedPanel.onBackTabPressed) {
|
|
PanelService.openedPanel.onBackTabPressed();
|
|
}
|
|
}
|
|
}
|
|
|
|
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: "Ctrl+N"
|
|
enabled: root.isPanelOpen
|
|
onActivated: {
|
|
if (PanelService.openedPanel && PanelService.openedPanel.onCtrlNPressed) {
|
|
PanelService.openedPanel.onCtrlNPressed();
|
|
}
|
|
}
|
|
}
|
|
|
|
Shortcut {
|
|
sequence: "Ctrl+P"
|
|
enabled: root.isPanelOpen
|
|
onActivated: {
|
|
if (PanelService.openedPanel && PanelService.openedPanel.onCtrlPPressed) {
|
|
PanelService.openedPanel.onCtrlPPressed();
|
|
}
|
|
}
|
|
}
|
|
}
|