quickshell and hyprland additions

This commit is contained in:
2026-03-15 13:56:00 +02:00
parent c9c27d1554
commit 1ad06b82a6
509 changed files with 68371 additions and 19 deletions

View File

@@ -0,0 +1,153 @@
pragma ComponentBehavior: Bound
import qs.components
import qs.components.containers
import qs.services
import qs.config
import Quickshell
import Quickshell.Wayland
import QtQuick
Loader {
active: Config.background.enabled
sourceComponent: Variants {
model: Quickshell.screens
StyledWindow {
id: win
required property ShellScreen modelData
screen: modelData
name: "background"
WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.layer: Config.background.wallpaperEnabled ? WlrLayer.Background : WlrLayer.Bottom
color: Config.background.wallpaperEnabled ? "black" : "transparent"
surfaceFormat.opaque: false
anchors.top: true
anchors.bottom: true
anchors.left: true
anchors.right: true
Item {
id: behindClock
anchors.fill: parent
Loader {
id: wallpaper
anchors.fill: parent
active: Config.background.wallpaperEnabled
sourceComponent: Wallpaper {}
}
Visualiser {
anchors.fill: parent
screen: win.modelData
wallpaper: wallpaper
}
}
Loader {
id: clockLoader
active: Config.background.desktopClock.enabled
anchors.margins: Appearance.padding.large * 2
anchors.leftMargin: Appearance.padding.large * 2 + Config.bar.sizes.innerWidth + Math.max(Appearance.padding.smaller, Config.border.thickness)
state: Config.background.desktopClock.position
states: [
State {
name: "top-left"
AnchorChanges {
target: clockLoader
anchors.top: parent.top
anchors.left: parent.left
}
},
State {
name: "top-center"
AnchorChanges {
target: clockLoader
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
}
},
State {
name: "top-right"
AnchorChanges {
target: clockLoader
anchors.top: parent.top
anchors.right: parent.right
}
},
State {
name: "middle-left"
AnchorChanges {
target: clockLoader
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
}
},
State {
name: "middle-center"
AnchorChanges {
target: clockLoader
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
}
},
State {
name: "middle-right"
AnchorChanges {
target: clockLoader
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
}
},
State {
name: "bottom-left"
AnchorChanges {
target: clockLoader
anchors.bottom: parent.bottom
anchors.left: parent.left
}
},
State {
name: "bottom-center"
AnchorChanges {
target: clockLoader
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
},
State {
name: "bottom-right"
AnchorChanges {
target: clockLoader
anchors.bottom: parent.bottom
anchors.right: parent.right
}
}
]
transitions: Transition {
AnchorAnimation {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
sourceComponent: DesktopClock {
wallpaper: behindClock
absX: clockLoader.x
absY: clockLoader.y
}
}
}
}
}

View File

@@ -0,0 +1,169 @@
pragma ComponentBehavior: Bound
import qs.components
import qs.services
import qs.config
import QtQuick
import QtQuick.Layouts
import QtQuick.Effects
Item {
id: root
required property Item wallpaper
required property real absX
required property real absY
property real scale: Config.background.desktopClock.scale
readonly property bool bgEnabled: Config.background.desktopClock.background.enabled
readonly property bool blurEnabled: bgEnabled && Config.background.desktopClock.background.blur && !GameMode.enabled
readonly property bool invertColors: Config.background.desktopClock.invertColors
readonly property bool useLightSet: Colours.light ? !invertColors : invertColors
readonly property color safePrimary: useLightSet ? Colours.palette.m3primaryContainer : Colours.palette.m3primary
readonly property color safeSecondary: useLightSet ? Colours.palette.m3secondaryContainer : Colours.palette.m3secondary
readonly property color safeTertiary: useLightSet ? Colours.palette.m3tertiaryContainer : Colours.palette.m3tertiary
implicitWidth: layout.implicitWidth + (Appearance.padding.large * 4 * root.scale)
implicitHeight: layout.implicitHeight + (Appearance.padding.large * 2 * root.scale)
Item {
id: clockContainer
anchors.fill: parent
layer.enabled: Config.background.desktopClock.shadow.enabled
layer.effect: MultiEffect {
shadowEnabled: true
shadowColor: Colours.palette.m3shadow
shadowOpacity: Config.background.desktopClock.shadow.opacity
shadowBlur: Config.background.desktopClock.shadow.blur
}
Loader {
anchors.fill: parent
active: root.blurEnabled
sourceComponent: MultiEffect {
source: ShaderEffectSource {
sourceItem: root.wallpaper
sourceRect: Qt.rect(root.absX, root.absY, root.width, root.height)
}
maskSource: backgroundPlate
maskEnabled: true
blurEnabled: true
blur: 1
blurMax: 64
autoPaddingEnabled: false
}
}
StyledRect {
id: backgroundPlate
visible: root.bgEnabled
anchors.fill: parent
radius: Appearance.rounding.large * root.scale
opacity: Config.background.desktopClock.background.opacity
color: Colours.palette.m3surface
layer.enabled: root.blurEnabled
}
RowLayout {
id: layout
anchors.centerIn: parent
spacing: Appearance.spacing.larger * root.scale
RowLayout {
spacing: Appearance.spacing.small
StyledText {
text: Time.hourStr
font.pointSize: Appearance.font.size.extraLarge * 3 * root.scale
font.weight: Font.Bold
color: root.safePrimary
}
StyledText {
text: ":"
font.pointSize: Appearance.font.size.extraLarge * 3 * root.scale
color: root.safeTertiary
opacity: 0.8
Layout.topMargin: -Appearance.padding.large * 1.5 * root.scale
}
StyledText {
text: Time.minuteStr
font.pointSize: Appearance.font.size.extraLarge * 3 * root.scale
font.weight: Font.Bold
color: root.safeSecondary
}
Loader {
Layout.alignment: Qt.AlignTop
Layout.topMargin: Appearance.padding.large * 1.4 * root.scale
active: Config.services.useTwelveHourClock
visible: active
sourceComponent: StyledText {
text: Time.amPmStr
font.pointSize: Appearance.font.size.large * root.scale
color: root.safeSecondary
}
}
}
StyledRect {
Layout.fillHeight: true
Layout.preferredWidth: 4 * root.scale
Layout.topMargin: Appearance.spacing.larger * root.scale
Layout.bottomMargin: Appearance.spacing.larger * root.scale
radius: Appearance.rounding.full
color: root.safePrimary
opacity: 0.8
}
ColumnLayout {
spacing: 0
StyledText {
text: Time.format("MMMM").toUpperCase()
font.pointSize: Appearance.font.size.large * root.scale
font.letterSpacing: 4
font.weight: Font.Bold
color: root.safeSecondary
}
StyledText {
text: Time.format("dd")
font.pointSize: Appearance.font.size.extraLarge * root.scale
font.letterSpacing: 2
font.weight: Font.Medium
color: root.safePrimary
}
StyledText {
text: Time.format("dddd")
font.pointSize: Appearance.font.size.larger * root.scale
font.letterSpacing: 2
color: root.safeSecondary
}
}
}
}
Behavior on scale {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
Behavior on implicitWidth {
Anim {
duration: Appearance.anim.durations.small
}
}
}

View File

@@ -0,0 +1,151 @@
pragma ComponentBehavior: Bound
import qs.components
import qs.services
import qs.config
import Caelestia.Services
import Quickshell
import Quickshell.Widgets
import QtQuick
import QtQuick.Effects
Item {
id: root
required property ShellScreen screen
required property Item wallpaper
readonly property bool shouldBeActive: Config.background.visualiser.enabled && (!Config.background.visualiser.autoHide || (Hypr.monitorFor(screen)?.activeWorkspace?.toplevels?.values.every(t => t.lastIpcObject?.floating) ?? true))
property real offset: shouldBeActive ? 0 : screen.height * 0.2
opacity: shouldBeActive ? 1 : 0
Loader {
anchors.fill: parent
active: root.opacity > 0 && Config.background.visualiser.blur
sourceComponent: MultiEffect {
source: root.wallpaper
maskSource: wrapper
maskEnabled: true
blurEnabled: true
blur: 1
blurMax: 32
autoPaddingEnabled: false
}
}
Item {
id: wrapper
anchors.fill: parent
layer.enabled: true
Loader {
anchors.fill: parent
anchors.topMargin: root.offset
anchors.bottomMargin: -root.offset
active: root.opacity > 0
sourceComponent: Item {
ServiceRef {
service: Audio.cava
}
Item {
id: content
anchors.fill: parent
anchors.margins: Config.border.thickness
anchors.leftMargin: Visibilities.bars.get(root.screen).exclusiveZone + Appearance.spacing.small * Config.background.visualiser.spacing
Side {
content: content
}
Side {
content: content
isRight: true
}
Behavior on anchors.leftMargin {
Anim {}
}
}
}
}
}
Behavior on offset {
Anim {}
}
Behavior on opacity {
Anim {}
}
component Side: Repeater {
id: side
required property Item content
property bool isRight
model: Config.services.visualiserBars
ClippingRectangle {
id: bar
required property int modelData
property real value: Math.max(0, Math.min(1, Audio.cava.values[side.isRight ? modelData : side.count - modelData - 1]))
clip: true
x: modelData * ((side.content.width * 0.4) / Config.services.visualiserBars) + (side.isRight ? side.content.width * 0.6 : 0)
implicitWidth: (side.content.width * 0.4) / Config.services.visualiserBars - Appearance.spacing.small * Config.background.visualiser.spacing
y: side.content.height - height
implicitHeight: bar.value * side.content.height * 0.4
color: "transparent"
topLeftRadius: Appearance.rounding.small * Config.background.visualiser.rounding
topRightRadius: Appearance.rounding.small * Config.background.visualiser.rounding
Rectangle {
topLeftRadius: parent.topLeftRadius
topRightRadius: parent.topRightRadius
gradient: Gradient {
orientation: Gradient.Vertical
GradientStop {
position: 0
color: Qt.alpha(Colours.palette.m3primary, 0.7)
Behavior on color {
CAnim {}
}
}
GradientStop {
position: 1
color: Qt.alpha(Colours.palette.m3inversePrimary, 0.7)
Behavior on color {
CAnim {}
}
}
}
anchors.left: parent.left
anchors.right: parent.right
y: parent.height - height
implicitHeight: side.content.height * 0.4
}
Behavior on value {
Anim {
duration: Appearance.anim.durations.small
}
}
}
}
}

View File

@@ -0,0 +1,145 @@
pragma ComponentBehavior: Bound
import qs.components
import qs.components.images
import qs.components.filedialog
import qs.services
import qs.config
import qs.utils
import QtQuick
Item {
id: root
property string source: Wallpapers.current
property Image current: one
onSourceChanged: {
if (!source)
current = null;
else if (current === one)
two.update();
else
one.update();
}
Component.onCompleted: {
if (source)
Qt.callLater(() => one.update());
}
Loader {
anchors.fill: parent
active: !root.source
sourceComponent: StyledRect {
color: Colours.palette.m3surfaceContainer
Row {
anchors.centerIn: parent
spacing: Appearance.spacing.large
MaterialIcon {
text: "sentiment_stressed"
color: Colours.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.extraLarge * 5
}
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: Appearance.spacing.small
StyledText {
text: qsTr("Wallpaper missing?")
color: Colours.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.extraLarge * 2
font.bold: true
}
StyledRect {
implicitWidth: selectWallText.implicitWidth + Appearance.padding.large * 2
implicitHeight: selectWallText.implicitHeight + Appearance.padding.small * 2
radius: Appearance.rounding.full
color: Colours.palette.m3primary
FileDialog {
id: dialog
title: qsTr("Select a wallpaper")
filterLabel: qsTr("Image files")
filters: Images.validImageExtensions
onAccepted: path => Wallpapers.setWallpaper(path)
}
StateLayer {
radius: parent.radius
color: Colours.palette.m3onPrimary
function onClicked(): void {
dialog.open();
}
}
StyledText {
id: selectWallText
anchors.centerIn: parent
text: qsTr("Set it now!")
color: Colours.palette.m3onPrimary
font.pointSize: Appearance.font.size.large
}
}
}
}
}
}
Img {
id: one
}
Img {
id: two
}
component Img: CachingImage {
id: img
function update(): void {
if (path === root.source)
root.current = this;
else
path = root.source;
}
anchors.fill: parent
opacity: 0
scale: Wallpapers.showPreview ? 1 : 0.8
onStatusChanged: {
if (status === Image.Ready)
root.current = this;
}
states: State {
name: "visible"
when: root.current === img
PropertyChanges {
img.opacity: 1
img.scale: 1
}
}
transitions: Transition {
Anim {
target: img
properties: "opacity,scale"
}
}
}
}