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

View File

@@ -226,7 +226,7 @@
"audio": {
"volumeStep": 5,
"volumeOverdrive": false,
"cavaFrameRate": 60,
"cavaFrameRate": 30,
"visualizerType": "linear",
"visualizerQuality": "high",
"mprisBlacklist": [],

View File

@@ -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
}

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
// Disabled when bar is hidden or not configured for this screen
Loader {

View File

@@ -56,6 +56,7 @@ Item {
BarBackground {
bar: root.bar
shapeContainer: backgroundsShape
windowRoot: root.windowRoot
}

View File

@@ -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

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
}
}
}
}

View File

@@ -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)
}
}

View File

@@ -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 {}
}
}