mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
208 lines
6.4 KiB
QML
208 lines
6.4 KiB
QML
import QtQuick
|
|
import QtQuick.Effects
|
|
import Quickshell
|
|
import Quickshell.Wayland
|
|
import qs.Commons
|
|
import qs.Services.Compositor
|
|
import qs.Services.UI
|
|
|
|
Loader {
|
|
active: CompositorService.isNiri && Settings.data.wallpaper.enabled && Settings.data.wallpaper.overviewEnabled
|
|
|
|
sourceComponent: Variants {
|
|
model: Quickshell.screens
|
|
|
|
delegate: PanelWindow {
|
|
id: panelWindow
|
|
|
|
required property ShellScreen modelData
|
|
property string wallpaper: ""
|
|
property string preprocessedWallpaper: "" // Pre-resized wallpaper from Background.qml for blur optimization
|
|
property string cachedWallpaper: ""
|
|
property string pendingWallpaper: ""
|
|
property bool isSolidColor: false
|
|
property bool useQtBlur: false // Fallback when ImageMagick not available
|
|
property color solidColor: Settings.data.wallpaper.solidColor
|
|
// Watch the actual tint color value, not just darkMode setting, since colors reload asynchronously
|
|
property color tintColor: Settings.data.colorSchemes.darkMode ? Color.mSurface : Color.mOnSurface
|
|
|
|
Component.onCompleted: {
|
|
if (modelData) {
|
|
Logger.d("Overview", "Loading overview for Niri on", modelData.name);
|
|
}
|
|
setWallpaperInitial();
|
|
}
|
|
|
|
Component.onDestruction: {
|
|
bgImage.source = "";
|
|
}
|
|
|
|
// External state management - wait for wallpaper processing to complete
|
|
// before starting blur, to avoid CPU contention on slower systems
|
|
Connections {
|
|
target: WallpaperService
|
|
function onWallpaperProcessingComplete(screenName, path, cachedPath) {
|
|
if (screenName === modelData.name) {
|
|
// Use pre-resized image for blur optimization (avoids re-reading/resizing 4K images)
|
|
preprocessedWallpaper = cachedPath || "";
|
|
wallpaper = path;
|
|
}
|
|
}
|
|
}
|
|
|
|
function setWallpaperInitial() {
|
|
// Overview now receives the initial wallpaper via wallpaperProcessingComplete signal
|
|
// from Background.qml, ensuring blur only starts after main wallpaper is processed.
|
|
// No direct wallpaper assignment needed here.
|
|
}
|
|
|
|
function requestBlurredOverview() {
|
|
requestBlurredOverviewWithTint(tintColor.toString(), Settings.data.colorSchemes.darkMode);
|
|
}
|
|
|
|
function requestBlurredOverviewWithTint(tint, isDarkMode) {
|
|
if (!wallpaper || isSolidColor)
|
|
return;
|
|
|
|
const compositorScale = CompositorService.getDisplayScale(modelData.name);
|
|
const targetWidth = Math.round(modelData.width * compositorScale);
|
|
const targetHeight = Math.round(modelData.height * compositorScale);
|
|
|
|
// Start fade out, then request new image
|
|
if (cachedWallpaper) {
|
|
fadeOutAnim.start();
|
|
}
|
|
|
|
// Use pre-resized image if available (optimization: avoids re-reading/resizing large images)
|
|
const sourceImage = preprocessedWallpaper || wallpaper;
|
|
|
|
ImageCacheService.getBlurredOverview(sourceImage, targetWidth, targetHeight, tint, isDarkMode, function (path, success) {
|
|
if (path) {
|
|
useQtBlur = !success; // Use Qt blur fallback if ImageMagick failed
|
|
pendingWallpaper = path;
|
|
// If fade out is done or wasn't needed, apply immediately
|
|
if (!fadeOutAnim.running) {
|
|
applyPendingWallpaper();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function applyPendingWallpaper() {
|
|
if (pendingWallpaper) {
|
|
cachedWallpaper = pendingWallpaper;
|
|
pendingWallpaper = "";
|
|
fadeInAnim.start();
|
|
}
|
|
}
|
|
|
|
// Request cached wallpaper when source changes
|
|
onWallpaperChanged: {
|
|
if (!wallpaper)
|
|
return;
|
|
|
|
// Check if this is a solid color path
|
|
if (WallpaperService.isSolidColorPath(wallpaper)) {
|
|
isSolidColor = true;
|
|
var colorStr = WallpaperService.getSolidColor(wallpaper);
|
|
solidColor = colorStr;
|
|
cachedWallpaper = "";
|
|
return;
|
|
}
|
|
|
|
isSolidColor = false;
|
|
requestBlurredOverview();
|
|
}
|
|
|
|
// Watch for color reloads - use the actual Color properties directly to avoid stale values
|
|
Connections {
|
|
target: Color
|
|
function onMSurfaceChanged() {
|
|
if (!isSolidColor && wallpaper && Settings.data.colorSchemes.darkMode) {
|
|
requestBlurredOverviewWithTint(Color.mSurface.toString(), true);
|
|
}
|
|
}
|
|
function onMOnSurfaceChanged() {
|
|
if (!isSolidColor && wallpaper && !Settings.data.colorSchemes.darkMode) {
|
|
requestBlurredOverviewWithTint(Color.mOnSurface.toString(), false);
|
|
}
|
|
}
|
|
}
|
|
|
|
color: "transparent"
|
|
screen: modelData
|
|
WlrLayershell.layer: WlrLayer.Background
|
|
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
|
WlrLayershell.namespace: "noctalia-overview-" + (screen?.name || "unknown")
|
|
|
|
anchors {
|
|
top: true
|
|
bottom: true
|
|
right: true
|
|
left: true
|
|
}
|
|
|
|
// Solid color background
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
visible: isSolidColor
|
|
color: solidColor
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
color: tintColor
|
|
}
|
|
}
|
|
|
|
// Image background (pre-blurred and tinted by ImageMagick, or Qt fallback)
|
|
Image {
|
|
id: bgImage
|
|
anchors.fill: parent
|
|
visible: !isSolidColor
|
|
fillMode: Image.PreserveAspectCrop
|
|
source: cachedWallpaper
|
|
smooth: true
|
|
mipmap: false
|
|
cache: false
|
|
asynchronous: true
|
|
|
|
// Qt blur fallback when ImageMagick not available
|
|
layer.enabled: useQtBlur
|
|
layer.smooth: false
|
|
layer.effect: MultiEffect {
|
|
blurEnabled: true
|
|
blur: 1.0
|
|
blurMax: 32
|
|
}
|
|
|
|
// Tint overlay for Qt blur fallback
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
visible: useQtBlur
|
|
color: tintColor
|
|
opacity: 0.6
|
|
}
|
|
|
|
NumberAnimation on opacity {
|
|
id: fadeOutAnim
|
|
running: false
|
|
from: 1
|
|
to: 0
|
|
duration: 200
|
|
easing.type: Easing.OutQuad
|
|
onFinished: applyPendingWallpaper()
|
|
}
|
|
|
|
NumberAnimation on opacity {
|
|
id: fadeInAnim
|
|
running: false
|
|
from: 0
|
|
to: 1
|
|
duration: 200
|
|
easing.type: Easing.InQuad
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|