MainScreen: Bar content is in its own PanelWindow

This commit is contained in:
ItsLemmy
2025-11-09 11:25:06 -05:00
parent d9ca5cdd3a
commit 15539e1445
8 changed files with 221 additions and 34 deletions
+1 -1
View File
@@ -226,7 +226,7 @@
"audio": { "audio": {
"volumeStep": 5, "volumeStep": 5,
"volumeOverdrive": false, "volumeOverdrive": false,
"cavaFrameRate": 60, "cavaFrameRate": 30,
"visualizerType": "linear", "visualizerType": "linear",
"visualizerQuality": "high", "visualizerQuality": "high",
"mprisBlacklist": [], "mprisBlacklist": [],
+4 -8
View File
@@ -30,9 +30,6 @@ Item {
readonly property real barMarginH: barFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0 readonly property real barMarginH: barFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
readonly property real barMarginV: barFloating ? Settings.data.bar.marginVertical * 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) // Fill the parent (the Loader)
anchors.fill: parent anchors.fill: parent
@@ -68,29 +65,28 @@ Item {
id: bar id: bar
// Position and size the bar based on orientation and floating margins // Position and size the bar based on orientation and floating margins
// Extend the bar by attachmentOverlap to eliminate hairline gap
x: { x: {
var baseX = (root.barPosition === "right") ? (parent.width - Style.barHeight - root.barMarginH) : root.barMarginH var baseX = (root.barPosition === "right") ? (parent.width - Style.barHeight - root.barMarginH) : root.barMarginH
if (root.barPosition === "right") if (root.barPosition === "right")
return baseX - root.attachmentOverlap // Extend left towards panels return baseX // Extend left towards panels
return baseX return baseX
} }
y: { y: {
var baseY = (root.barPosition === "bottom") ? (parent.height - Style.barHeight - root.barMarginV) : root.barMarginV var baseY = (root.barPosition === "bottom") ? (parent.height - Style.barHeight - root.barMarginV) : root.barMarginV
if (root.barPosition === "bottom") if (root.barPosition === "bottom")
return baseY - root.attachmentOverlap // Extend up towards panels return baseY // Extend up towards panels
return baseY return baseY
} }
width: { width: {
var baseWidth = root.barIsVertical ? Style.barHeight : (parent.width - root.barMarginH * 2) var baseWidth = root.barIsVertical ? Style.barHeight : (parent.width - root.barMarginH * 2)
if (!root.barIsVertical) if (!root.barIsVertical)
return baseWidth // Horizontal bars extend via height, not width return baseWidth // Horizontal bars extend via height, not width
return baseWidth + root.attachmentOverlap + 1 return baseWidth + 1
} }
height: { height: {
var baseHeight = root.barIsVertical ? (parent.height - root.barMarginV * 2) : Style.barHeight var baseHeight = root.barIsVertical ? (parent.height - root.barMarginV * 2) : Style.barHeight
if (!root.barIsVertical) if (!root.barIsVertical)
return baseHeight + root.attachmentOverlap return baseHeight
return baseHeight // Vertical bars extend via width, not height return baseHeight // Vertical bars extend via width, not height
} }
+19
View File
@@ -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 // BarExclusionZone - created after MainScreen has fully loaded
// Disabled when bar is hidden or not configured for this screen // Disabled when bar is hidden or not configured for this screen
Loader { Loader {
@@ -56,6 +56,7 @@ Item {
BarBackground { BarBackground {
bar: root.bar bar: root.bar
shapeContainer: backgroundsShape shapeContainer: backgroundsShape
windowRoot: root.windowRoot
} }
@@ -1,6 +1,7 @@
import QtQuick import QtQuick
import QtQuick.Shapes import QtQuick.Shapes
import qs.Commons import qs.Commons
import qs.Services.UI
import qs.Modules.MainScreen.Backgrounds import qs.Modules.MainScreen.Backgrounds
@@ -25,6 +26,24 @@ ShapePath {
// Required reference to AllBackgrounds shapeContainer // Required reference to AllBackgrounds shapeContainer
required property var 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) // Corner radius (from Style)
readonly property real radius: Style.radiusL readonly property real radius: Style.radiusL
@@ -64,7 +83,7 @@ ShapePath {
// ShapePath configuration // ShapePath configuration
strokeWidth: -1 // No stroke, fill only 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) // Starting position (top-left corner, after the arc)
// Use mapped coordinates relative to the Shape container // Use mapped coordinates relative to the Shape container
+72
View File
@@ -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
}
}
}
}
+102 -23
View File
@@ -101,12 +101,11 @@ PanelWindow {
// Bar region - subtract bar area from mask // Bar region - subtract bar area from mask
Region { Region {
id: barMaskRegion id: barMaskRegion
property var barRegion: barLoader.item?.barRegion
x: barRegion?.x ?? 0 x: barPlaceholder.x
y: barRegion?.y ?? 0 y: barPlaceholder.y
width: barRegion?.width ?? 0 width: barPlaceholder.width
height: barRegion?.height ?? 0 height: barPlaceholder.height
intersection: Intersection.Subtract intersection: Intersection.Subtract
} }
@@ -133,7 +132,7 @@ PanelWindow {
Backgrounds.AllBackgrounds { Backgrounds.AllBackgrounds {
id: unifiedBackgrounds id: unifiedBackgrounds
anchors.fill: parent anchors.fill: parent
bar: barLoader.item?.barItem || null bar: barPlaceholder.barItem || null
windowRoot: root windowRoot: root
z: 0 // Behind all content z: 0 // Behind all content
} }
@@ -309,28 +308,108 @@ PanelWindow {
} }
} }
// Bar (always on top - rendered last in tree, so naturally on top) // Bar placeholder - just for background positioning (actual bar content is in BarContentWindow)
Loader { Item {
id: barLoader id: barPlaceholder
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 // Expose self as barItem for AllBackgrounds compatibility
anchors.fill: parent readonly property var barItem: barPlaceholder
// Screen reference
property ShellScreen screen: root.screen property ShellScreen screen: root.screen
onLoaded: { // Bar positioning properties (match Bar.qml logic)
if (item) { readonly property string barPosition: Settings.data.bar.position || "top"
Logger.d("MainScreen", "Bar loaded with screen", item.screen?.name) readonly property bool barIsVertical: barPosition === "left" || barPosition === "right"
// Bind screen to bar component (use binding for reactivity) readonly property bool barFloating: Settings.data.bar.floating || false
item.screen = Qt.binding(function () { readonly property real barMarginH: barFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
return barLoader.screen 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)
} }
} }
+2 -1
View File
@@ -23,6 +23,7 @@ import qs.Services.UI
// Modules // Modules
import qs.Modules.Background import qs.Modules.Background
import qs.Modules.Bar
import qs.Modules.Dock import qs.Modules.Dock
import qs.Modules.MainScreen import qs.Modules.MainScreen
import qs.Modules.LockScreen import qs.Modules.LockScreen
@@ -106,7 +107,7 @@ ShellRoot {
// Item that needs to exists in the shell. // Item that needs to exists in the shell.
IPCService {} IPCService {}
// MainScreen for each screen (manages bar + all panels) // MainScreen for each screen
AllScreens {} AllScreens {}
} }
} }