config/modules/home/services/quickshell/qml/Layout/Desktop.qml

319 lines
13 KiB
QML
Raw Normal View History

2025-07-22 20:21:21 -04:00
import QtQuick
import QtQuick.Shapes
import Quickshell
import Quickshell.Wayland
import Qt5Compat.GraphicalEffects
import "root:/Data" as Data
import "root:/Widgets/System" as System
import "root:/Core" as Core
import "root:/Widgets" as Widgets
import "root:/Widgets/Notifications" as Notifications
import "root:/Widgets/ControlPanel" as ControlPanel
// Desktop with borders and UI widgets
Scope {
id: desktop
property var shell
property var notificationService
// Wallpaper layer - one per screen
Variants {
model: Quickshell.screens
Core.Wallpaper {
required property var modelData
screen: modelData
}
}
// Desktop UI layer per screen
Variants {
model: Quickshell.screens
PanelWindow {
required property var modelData
screen: modelData
implicitWidth: Screen.width
implicitHeight: Screen.height
color: "transparent"
exclusiveZone: 0
WlrLayershell.namespace: "quickshell-desktop"
// Interactive mask for workspace indicator only
mask: Region {
item: workspaceIndicator
}
anchors {
top: true
left: true
bottom: true
right: true
}
// Workspace indicator at left border
System.NiriWorkspaces {
id: workspaceIndicator
anchors {
left: parent.left
verticalCenter: parent.verticalCenter
leftMargin: Data.Settings.borderWidth
}
z: 10
width: 32
}
// Volume OSD at right border (primary screen only)
System.OSD {
id: osd
shell: desktop.shell
visible: modelData === Quickshell.primaryScreen
anchors {
right: parent.right
verticalCenter: parent.verticalCenter
rightMargin: Data.Settings.borderWidth
}
z: 10
}
// Widget shadows (positioned behind border for proper layering)
// Workspace indicator shadow
Rectangle {
id: workspaceShadow
visible: workspaceIndicator !== null
x: workspaceIndicator.x
y: workspaceIndicator.y
width: workspaceIndicator.width
height: workspaceIndicator.height
color: "black"
radius: 16
z: -10 // Behind border
layer.enabled: true
layer.effect: DropShadow {
transparentBorder: true
horizontalOffset: 2
verticalOffset: 2
radius: 8 + (workspaceIndicator.effectsActive && Data.Settings.workspaceGlowEnabled ? Math.sin(workspaceIndicator.masterProgress * Math.PI) * 3 : 0)
samples: 17
color: {
if (!workspaceIndicator.effectsActive) return Qt.rgba(0, 0, 0, 0.3)
if (!Data.Settings.workspaceGlowEnabled) return Qt.rgba(0, 0, 0, 0.3)
// Use accent color glow with reduced intensity
const intensity = Math.sin(workspaceIndicator.masterProgress * Math.PI) * 0.3
return Qt.rgba(
workspaceIndicator.effectColor.r * intensity + 0.05,
workspaceIndicator.effectColor.g * intensity + 0.05,
workspaceIndicator.effectColor.b * intensity + 0.05,
0.3 + intensity * 0.15
)
}
cached: true
spread: 0.1 + (workspaceIndicator.effectsActive && Data.Settings.workspaceGlowEnabled ? Math.sin(workspaceIndicator.masterProgress * Math.PI) * 0.1 : 0)
}
}
// Clock widget shadow
Rectangle {
id: clockShadow
visible: clockWidget !== null
x: clockWidget.x
y: clockWidget.y
width: clockWidget.width
height: clockWidget.height
color: "black"
topLeftRadius: 0
topRightRadius: clockWidget.height / 2
bottomLeftRadius: 0
bottomRightRadius: 0
z: -10 // Behind border
layer.enabled: true
layer.effect: DropShadow {
transparentBorder: true
horizontalOffset: 1
verticalOffset: -1
radius: 8
samples: 17
color: Qt.rgba(0, 0, 0, 0.3)
cached: true
spread: 0.1
}
}
// Border background with shadow
Border {
id: screenBorder
anchors.fill: parent
workspaceIndicator: workspaceIndicator
volumeOSD: volumeOsd
clockWidget: clockWidget
z: -5 // Behind UI elements to prevent shadow from covering control panel
}
// Unified Wave Overlay - simple burst effect
Item {
id: waveOverlay
anchors.fill: parent
visible: workspaceIndicator.effectsActive && Data.Settings.workspaceBurstEnabled
z: 15
property real progress: workspaceIndicator.masterProgress
property color waveColor: workspaceIndicator.effectColor
// Workspace indicator burst effects
Item {
x: workspaceIndicator.x
y: workspaceIndicator.y
width: workspaceIndicator.width
height: workspaceIndicator.height
// Expanding pill burst - positioned at current workspace index (mimics pill shape)
Rectangle {
x: parent.width / 2 - width / 2
y: {
// Find current workspace index directly from currentWorkspace
let focusedIndex = 0
for (let i = 0; i < workspaceIndicator.workspaces.count; i++) {
const workspace = workspaceIndicator.workspaces.get(i)
if (workspace && workspace.id === workspaceIndicator.currentWorkspace) {
focusedIndex = i
break
}
}
// Calculate position accounting for Column centering and pill sizes
let cumulativeHeight = 0
for (let i = 0; i < focusedIndex; i++) {
const ws = workspaceIndicator.workspaces.get(i)
cumulativeHeight += (ws && ws.isFocused ? 36 : 22) + 6 // pill height + spacing
}
// Current pill height
const currentWs = workspaceIndicator.workspaces.get(focusedIndex)
const currentPillHeight = (currentWs && currentWs.isFocused ? 36 : 22)
// Column is centered, so start from center and calculate offset
const columnHeight = parent.height - 24 // Total available height minus padding
const columnTop = 12 // Top padding
return columnTop + cumulativeHeight + currentPillHeight / 2 - height / 2
}
width: 20 + waveOverlay.progress * 30
height: 36 + waveOverlay.progress * 20 // Pill-like height
radius: width / 2 // Pill-like rounded shape
color: "transparent"
border.width: 2
border.color: Qt.rgba(waveOverlay.waveColor.r, waveOverlay.waveColor.g, waveOverlay.waveColor.b, 1.0 - waveOverlay.progress)
opacity: Math.max(0, 1.0 - waveOverlay.progress)
}
// Secondary expanding pill burst - positioned at current workspace index
Rectangle {
x: parent.width / 2 - width / 2
y: {
// Find current workspace index directly from currentWorkspace
let focusedIndex = 0
for (let i = 0; i < workspaceIndicator.workspaces.count; i++) {
const workspace = workspaceIndicator.workspaces.get(i)
if (workspace && workspace.id === workspaceIndicator.currentWorkspace) {
focusedIndex = i
break
}
}
// Calculate position accounting for Column centering and pill sizes
let cumulativeHeight = 0
for (let i = 0; i < focusedIndex; i++) {
const ws = workspaceIndicator.workspaces.get(i)
cumulativeHeight += (ws && ws.isFocused ? 36 : 22) + 6 // pill height + spacing
}
// Current pill height
const currentWs = workspaceIndicator.workspaces.get(focusedIndex)
const currentPillHeight = (currentWs && currentWs.isFocused ? 36 : 22)
// Column is centered, so start from center and calculate offset
const columnHeight = parent.height - 24 // Total available height minus padding
const columnTop = 12 // Top padding
return columnTop + cumulativeHeight + currentPillHeight / 2 - height / 2
}
width: 18 + waveOverlay.progress * 45
height: 30 + waveOverlay.progress * 35 // Pill-like height
radius: width / 2 // Pill-like rounded shape
color: "transparent"
border.width: 1.5
border.color: Qt.rgba(waveOverlay.waveColor.r, waveOverlay.waveColor.g, waveOverlay.waveColor.b, 0.6)
opacity: Math.max(0, 0.8 - waveOverlay.progress * 1.2)
visible: waveOverlay.progress > 0.2
}
}
}
// Clock at bottom-left corner
Widgets.Clock {
id: clockWidget
anchors {
bottom: parent.bottom
left: parent.left
bottomMargin: Data.Settings.borderWidth
leftMargin: Data.Settings.borderWidth
}
z: 10
}
// Notification popups (primary screen only)
Notifications.Notification {
id: notificationPopup
visible: (modelData === (Quickshell.primaryScreen || Quickshell.screens[0])) && calculatedHeight > 20
anchors {
top: parent.top
right: parent.right
rightMargin: Data.Settings.borderWidth + 20
topMargin: 0
}
width: 420
height: calculatedHeight
shell: desktop.shell
notificationServer: desktop.notificationService ? desktop.notificationService.notificationServer : null
z: 15
Component.onCompleted: {
let targetScreen = Quickshell.primaryScreen || Quickshell.screens[0]
if (modelData === targetScreen) {
desktop.shell.notificationWindow = notificationPopup
}
}
}
// UI overlay layer for modal components
Item {
id: uiLayer
anchors.fill: parent
z: 20
Core.Version {
visible: modelData === Quickshell.primaryScreen
}
ControlPanel.ControlPanel {
id: controlPanelComponent
shell: desktop.shell
}
}
}
}
// Handle dynamic screen configuration changes
Connections {
target: Quickshell
function onScreensChanged() {
// Screen changes handled by Variants automatically
}
}
}