diff --git a/Assets/settings-default.json b/Assets/settings-default.json index 70f521918..add3d975f 100644 --- a/Assets/settings-default.json +++ b/Assets/settings-default.json @@ -226,7 +226,7 @@ "audio": { "volumeStep": 5, "volumeOverdrive": false, - "cavaFrameRate": 60, + "cavaFrameRate": 30, "visualizerType": "linear", "visualizerQuality": "high", "mprisBlacklist": [], diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml index 2d472595a..f5cc180e7 100644 --- a/Modules/Bar/Bar.qml +++ b/Modules/Bar/Bar.qml @@ -30,9 +30,6 @@ Item { readonly property real barMarginH: barFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0 readonly property real barMarginV: barFloating ? Settings.data.bar.marginVertical * Style.marginXL : 0 - // Attachment overlap to fix hairline gap with fractional scaling - readonly property real attachmentOverlap: 1 - // Fill the parent (the Loader) anchors.fill: parent @@ -68,29 +65,28 @@ Item { id: bar // Position and size the bar based on orientation and floating margins - // Extend the bar by attachmentOverlap to eliminate hairline gap x: { var baseX = (root.barPosition === "right") ? (parent.width - Style.barHeight - root.barMarginH) : root.barMarginH if (root.barPosition === "right") - return baseX - root.attachmentOverlap // Extend left towards panels + return baseX // Extend left towards panels return baseX } y: { var baseY = (root.barPosition === "bottom") ? (parent.height - Style.barHeight - root.barMarginV) : root.barMarginV if (root.barPosition === "bottom") - return baseY - root.attachmentOverlap // Extend up towards panels + return baseY // Extend up towards panels return baseY } width: { var baseWidth = root.barIsVertical ? Style.barHeight : (parent.width - root.barMarginH * 2) if (!root.barIsVertical) return baseWidth // Horizontal bars extend via height, not width - return baseWidth + root.attachmentOverlap + 1 + return baseWidth + 1 } height: { var baseHeight = root.barIsVertical ? (parent.height - root.barMarginV * 2) : Style.barHeight if (!root.barIsVertical) - return baseHeight + root.attachmentOverlap + return baseHeight return baseHeight // Vertical bars extend via width, not height } diff --git a/Modules/MainScreen/AllScreens.qml b/Modules/MainScreen/AllScreens.qml index 9758790d6..7ae5a29c1 100644 --- a/Modules/MainScreen/AllScreens.qml +++ b/Modules/MainScreen/AllScreens.qml @@ -41,6 +41,25 @@ Variants { } } + // Bar content in separate windows to prevent fullscreen redraws + Loader { + active: { + if (!parent.windowLoaded || !parent.shouldBeActive || !BarService.isVisible) + return false + + // Check if bar is configured for this screen + var monitors = Settings.data.bar.monitors || [] + return monitors.length === 0 || monitors.includes(modelData?.name) + } + asynchronous: false + + sourceComponent: BarContentWindow {} + + onLoaded: { + Logger.d("Shell", "BarContentWindow created for", modelData?.name) + } + } + // BarExclusionZone - created after MainScreen has fully loaded // Disabled when bar is hidden or not configured for this screen Loader { diff --git a/Modules/MainScreen/Backgrounds/AllBackgrounds.qml b/Modules/MainScreen/Backgrounds/AllBackgrounds.qml index 7d953f3d5..e566aedbe 100644 --- a/Modules/MainScreen/Backgrounds/AllBackgrounds.qml +++ b/Modules/MainScreen/Backgrounds/AllBackgrounds.qml @@ -56,6 +56,7 @@ Item { BarBackground { bar: root.bar shapeContainer: backgroundsShape + windowRoot: root.windowRoot } diff --git a/Modules/MainScreen/Backgrounds/BarBackground.qml b/Modules/MainScreen/Backgrounds/BarBackground.qml index 9399ab125..e91ca8ccc 100644 --- a/Modules/MainScreen/Backgrounds/BarBackground.qml +++ b/Modules/MainScreen/Backgrounds/BarBackground.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Shapes import qs.Commons +import qs.Services.UI import qs.Modules.MainScreen.Backgrounds @@ -25,6 +26,24 @@ ShapePath { // Required reference to AllBackgrounds shapeContainer required property var shapeContainer + // Required reference to windowRoot for screen access + required property var windowRoot + + // Check if bar should be visible on this screen + readonly property bool shouldShowBar: { + // Check global bar visibility + if (!BarService.isVisible) + return false + + // Check screen-specific configuration + var monitors = Settings.data.bar.monitors || [] + var screenName = windowRoot?.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) + } + // Corner radius (from Style) readonly property real radius: Style.radiusL @@ -64,7 +83,7 @@ ShapePath { // ShapePath configuration strokeWidth: -1 // No stroke, fill only - fillColor: Qt.alpha(Color.mSurface, Settings.data.bar.backgroundOpacity) + fillColor: shouldShowBar ? Qt.alpha(Color.mSurface, Settings.data.bar.backgroundOpacity) : Color.transparent // Starting position (top-left corner, after the arc) // Use mapped coordinates relative to the Shape container diff --git a/Modules/MainScreen/BarContentWindow.qml b/Modules/MainScreen/BarContentWindow.qml new file mode 100644 index 000000000..9637a4103 --- /dev/null +++ b/Modules/MainScreen/BarContentWindow.qml @@ -0,0 +1,72 @@ +import QtQuick +import Quickshell +import Quickshell.Wayland +import qs.Commons +import qs.Services.UI +import qs.Modules.Bar + + +/** + * BarContentWindow - Separate transparent PanelWindow for bar content + * + * This window contains only the bar widgets (content), while the background + * is rendered in MainScreen's unified Shape system. This separation prevents + * fullscreen redraws when bar widgets redraw. + */ +Variants { + model: Quickshell.screens + + delegate: Loader { + id: barWindowLoader + + required property ShellScreen modelData + + // Only create window if bar should be visible on this screen + active: { + if (!modelData || !modelData.name) + return false + var monitors = Settings.data.bar.monitors || [] + return BarService.isVisible && (monitors.length === 0 || monitors.includes(modelData.name)) + } + + sourceComponent: PanelWindow { + id: barWindow + screen: modelData + + color: Color.transparent // Transparent - background is in MainScreen below + + Component.onCompleted: { + Logger.d("BarContentWindow", "Bar content window created for screen:", screen?.name) + } + + // Wayland layer configuration + WlrLayershell.namespace: "noctalia-bar-content-" + (screen?.name || "unknown") + WlrLayershell.layer: WlrLayer.Top + WlrLayershell.exclusionMode: ExclusionMode.Ignore // Don't reserve space - BarExclusionZone in MainScreen handles that + + // Position and size to match bar location + 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 ? Settings.data.bar.marginHorizontal * Style.marginXL : 0 + readonly property real barMarginV: barFloating ? Settings.data.bar.marginVertical * Style.marginXL : 0 + + // Anchor to the bar's edge + anchors { + top: barPosition === "top" || barIsVertical + bottom: barPosition === "bottom" || barIsVertical + left: barPosition === "left" || !barIsVertical + right: barPosition === "right" || !barIsVertical + } + // Set to FULL screen dimensions - margins will reduce the actual window size + implicitWidth: (barIsVertical ? (Style.barHeight + 1) : screen.width) + barMarginH + implicitHeight: (barIsVertical ? screen.height : Style.barHeight) + barMarginV + + // Bar content - just the widgets, no background + Bar { + anchors.fill: parent + screen: modelData + } + } + } +} diff --git a/Modules/MainScreen/MainScreen.qml b/Modules/MainScreen/MainScreen.qml index 1d6fa6655..c01b75531 100644 --- a/Modules/MainScreen/MainScreen.qml +++ b/Modules/MainScreen/MainScreen.qml @@ -101,12 +101,11 @@ PanelWindow { // 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 + x: barPlaceholder.x + y: barPlaceholder.y + width: barPlaceholder.width + height: barPlaceholder.height intersection: Intersection.Subtract } @@ -133,7 +132,7 @@ PanelWindow { Backgrounds.AllBackgrounds { id: unifiedBackgrounds anchors.fill: parent - bar: barLoader.item?.barItem || null + bar: barPlaceholder.barItem || null windowRoot: root z: 0 // Behind all content } @@ -309,28 +308,108 @@ PanelWindow { } } - // 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 + // Bar placeholder - just for background positioning (actual bar content is in BarContentWindow) + Item { + id: barPlaceholder - // Fill parent to provide dimensions for Bar to reference - anchors.fill: parent + // Expose self as barItem for AllBackgrounds compatibility + readonly property var barItem: barPlaceholder + // Screen reference 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 - }) + // Bar positioning properties (match Bar.qml logic) + 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 ? Settings.data.bar.marginHorizontal * Style.marginXL : 0 + readonly property real barMarginV: barFloating ? Settings.data.bar.marginVertical * Style.marginXL : 0 + + // Expose bar dimensions directly on this Item for BarBackground + // Use screen dimensions directly + x: { + if (barPosition === "right") + return screen.width - Style.barHeight - barMarginH + return barMarginH + } + y: { + if (barPosition === "bottom") + return screen.height - Style.barHeight - barMarginV + return barMarginV + } + width: { + if (barIsVertical) { + return Style.barHeight + 1 } + return screen.width - barMarginH * 2 + } + height: { + if (barIsVertical) { + return screen.height - barMarginV * 2 + } + return Style.barHeight + } + + // 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 + } + + Component.onCompleted: { + Logger.d("MainScreen", "===== Bar placeholder loaded =====") + Logger.d("MainScreen", " Screen:", screen?.name, "Size:", screen?.width, "x", screen?.height) + Logger.d("MainScreen", " Bar position:", barPosition, "| isVertical:", barIsVertical) + Logger.d("MainScreen", " Bar dimensions: x=" + x, "y=" + y, "width=" + width, "height=" + height) + Logger.d("MainScreen", " Style.barHeight =", Style.barHeight) + Logger.d("MainScreen", " Margins: H=" + barMarginH, "V=" + barMarginV, "| Floating:", barFloating) } } diff --git a/shell.qml b/shell.qml index f21e6d589..666d98840 100644 --- a/shell.qml +++ b/shell.qml @@ -23,6 +23,7 @@ import qs.Services.UI // Modules import qs.Modules.Background +import qs.Modules.Bar import qs.Modules.Dock import qs.Modules.MainScreen import qs.Modules.LockScreen @@ -106,7 +107,7 @@ ShellRoot { // Item that needs to exists in the shell. IPCService {} - // MainScreen for each screen (manages bar + all panels) + // MainScreen for each screen AllScreens {} } }