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.Dock import qs.Modules.Panels.Launcher import qs.Modules.Panels.Media import qs.Modules.Panels.Network import qs.Modules.Panels.NotificationHistory import qs.Modules.Panels.Plugins import qs.Modules.Panels.SessionMenu import qs.Modules.Panels.Settings import qs.Modules.Panels.SetupWizard import qs.Modules.Panels.SystemStats import qs.Modules.Panels.Tray import qs.Modules.Panels.Wallpaper import qs.Services.Compositor import qs.Services.Power import qs.Services.UI /** * MainScreen - Single PanelWindow per screen that manages all panels and the bar */ PanelWindow { id: root Component.onCompleted: { Logger.d("MainScreen", "Initialized for screen:", screen?.name, "- Dimensions:", screen?.width, "x", screen?.height, "- Position:", screen?.x, ",", screen?.y); } // Wayland WlrLayershell.namespace: "noctalia-background-" + (screen?.name || "unknown") WlrLayershell.exclusionMode: ExclusionMode.Ignore // Don't reserve space - BarExclusionZone handles that WlrLayershell.keyboardFocus: { // No panel open anywhere: no keyboard focus needed if (!root.isAnyPanelOpen) { return WlrKeyboardFocus.None; } // Panel open on THIS screen: use panel's preferred focus mode if (root.isPanelOpen) { // Hyprland's Exclusive captures ALL input globally (including pointer), // preventing click-to-close from working on other monitors. // Workaround: briefly use Exclusive when panel opens (for text input focus), // then switch to OnDemand (for click-to-close on other screens). if (CompositorService.isHyprland) { return PanelService.isInitializingKeyboard ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.OnDemand; } return PanelService.openedPanel.exclusiveKeyboard ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.OnDemand; } // Panel open on ANOTHER screen: OnDemand allows receiving pointer events for click-to-close return WlrKeyboardFocus.OnDemand; } anchors { top: isFramed || _needsFullscreen || _barPosition !== "bottom" bottom: isFramed || _needsFullscreen || _barPosition !== "top" left: isFramed || _needsFullscreen || _barPosition !== "right" right: isFramed || _needsFullscreen || _barPosition !== "left" } // Implicit sizes control the non-anchored dimension when collapsed (3 anchors). // Layout: [margin] [bar] [max(margin, shadow)] — screen-edge side uses margin, inner side uses whichever is larger. // When fullscreen (4 anchors), compositor ignores these. implicitWidth: Math.ceil(barPlaceholder.barMarginH + barPlaceholder.barHeight + _innerPaddingH) implicitHeight: Math.ceil(barPlaceholder.barMarginV + barPlaceholder.barHeight + _innerPaddingV) // 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 property bool isAnyPanelOpen: PanelService.openedPanel !== null // Dynamic fullscreen management — collapse to bar-sized when idle to avoid fullscreen compositor damage readonly property bool isFramed: Settings.data.bar.barType === "framed" readonly property string _barPosition: Settings.getBarPositionForScreen(screen?.name) readonly property bool _barIsVertical: _barPosition === "left" || _barPosition === "right" property bool _needsFullscreen: isAnyPanelOpen || PanelService.closingPanel !== null || _dimmerAnimating property bool _dimmerAnimating: false // Shadow padding for collapsed window — shadow extends beyond bar into the screen readonly property real _shadowPadding: (Settings.data.general.enableShadows && !PowerProfileService.noctaliaPerformanceMode) ? Style.shadowBlurMax + Math.max(Math.abs(Style.shadowHorizontalOffset), Math.abs(Style.shadowVerticalOffset)) : 0 // Inner padding: the side facing into the screen needs at least shadow clearance readonly property real _innerPaddingH: Math.max(barPlaceholder.barMarginH, _shadowPadding) readonly property real _innerPaddingV: Math.max(barPlaceholder.barMarginV, _shadowPadding) color: { if (dimmerOpacity > 0 && isPanelOpen && !isPanelClosing) { return Qt.alpha(Color.mShadow, dimmerOpacity); } return "transparent"; } Behavior on color { enabled: !PanelService.closedImmediately ColorAnimation { duration: isPanelClosing ? Style.animationFaster : Style.animationNormal easing.type: Easing.OutQuad onRunningChanged: root._dimmerAnimating = running } } // Reset closedImmediately flag after color change is applied onColorChanged: { if (PanelService.closedImmediately) { PanelService.closedImmediately = false; } } // Check if bar should be visible on this screen readonly property bool barShouldShow: { // Check global bar visibility (includes overview state) if (!BarService.effectivelyVisible) 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 readonly property bool isFramed: Settings.data.bar.barType === "framed" readonly property real barThickness: Style.barHeight readonly property real frameThickness: Settings.data.bar.frameThickness ?? 12 readonly property string barPos: Settings.data.bar.position || "top" // Bar / Frame Mask Region { // Mode: Simple or Floating x: barPlaceholder.x y: barPlaceholder.y width: (!barMaskRegion.isFramed && root.barShouldShow) ? barPlaceholder.width : 0 height: (!barMaskRegion.isFramed && root.barShouldShow) ? barPlaceholder.height : 0 intersection: Intersection.Subtract } // Mode: Framed - 4 sides Region { // Top side Region { x: 0 y: 0 width: (barMaskRegion.isFramed && root.barShouldShow) ? root.width : 0 height: (barMaskRegion.isFramed && root.barShouldShow) ? (barMaskRegion.barPos === "top" ? barMaskRegion.barThickness : barMaskRegion.frameThickness) : 0 intersection: Intersection.Subtract } // Bottom side Region { x: 0 y: (barMaskRegion.isFramed && root.barShouldShow) ? (root.height - (barMaskRegion.barPos === "bottom" ? barMaskRegion.barThickness : barMaskRegion.frameThickness)) : 0 width: (barMaskRegion.isFramed && root.barShouldShow) ? root.width : 0 height: (barMaskRegion.isFramed && root.barShouldShow) ? (barMaskRegion.barPos === "bottom" ? barMaskRegion.barThickness : barMaskRegion.frameThickness) : 0 intersection: Intersection.Subtract } // Left side Region { x: 0 y: 0 width: (barMaskRegion.isFramed && root.barShouldShow) ? (barMaskRegion.barPos === "left" ? barMaskRegion.barThickness : barMaskRegion.frameThickness) : 0 height: (barMaskRegion.isFramed && root.barShouldShow) ? root.height : 0 intersection: Intersection.Subtract } // Right side Region { x: (barMaskRegion.isFramed && root.barShouldShow) ? (root.width - (barMaskRegion.barPos === "right" ? barMaskRegion.barThickness : barMaskRegion.frameThickness)) : 0 width: (barMaskRegion.isFramed && root.barShouldShow) ? (barMaskRegion.barPos === "right" ? barMaskRegion.barThickness : barMaskRegion.frameThickness) : 0 height: (barMaskRegion.isFramed && root.barShouldShow) ? root.height : 0 intersection: Intersection.Subtract } } } // Background region for click-to-close - reactive sizing // Uses isAnyPanelOpen so clicking on any screen's background closes the panel Region { id: backgroundMaskRegion x: 0 y: 0 width: root.isAnyPanelOpen ? root.width : 0 height: root.isAnyPanelOpen ? root.height : 0 intersection: Intersection.Subtract } } // Blur behind the bar and open panels — attached to PanelWindow (required by BackgroundEffect API) BackgroundEffect.blurRegion: Settings.data.general.enableBlurBehind ? blurRegion : null Region { id: blurRegion // ── Non-framed bar (simple/floating): single rectangle with bar corner states ── Region { x: (!barPlaceholder.isFramed && root.barShouldShow && !barPlaceholder.isHidden) ? barPlaceholder.x : 0 y: (!barPlaceholder.isFramed && root.barShouldShow && !barPlaceholder.isHidden) ? barPlaceholder.y : 0 width: (!barPlaceholder.isFramed && root.barShouldShow && !barPlaceholder.isHidden) ? barPlaceholder.width : 0 height: (!barPlaceholder.isFramed && root.barShouldShow && !barPlaceholder.isHidden) ? barPlaceholder.height : 0 radius: Style.radiusL topLeftCorner: barPlaceholder.topLeftCornerState topRightCorner: barPlaceholder.topRightCornerState bottomLeftCorner: barPlaceholder.bottomLeftCornerState bottomRightCorner: barPlaceholder.bottomRightCornerState } // ── Framed bar: full screen minus rounded hole ── Region { x: 0 y: 0 width: (barPlaceholder.isFramed && root.barShouldShow && !barPlaceholder.isHidden) ? root.width : 0 height: (barPlaceholder.isFramed && root.barShouldShow && !barPlaceholder.isHidden) ? root.height : 0 Region { intersection: Intersection.Subtract x: backgroundBlur.frameHoleX y: backgroundBlur.frameHoleY width: backgroundBlur.frameHoleX2 - backgroundBlur.frameHoleX height: backgroundBlur.frameHoleY2 - backgroundBlur.frameHoleY radius: backgroundBlur.frameR } } // ── Panel blur regions ── // Opening panel Region { x: backgroundBlur.panelBg ? Math.round(backgroundBlur.panelBg.x) : 0 y: backgroundBlur.panelBg ? Math.round(backgroundBlur.panelBg.y) : 0 width: backgroundBlur.panelBg ? Math.round(backgroundBlur.panelBg.width) : 0 height: backgroundBlur.panelBg ? Math.round(backgroundBlur.panelBg.height) : 0 radius: Style.radiusL topLeftCorner: backgroundBlur.panelBg ? backgroundBlur.panelBg.topLeftCornerState : CornerState.Normal topRightCorner: backgroundBlur.panelBg ? backgroundBlur.panelBg.topRightCornerState : CornerState.Normal bottomLeftCorner: backgroundBlur.panelBg ? backgroundBlur.panelBg.bottomLeftCornerState : CornerState.Normal bottomRightCorner: backgroundBlur.panelBg ? backgroundBlur.panelBg.bottomRightCornerState : CornerState.Normal } // Closing panel (coexists with opening panel during transition) Region { x: backgroundBlur.closingPanelBg ? Math.round(backgroundBlur.closingPanelBg.x) : 0 y: backgroundBlur.closingPanelBg ? Math.round(backgroundBlur.closingPanelBg.y) : 0 width: backgroundBlur.closingPanelBg ? Math.round(backgroundBlur.closingPanelBg.width) : 0 height: backgroundBlur.closingPanelBg ? Math.round(backgroundBlur.closingPanelBg.height) : 0 radius: Style.radiusL topLeftCorner: backgroundBlur.closingPanelBg ? backgroundBlur.closingPanelBg.topLeftCornerState : CornerState.Normal topRightCorner: backgroundBlur.closingPanelBg ? backgroundBlur.closingPanelBg.topRightCornerState : CornerState.Normal bottomLeftCorner: backgroundBlur.closingPanelBg ? backgroundBlur.closingPanelBg.bottomLeftCornerState : CornerState.Normal bottomRightCorner: backgroundBlur.closingPanelBg ? backgroundBlur.closingPanelBg.bottomRightCornerState : CornerState.Normal } } // -------------------------------------- // 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 // Uses isAnyPanelOpen so clicking on any screen's background closes the panel MouseArea { anchors.fill: parent enabled: root.isAnyPanelOpen 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 } MediaPlayerPanel { id: mediaPlayerPanel objectName: "mediaPlayerPanel-" + (root.screen?.name || "unknown") screen: root.screen } BatteryPanel { id: batteryPanel objectName: "batteryPanel-" + (root.screen?.name || "unknown") screen: root.screen } BluetoothPanel { id: bluetoothPanel objectName: "bluetoothPanel-" + (root.screen?.name || "unknown") screen: root.screen } BrightnessPanel { id: brightnessPanel objectName: "brightnessPanel-" + (root.screen?.name || "unknown") screen: root.screen } ControlCenterPanel { id: controlCenterPanel objectName: "controlCenterPanel-" + (root.screen?.name || "unknown") screen: root.screen } ChangelogPanel { id: changelogPanel objectName: "changelogPanel-" + (root.screen?.name || "unknown") screen: root.screen } ClockPanel { id: clockPanel objectName: "clockPanel-" + (root.screen?.name || "unknown") screen: root.screen } Launcher { id: launcherPanel objectName: "launcherPanel-" + (root.screen?.name || "unknown") screen: root.screen } NotificationHistoryPanel { id: notificationHistoryPanel objectName: "notificationHistoryPanel-" + (root.screen?.name || "unknown") screen: root.screen } SessionMenu { id: sessionMenuPanel objectName: "sessionMenuPanel-" + (root.screen?.name || "unknown") screen: root.screen } SettingsPanel { id: settingsPanel objectName: "settingsPanel-" + (root.screen?.name || "unknown") screen: root.screen } SetupWizard { id: setupWizardPanel objectName: "setupWizardPanel-" + (root.screen?.name || "unknown") screen: root.screen } TrayDrawerPanel { id: trayDrawerPanel objectName: "trayDrawerPanel-" + (root.screen?.name || "unknown") screen: root.screen } WallpaperPanel { id: wallpaperPanel objectName: "wallpaperPanel-" + (root.screen?.name || "unknown") screen: root.screen } NetworkPanel { id: networkPanel objectName: "networkPanel-" + (root.screen?.name || "unknown") screen: root.screen } SystemStatsPanel { id: systemStatsPanel objectName: "systemStatsPanel-" + (root.screen?.name || "unknown") screen: root.screen } StaticDockPanel { id: staticDockPanel objectName: "staticDockPanel-" + (root.screen?.name || "unknown") screen: root.screen } // ---------------------------------------------- // Plugin panel slots // ---------------------------------------------- PluginPanelSlot { id: pluginPanel1 objectName: "pluginPanel1-" + (root.screen?.name || "unknown") screen: root.screen slotNumber: 1 } PluginPanelSlot { id: pluginPanel2 objectName: "pluginPanel2-" + (root.screen?.name || "unknown") screen: root.screen slotNumber: 2 } // ---------------------------------------------- // 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 (per-screen) readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name) readonly property bool barIsVertical: barPosition === "left" || barPosition === "right" readonly property bool isFramed: Settings.data.bar.barType === "framed" readonly property real frameThickness: Settings.data.bar.frameThickness ?? 12 readonly property bool barFloating: Settings.data.bar.floating || false readonly property real barMarginH: barFloating ? Math.floor(Settings.data.bar.marginHorizontal) : 0 readonly property real barMarginV: barFloating ? Math.floor(Settings.data.bar.marginVertical) : 0 readonly property real barHeight: Style.getBarHeightForScreen(screen?.name) // Auto-hide properties (read by AllBackgrounds for background fade) readonly property bool autoHide: Settings.getBarDisplayModeForScreen(screen?.name) === "auto_hide" property bool isHidden: autoHide Connections { target: BarService function onBarAutoHideStateChanged(screenName, hidden) { if (screenName === barPlaceholder.screen?.name) { barPlaceholder.isHidden = hidden; } } } // Expose bar dimensions directly on this Item for BarBackground // Use screen dimensions directly x: { if (!root._needsFullscreen && !root.isFramed) { // Collapsed: bar is at margin from screen edge, shadow extends inward. // For right bar the window faces left (inner side first), so bar starts after shadow clearance. return barPosition === "right" ? root._innerPaddingH : barMarginH; } if (barPosition === "right") return (screen?.width ?? 0) - barHeight - barMarginH; if (isFramed && !barIsVertical) return frameThickness; return barMarginH; } y: { if (!root._needsFullscreen && !root.isFramed) { return barPosition === "bottom" ? root._innerPaddingV : barMarginV; } if (barPosition === "bottom") return (screen?.height ?? 0) - barHeight - barMarginV; if (isFramed && barIsVertical) return frameThickness; return barMarginV; } width: { if (barIsVertical) { return barHeight; } if (isFramed) return (screen?.width ?? 0) - frameThickness * 2; return (screen?.width ?? 0) - barMarginH * 2; } height: { if (!barIsVertical) { return barHeight; } if (isFramed) return (screen?.height ?? 0) - frameThickness * 2; return (screen?.height ?? 0) - barMarginV * 2; } // 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 {} // Blur behind the bar and open panels // Helper object holding computed properties for blur regions QtObject { id: backgroundBlur // Panel background geometry (from the currently open panel on this screen) readonly property var panelBg: { var op = PanelService.openedPanel; if (!op || op.screen !== root.screen || op.blurEnabled === false) return null; var region = op.panelRegion; return (region && region.visible) ? region.panelItem : null; } // Panel background geometry for the closing panel (may coexist with panelBg) readonly property var closingPanelBg: { var cp = PanelService.closingPanel; if (!cp || cp.screen !== root.screen || cp.blurEnabled === false) return null; var region = cp.panelRegion; return (region && region.visible) ? region.panelItem : null; } // Framed bar: inner hole boundary (where the hole begins on each axis) // These are the x/y coordinates of the 4 inner hole corners readonly property real frameHoleX: barPlaceholder.barPosition === "left" ? barPlaceholder.barHeight : barPlaceholder.frameThickness readonly property real frameHoleY: barPlaceholder.barPosition === "top" ? barPlaceholder.barHeight : barPlaceholder.frameThickness readonly property real frameHoleX2: root.width - (barPlaceholder.barPosition === "right" ? barPlaceholder.barHeight : barPlaceholder.frameThickness) readonly property real frameHoleY2: root.height - (barPlaceholder.barPosition === "bottom" ? barPlaceholder.barHeight : barPlaceholder.frameThickness) readonly property real frameR: Settings.data.bar.frameRadius ?? 20 } // Native idle inhibitor — one per active MainScreen window. // Multiple inhibitors bound to the same enabled state are harmless; // having one per screen is more robust than picking a "primary" screen. IdleInhibitor { window: root enabled: IdleInhibitorService.isInhibited Component.onCompleted: { IdleInhibitorService.nativeInhibitorAvailable = true; Logger.d("IdleInhibitor", "Native IdleInhibitor active on screen:", root.screen?.name); } } } // Centralized Keyboard Shortcuts // These shortcuts delegate to the opened panel's handler functions // Panels can implement: onEscapePressed, onTabPressed, onBackTabPressed, // onUpPressed, onDownPressed, onReturnPressed, etc... Instantiator { model: Settings.data.general.keybinds.keyEscape || [] Shortcut { sequence: modelData enabled: root.isPanelOpen && (PanelService.openedPanel.onEscapePressed !== undefined) && !PanelService.isKeybindRecording onActivated: PanelService.openedPanel.onEscapePressed() } } Shortcut { sequence: "Tab" enabled: root.isPanelOpen && (PanelService.openedPanel.onTabPressed !== undefined) onActivated: PanelService.openedPanel.onTabPressed() } Shortcut { sequence: "Backtab" enabled: root.isPanelOpen && (PanelService.openedPanel.onBackTabPressed !== undefined) onActivated: PanelService.openedPanel.onBackTabPressed() } Instantiator { model: Settings.data.general.keybinds.keyUp || [] Shortcut { sequence: modelData enabled: root.isPanelOpen && (PanelService.openedPanel.onUpPressed !== undefined) && !PanelService.isKeybindRecording onActivated: PanelService.openedPanel.onUpPressed() } } Instantiator { model: Settings.data.general.keybinds.keyDown || [] Shortcut { sequence: modelData enabled: root.isPanelOpen && (PanelService.openedPanel.onDownPressed !== undefined) && !PanelService.isKeybindRecording onActivated: PanelService.openedPanel.onDownPressed() } } Instantiator { model: Settings.data.general.keybinds.keyEnter || [] Shortcut { sequence: modelData enabled: root.isPanelOpen && (PanelService.openedPanel.onEnterPressed !== undefined) && !PanelService.isKeybindRecording onActivated: PanelService.openedPanel.onEnterPressed() } } Instantiator { model: Settings.data.general.keybinds.keyLeft || [] Shortcut { sequence: modelData enabled: root.isPanelOpen && (PanelService.openedPanel.onLeftPressed !== undefined) && !PanelService.isKeybindRecording onActivated: PanelService.openedPanel.onLeftPressed() } } Instantiator { model: Settings.data.general.keybinds.keyRight || [] Shortcut { sequence: modelData enabled: root.isPanelOpen && (PanelService.openedPanel.onRightPressed !== undefined) && !PanelService.isKeybindRecording onActivated: PanelService.openedPanel.onRightPressed() } } Shortcut { sequence: "Home" enabled: root.isPanelOpen && (PanelService.openedPanel.onHomePressed !== undefined) onActivated: PanelService.openedPanel.onHomePressed() } Shortcut { sequence: "End" enabled: root.isPanelOpen && (PanelService.openedPanel.onEndPressed !== undefined) onActivated: PanelService.openedPanel.onEndPressed() } Shortcut { sequence: "PgUp" enabled: root.isPanelOpen && (PanelService.openedPanel.onPageUpPressed !== undefined) onActivated: PanelService.openedPanel.onPageUpPressed() } Shortcut { sequence: "PgDown" enabled: root.isPanelOpen && (PanelService.openedPanel.onPageDownPressed !== undefined) onActivated: PanelService.openedPanel.onPageDownPressed() } }