mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2026-06-08 12:52:24 +00:00
MainScreen: Bar content is in its own PanelWindow
This commit is contained in:
@@ -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
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user