mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-28 15:32:50 -05:00
More wallpaper effects + per-monitor auto cycling
This commit is contained in:
@@ -43,6 +43,7 @@ Singleton {
|
|||||||
property string wallpaperCyclingMode: "interval" // "interval" or "time"
|
property string wallpaperCyclingMode: "interval" // "interval" or "time"
|
||||||
property int wallpaperCyclingInterval: 300 // seconds (5 minutes)
|
property int wallpaperCyclingInterval: 300 // seconds (5 minutes)
|
||||||
property string wallpaperCyclingTime: "06:00" // HH:mm format
|
property string wallpaperCyclingTime: "06:00" // HH:mm format
|
||||||
|
property var monitorCyclingSettings: ({})
|
||||||
property string lastBrightnessDevice: ""
|
property string lastBrightnessDevice: ""
|
||||||
property string launchPrefix: ""
|
property string launchPrefix: ""
|
||||||
property string wallpaperTransition: "fade"
|
property string wallpaperTransition: "fade"
|
||||||
@@ -113,6 +114,7 @@ Singleton {
|
|||||||
wallpaperCyclingMode = settings.wallpaperCyclingMode !== undefined ? settings.wallpaperCyclingMode : "interval"
|
wallpaperCyclingMode = settings.wallpaperCyclingMode !== undefined ? settings.wallpaperCyclingMode : "interval"
|
||||||
wallpaperCyclingInterval = settings.wallpaperCyclingInterval !== undefined ? settings.wallpaperCyclingInterval : 300
|
wallpaperCyclingInterval = settings.wallpaperCyclingInterval !== undefined ? settings.wallpaperCyclingInterval : 300
|
||||||
wallpaperCyclingTime = settings.wallpaperCyclingTime !== undefined ? settings.wallpaperCyclingTime : "06:00"
|
wallpaperCyclingTime = settings.wallpaperCyclingTime !== undefined ? settings.wallpaperCyclingTime : "06:00"
|
||||||
|
monitorCyclingSettings = settings.monitorCyclingSettings !== undefined ? settings.monitorCyclingSettings : {}
|
||||||
lastBrightnessDevice = settings.lastBrightnessDevice !== undefined ? settings.lastBrightnessDevice : ""
|
lastBrightnessDevice = settings.lastBrightnessDevice !== undefined ? settings.lastBrightnessDevice : ""
|
||||||
launchPrefix = settings.launchPrefix !== undefined ? settings.launchPrefix : ""
|
launchPrefix = settings.launchPrefix !== undefined ? settings.launchPrefix : ""
|
||||||
wallpaperTransition = settings.wallpaperTransition !== undefined ? settings.wallpaperTransition : "fade"
|
wallpaperTransition = settings.wallpaperTransition !== undefined ? settings.wallpaperTransition : "fade"
|
||||||
@@ -166,6 +168,7 @@ Singleton {
|
|||||||
"wallpaperCyclingMode": wallpaperCyclingMode,
|
"wallpaperCyclingMode": wallpaperCyclingMode,
|
||||||
"wallpaperCyclingInterval": wallpaperCyclingInterval,
|
"wallpaperCyclingInterval": wallpaperCyclingInterval,
|
||||||
"wallpaperCyclingTime": wallpaperCyclingTime,
|
"wallpaperCyclingTime": wallpaperCyclingTime,
|
||||||
|
"monitorCyclingSettings": monitorCyclingSettings,
|
||||||
"lastBrightnessDevice": lastBrightnessDevice,
|
"lastBrightnessDevice": lastBrightnessDevice,
|
||||||
"launchPrefix": launchPrefix,
|
"launchPrefix": launchPrefix,
|
||||||
"wallpaperTransition": wallpaperTransition,
|
"wallpaperTransition": wallpaperTransition,
|
||||||
@@ -367,18 +370,64 @@ Singleton {
|
|||||||
saveSettings()
|
saveSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMonitorCyclingSettings(screenName) {
|
||||||
|
return monitorCyclingSettings[screenName] || {
|
||||||
|
enabled: false,
|
||||||
|
mode: "interval",
|
||||||
|
interval: 300,
|
||||||
|
time: "06:00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMonitorCyclingEnabled(screenName, enabled) {
|
||||||
|
var newSettings = Object.assign({}, monitorCyclingSettings)
|
||||||
|
if (!newSettings[screenName]) {
|
||||||
|
newSettings[screenName] = { enabled: false, mode: "interval", interval: 300, time: "06:00" }
|
||||||
|
}
|
||||||
|
newSettings[screenName].enabled = enabled
|
||||||
|
monitorCyclingSettings = newSettings
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMonitorCyclingMode(screenName, mode) {
|
||||||
|
var newSettings = Object.assign({}, monitorCyclingSettings)
|
||||||
|
if (!newSettings[screenName]) {
|
||||||
|
newSettings[screenName] = { enabled: false, mode: "interval", interval: 300, time: "06:00" }
|
||||||
|
}
|
||||||
|
newSettings[screenName].mode = mode
|
||||||
|
monitorCyclingSettings = newSettings
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMonitorCyclingInterval(screenName, interval) {
|
||||||
|
var newSettings = Object.assign({}, monitorCyclingSettings)
|
||||||
|
if (!newSettings[screenName]) {
|
||||||
|
newSettings[screenName] = { enabled: false, mode: "interval", interval: 300, time: "06:00" }
|
||||||
|
}
|
||||||
|
newSettings[screenName].interval = interval
|
||||||
|
monitorCyclingSettings = newSettings
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMonitorCyclingTime(screenName, time) {
|
||||||
|
var newSettings = Object.assign({}, monitorCyclingSettings)
|
||||||
|
if (!newSettings[screenName]) {
|
||||||
|
newSettings[screenName] = { enabled: false, mode: "interval", interval: 300, time: "06:00" }
|
||||||
|
}
|
||||||
|
newSettings[screenName].time = time
|
||||||
|
monitorCyclingSettings = newSettings
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
function setPerMonitorWallpaper(enabled) {
|
function setPerMonitorWallpaper(enabled) {
|
||||||
perMonitorWallpaper = enabled
|
perMonitorWallpaper = enabled
|
||||||
|
|
||||||
// Disable automatic cycling when per-monitor mode is enabled
|
|
||||||
if (enabled && wallpaperCyclingEnabled) {
|
|
||||||
wallpaperCyclingEnabled = false
|
|
||||||
}
|
|
||||||
|
|
||||||
saveSettings()
|
saveSettings()
|
||||||
|
|
||||||
// Refresh dynamic theming when per-monitor mode changes
|
// Refresh dynamic theming when per-monitor mode changes
|
||||||
if (typeof Theme !== "undefined") {
|
if (typeof Theme !== "undefined") {
|
||||||
|
if (Theme.currentTheme === Theme.dynamic) {
|
||||||
|
Theme.extractColors()
|
||||||
|
}
|
||||||
Theme.generateSystemThemesFromCurrentTheme()
|
Theme.generateSystemThemesFromCurrentTheme()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -511,13 +511,13 @@ Item {
|
|||||||
height: 1
|
height: 1
|
||||||
color: Theme.outline
|
color: Theme.outline
|
||||||
opacity: 0.2
|
opacity: 0.2
|
||||||
visible: SessionData.wallpaperPath !== "" && !SessionData.perMonitorWallpaper
|
visible: SessionData.wallpaperPath !== "" || SessionData.perMonitorWallpaper
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
visible: SessionData.wallpaperPath !== "" && !SessionData.perMonitorWallpaper
|
visible: SessionData.wallpaperPath !== "" || SessionData.perMonitorWallpaper
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -554,11 +554,23 @@ Item {
|
|||||||
id: cyclingToggle
|
id: cyclingToggle
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
checked: SessionData.wallpaperCyclingEnabled
|
checked: SessionData.perMonitorWallpaper ? SessionData.getMonitorCyclingSettings(selectedMonitorName).enabled : SessionData.wallpaperCyclingEnabled
|
||||||
enabled: !SessionData.perMonitorWallpaper
|
|
||||||
onToggled: toggled => {
|
onToggled: toggled => {
|
||||||
return SessionData.setWallpaperCyclingEnabled(toggled)
|
if (SessionData.perMonitorWallpaper) {
|
||||||
|
return SessionData.setMonitorCyclingEnabled(selectedMonitorName, toggled)
|
||||||
|
} else {
|
||||||
|
return SessionData.setWallpaperCyclingEnabled(toggled)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: personalizationTab
|
||||||
|
function onSelectedMonitorNameChanged() {
|
||||||
|
cyclingToggle.checked = Qt.binding(() => {
|
||||||
|
return SessionData.perMonitorWallpaper ? SessionData.getMonitorCyclingSettings(selectedMonitorName).enabled : SessionData.wallpaperCyclingEnabled
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,7 +578,7 @@ Item {
|
|||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
visible: SessionData.wallpaperCyclingEnabled
|
visible: SessionData.perMonitorWallpaper ? SessionData.getMonitorCyclingSettings(selectedMonitorName).enabled : SessionData.wallpaperCyclingEnabled
|
||||||
leftPadding: Theme.iconSize + Theme.spacingM
|
leftPadding: Theme.iconSize + Theme.spacingM
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -596,40 +608,104 @@ Item {
|
|||||||
"text": "Time",
|
"text": "Time",
|
||||||
"icon": "access_time"
|
"icon": "access_time"
|
||||||
}]
|
}]
|
||||||
currentIndex: SessionData.wallpaperCyclingMode === "time" ? 1 : 0
|
currentIndex: {
|
||||||
|
if (SessionData.perMonitorWallpaper) {
|
||||||
|
return SessionData.getMonitorCyclingSettings(selectedMonitorName).mode === "time" ? 1 : 0
|
||||||
|
} else {
|
||||||
|
return SessionData.wallpaperCyclingMode === "time" ? 1 : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
onTabClicked: index => {
|
onTabClicked: index => {
|
||||||
SessionData.setWallpaperCyclingMode(index === 1 ? "time" : "interval")
|
if (SessionData.perMonitorWallpaper) {
|
||||||
|
SessionData.setMonitorCyclingMode(selectedMonitorName, index === 1 ? "time" : "interval")
|
||||||
|
} else {
|
||||||
|
SessionData.setWallpaperCyclingMode(index === 1 ? "time" : "interval")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: personalizationTab
|
||||||
|
function onSelectedMonitorNameChanged() {
|
||||||
|
modeTabBar.currentIndex = Qt.binding(() => {
|
||||||
|
if (SessionData.perMonitorWallpaper) {
|
||||||
|
return SessionData.getMonitorCyclingSettings(selectedMonitorName).mode === "time" ? 1 : 0
|
||||||
|
} else {
|
||||||
|
return SessionData.wallpaperCyclingMode === "time" ? 1 : 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Qt.callLater(modeTabBar.updateIndicator)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interval settings
|
// Interval settings
|
||||||
DankDropdown {
|
DankDropdown {
|
||||||
|
id: intervalDropdown
|
||||||
property var intervalOptions: ["1 minute", "5 minutes", "15 minutes", "30 minutes", "1 hour", "1.5 hours", "2 hours", "3 hours", "4 hours", "6 hours", "8 hours", "12 hours"]
|
property var intervalOptions: ["1 minute", "5 minutes", "15 minutes", "30 minutes", "1 hour", "1.5 hours", "2 hours", "3 hours", "4 hours", "6 hours", "8 hours", "12 hours"]
|
||||||
property var intervalValues: [60, 300, 900, 1800, 3600, 5400, 7200, 10800, 14400, 21600, 28800, 43200]
|
property var intervalValues: [60, 300, 900, 1800, 3600, 5400, 7200, 10800, 14400, 21600, 28800, 43200]
|
||||||
|
|
||||||
width: parent.width - parent.leftPadding
|
width: parent.width - parent.leftPadding
|
||||||
visible: SessionData.wallpaperCyclingMode === "interval"
|
visible: {
|
||||||
|
if (SessionData.perMonitorWallpaper) {
|
||||||
|
return SessionData.getMonitorCyclingSettings(selectedMonitorName).mode === "interval"
|
||||||
|
} else {
|
||||||
|
return SessionData.wallpaperCyclingMode === "interval"
|
||||||
|
}
|
||||||
|
}
|
||||||
text: "Interval"
|
text: "Interval"
|
||||||
description: "How often to change wallpaper"
|
description: "How often to change wallpaper"
|
||||||
options: intervalOptions
|
options: intervalOptions
|
||||||
currentValue: {
|
currentValue: {
|
||||||
const currentSeconds = SessionData.wallpaperCyclingInterval
|
var currentSeconds
|
||||||
|
if (SessionData.perMonitorWallpaper) {
|
||||||
|
currentSeconds = SessionData.getMonitorCyclingSettings(selectedMonitorName).interval
|
||||||
|
} else {
|
||||||
|
currentSeconds = SessionData.wallpaperCyclingInterval
|
||||||
|
}
|
||||||
const index = intervalValues.indexOf(currentSeconds)
|
const index = intervalValues.indexOf(currentSeconds)
|
||||||
return index >= 0 ? intervalOptions[index] : "5 minutes"
|
return index >= 0 ? intervalOptions[index] : "5 minutes"
|
||||||
}
|
}
|
||||||
onValueChanged: value => {
|
onValueChanged: value => {
|
||||||
const index = intervalOptions.indexOf(value)
|
const index = intervalOptions.indexOf(value)
|
||||||
if (index >= 0)
|
if (index >= 0) {
|
||||||
SessionData.setWallpaperCyclingInterval(intervalValues[index])
|
if (SessionData.perMonitorWallpaper) {
|
||||||
|
SessionData.setMonitorCyclingInterval(selectedMonitorName, intervalValues[index])
|
||||||
|
} else {
|
||||||
|
SessionData.setWallpaperCyclingInterval(intervalValues[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: personalizationTab
|
||||||
|
function onSelectedMonitorNameChanged() {
|
||||||
|
// Force dropdown to refresh its currentValue
|
||||||
|
Qt.callLater(() => {
|
||||||
|
var currentSeconds
|
||||||
|
if (SessionData.perMonitorWallpaper) {
|
||||||
|
currentSeconds = SessionData.getMonitorCyclingSettings(selectedMonitorName).interval
|
||||||
|
} else {
|
||||||
|
currentSeconds = SessionData.wallpaperCyclingInterval
|
||||||
|
}
|
||||||
|
const index = intervalDropdown.intervalValues.indexOf(currentSeconds)
|
||||||
|
intervalDropdown.currentValue = index >= 0 ? intervalDropdown.intervalOptions[index] : "5 minutes"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Time settings
|
// Time settings
|
||||||
Row {
|
Row {
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
visible: SessionData.wallpaperCyclingMode === "time"
|
visible: {
|
||||||
|
if (SessionData.perMonitorWallpaper) {
|
||||||
|
return SessionData.getMonitorCyclingSettings(selectedMonitorName).mode === "time"
|
||||||
|
} else {
|
||||||
|
return SessionData.wallpaperCyclingMode === "time"
|
||||||
|
}
|
||||||
|
}
|
||||||
width: parent.width - parent.leftPadding
|
width: parent.width - parent.leftPadding
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -640,32 +716,71 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DankTextField {
|
DankTextField {
|
||||||
|
id: timeTextField
|
||||||
width: 100
|
width: 100
|
||||||
height: 40
|
height: 40
|
||||||
text: SessionData.wallpaperCyclingTime
|
text: {
|
||||||
|
if (SessionData.perMonitorWallpaper) {
|
||||||
|
return SessionData.getMonitorCyclingSettings(selectedMonitorName).time
|
||||||
|
} else {
|
||||||
|
return SessionData.wallpaperCyclingTime
|
||||||
|
}
|
||||||
|
}
|
||||||
placeholderText: "00:00"
|
placeholderText: "00:00"
|
||||||
maximumLength: 5
|
maximumLength: 5
|
||||||
topPadding: Theme.spacingS
|
topPadding: Theme.spacingS
|
||||||
bottomPadding: Theme.spacingS
|
bottomPadding: Theme.spacingS
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
var isValid = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/.test(text)
|
var isValid = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/.test(text)
|
||||||
if (isValid)
|
if (isValid) {
|
||||||
SessionData.setWallpaperCyclingTime(text)
|
if (SessionData.perMonitorWallpaper) {
|
||||||
else
|
SessionData.setMonitorCyclingTime(selectedMonitorName, text)
|
||||||
text = SessionData.wallpaperCyclingTime
|
} else {
|
||||||
|
SessionData.setWallpaperCyclingTime(text)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (SessionData.perMonitorWallpaper) {
|
||||||
|
text = SessionData.getMonitorCyclingSettings(selectedMonitorName).time
|
||||||
|
} else {
|
||||||
|
text = SessionData.wallpaperCyclingTime
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onEditingFinished: {
|
onEditingFinished: {
|
||||||
var isValid = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/.test(text)
|
var isValid = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/.test(text)
|
||||||
if (isValid)
|
if (isValid) {
|
||||||
SessionData.setWallpaperCyclingTime(text)
|
if (SessionData.perMonitorWallpaper) {
|
||||||
else
|
SessionData.setMonitorCyclingTime(selectedMonitorName, text)
|
||||||
text = SessionData.wallpaperCyclingTime
|
} else {
|
||||||
|
SessionData.setWallpaperCyclingTime(text)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (SessionData.perMonitorWallpaper) {
|
||||||
|
text = SessionData.getMonitorCyclingSettings(selectedMonitorName).time
|
||||||
|
} else {
|
||||||
|
text = SessionData.wallpaperCyclingTime
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
validator: RegularExpressionValidator {
|
validator: RegularExpressionValidator {
|
||||||
regularExpression: /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/
|
regularExpression: /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: personalizationTab
|
||||||
|
function onSelectedMonitorNameChanged() {
|
||||||
|
// Force text field to refresh its value
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (SessionData.perMonitorWallpaper) {
|
||||||
|
timeTextField.text = SessionData.getMonitorCyclingSettings(selectedMonitorName).time
|
||||||
|
} else {
|
||||||
|
timeTextField.text = SessionData.wallpaperCyclingTime
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -695,10 +810,13 @@ Item {
|
|||||||
case "wipe": return "Wipe"
|
case "wipe": return "Wipe"
|
||||||
case "disc": return "Disc"
|
case "disc": return "Disc"
|
||||||
case "stripes": return "Stripes"
|
case "stripes": return "Stripes"
|
||||||
|
case "iris bloom": return "Iris Bloom"
|
||||||
|
case "pixelate": return "Pixelate"
|
||||||
|
case "portal": return "Portal"
|
||||||
default: return "Fade"
|
default: return "Fade"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
options: ["Fade", "Wipe", "Disc", "Stripes"]
|
options: ["Fade", "Wipe", "Disc", "Stripes", "Iris Bloom", "Pixelate", "Portal"]
|
||||||
onValueChanged: value => {
|
onValueChanged: value => {
|
||||||
var transition = value.toLowerCase()
|
var transition = value.toLowerCase()
|
||||||
SessionData.setWallpaperTransition(transition)
|
SessionData.setWallpaperTransition(transition)
|
||||||
|
|||||||
@@ -282,6 +282,78 @@ LazyLoader {
|
|||||||
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_stripes.frag.qsb")
|
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_stripes.frag.qsb")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShaderEffect {
|
||||||
|
id: irisBloomShader
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: (root.transitionType === "iris bloom" || root.transitionType === "none") && (root.hasCurrent || root.booting)
|
||||||
|
|
||||||
|
property variant source1: root.hasCurrent ? currentWallpaper : transparentSource
|
||||||
|
property variant source2: nextWallpaper
|
||||||
|
property real progress: root.transitionProgress
|
||||||
|
property real smoothness: root.edgeSmoothness
|
||||||
|
property real centerX: root.discCenterX
|
||||||
|
property real centerY: root.discCenterY
|
||||||
|
property real aspectRatio: root.width / root.height
|
||||||
|
property real fillMode: root.fillMode
|
||||||
|
property vector4d fillColor: root.fillColor
|
||||||
|
property real imageWidth1: Math.max(1, root.hasCurrent ? source1.sourceSize.width : width)
|
||||||
|
property real imageHeight1: Math.max(1, root.hasCurrent ? source1.sourceSize.height : height)
|
||||||
|
property real imageWidth2: Math.max(1, source2.sourceSize.width)
|
||||||
|
property real imageHeight2: Math.max(1, source2.sourceSize.height)
|
||||||
|
property real screenWidth: width
|
||||||
|
property real screenHeight: height
|
||||||
|
|
||||||
|
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_iris_bloom.frag.qsb")
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderEffect {
|
||||||
|
id: pixelateShader
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: root.transitionType === "pixelate" && (root.hasCurrent || root.booting)
|
||||||
|
|
||||||
|
property variant source1: root.hasCurrent ? currentWallpaper : transparentSource
|
||||||
|
property variant source2: nextWallpaper
|
||||||
|
property real progress: root.transitionProgress
|
||||||
|
property real smoothness: root.edgeSmoothness // controls starting block size
|
||||||
|
property real fillMode: root.fillMode
|
||||||
|
property vector4d fillColor: root.fillColor
|
||||||
|
property real imageWidth1: Math.max(1, root.hasCurrent ? source1.sourceSize.width : width)
|
||||||
|
property real imageHeight1: Math.max(1, root.hasCurrent ? source1.sourceSize.height : height)
|
||||||
|
property real imageWidth2: Math.max(1, source2.sourceSize.width)
|
||||||
|
property real imageHeight2: Math.max(1, source2.sourceSize.height)
|
||||||
|
property real screenWidth: width
|
||||||
|
property real screenHeight: height
|
||||||
|
property real centerX: root.discCenterX
|
||||||
|
property real centerY: root.discCenterY
|
||||||
|
property real aspectRatio: root.width / root.height
|
||||||
|
|
||||||
|
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_pixelate.frag.qsb")
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderEffect {
|
||||||
|
id: portalShader
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: root.transitionType === "portal" && (root.hasCurrent || root.booting)
|
||||||
|
|
||||||
|
property variant source1: root.hasCurrent ? currentWallpaper : transparentSource
|
||||||
|
property variant source2: nextWallpaper
|
||||||
|
property real progress: root.transitionProgress
|
||||||
|
property real smoothness: root.edgeSmoothness
|
||||||
|
property real aspectRatio: root.width / root.height
|
||||||
|
property real centerX: root.discCenterX
|
||||||
|
property real centerY: root.discCenterY
|
||||||
|
property real fillMode: root.fillMode
|
||||||
|
property vector4d fillColor: root.fillColor
|
||||||
|
property real imageWidth1: Math.max(1, root.hasCurrent ? source1.sourceSize.width : width)
|
||||||
|
property real imageHeight1: Math.max(1, root.hasCurrent ? source1.sourceSize.height : height)
|
||||||
|
property real imageWidth2: Math.max(1, source2.sourceSize.width)
|
||||||
|
property real imageHeight2: Math.max(1, source2.sourceSize.height)
|
||||||
|
property real screenWidth: width
|
||||||
|
property real screenHeight: height
|
||||||
|
|
||||||
|
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_portal.frag.qsb")
|
||||||
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
id: transitionAnimation
|
id: transitionAnimation
|
||||||
target: root
|
target: root
|
||||||
|
|||||||
@@ -13,11 +13,63 @@ Singleton {
|
|||||||
property string cachedCyclingTime: SessionData.wallpaperCyclingTime
|
property string cachedCyclingTime: SessionData.wallpaperCyclingTime
|
||||||
property int cachedCyclingInterval: SessionData.wallpaperCyclingInterval
|
property int cachedCyclingInterval: SessionData.wallpaperCyclingInterval
|
||||||
property string lastTimeCheck: ""
|
property string lastTimeCheck: ""
|
||||||
|
property var monitorTimers: ({})
|
||||||
|
property var monitorLastTimeChecks: ({})
|
||||||
|
property var monitorProcesses: ({})
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
updateCyclingState()
|
updateCyclingState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: monitorTimerComponent
|
||||||
|
Timer {
|
||||||
|
property string targetScreen: ""
|
||||||
|
running: false
|
||||||
|
repeat: true
|
||||||
|
onTriggered: {
|
||||||
|
if (typeof WallpaperCyclingService !== "undefined" && targetScreen !== "") {
|
||||||
|
WallpaperCyclingService.cycleNextForMonitor(targetScreen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: monitorProcessComponent
|
||||||
|
Process {
|
||||||
|
property string targetScreenName: ""
|
||||||
|
property string currentWallpaper: ""
|
||||||
|
property bool goToPrevious: false
|
||||||
|
running: false
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
if (text && text.trim()) {
|
||||||
|
const files = text.trim().split('\n').filter(file => file.length > 0)
|
||||||
|
if (files.length <= 1) return
|
||||||
|
const wallpaperList = files.sort()
|
||||||
|
const currentPath = currentWallpaper
|
||||||
|
let currentIndex = wallpaperList.findIndex(path => path === currentPath)
|
||||||
|
if (currentIndex === -1) currentIndex = 0
|
||||||
|
let targetIndex
|
||||||
|
if (goToPrevious) {
|
||||||
|
targetIndex = currentIndex === 0 ? wallpaperList.length - 1 : currentIndex - 1
|
||||||
|
} else {
|
||||||
|
targetIndex = (currentIndex + 1) % wallpaperList.length
|
||||||
|
}
|
||||||
|
const targetWallpaper = wallpaperList[targetIndex]
|
||||||
|
if (targetWallpaper && targetWallpaper !== currentPath) {
|
||||||
|
if (targetScreenName) {
|
||||||
|
SessionData.setMonitorWallpaper(targetScreenName, targetWallpaper)
|
||||||
|
} else {
|
||||||
|
SessionData.setWallpaper(targetWallpaper)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: SessionData
|
target: SessionData
|
||||||
|
|
||||||
@@ -46,13 +98,46 @@ Singleton {
|
|||||||
function onPerMonitorWallpaperChanged() {
|
function onPerMonitorWallpaperChanged() {
|
||||||
updateCyclingState()
|
updateCyclingState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onMonitorCyclingSettingsChanged() {
|
||||||
|
updateCyclingState()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCyclingState() {
|
function updateCyclingState() {
|
||||||
if (SessionData.wallpaperCyclingEnabled && SessionData.wallpaperPath && !SessionData.perMonitorWallpaper) {
|
if (SessionData.perMonitorWallpaper) {
|
||||||
|
stopCycling()
|
||||||
|
updatePerMonitorCycling()
|
||||||
|
} else if (SessionData.wallpaperCyclingEnabled && SessionData.wallpaperPath) {
|
||||||
startCycling()
|
startCycling()
|
||||||
|
stopAllMonitorCycling()
|
||||||
} else {
|
} else {
|
||||||
stopCycling()
|
stopCycling()
|
||||||
|
stopAllMonitorCycling()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePerMonitorCycling() {
|
||||||
|
if (typeof Quickshell === "undefined") return
|
||||||
|
|
||||||
|
var screens = Quickshell.screens
|
||||||
|
for (var i = 0; i < screens.length; i++) {
|
||||||
|
var screenName = screens[i].name
|
||||||
|
var settings = SessionData.getMonitorCyclingSettings(screenName)
|
||||||
|
var wallpaper = SessionData.getMonitorWallpaper(screenName)
|
||||||
|
|
||||||
|
if (settings.enabled && wallpaper && !wallpaper.startsWith("#") && !wallpaper.startsWith("we:")) {
|
||||||
|
startMonitorCycling(screenName, settings)
|
||||||
|
} else {
|
||||||
|
stopMonitorCycling(screenName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopAllMonitorCycling() {
|
||||||
|
var screenNames = Object.keys(monitorTimers)
|
||||||
|
for (var i = 0; i < screenNames.length; i++) {
|
||||||
|
stopMonitorCycling(screenNames[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,15 +157,80 @@ Singleton {
|
|||||||
cyclingActive = false
|
cyclingActive = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function startMonitorCycling(screenName, settings) {
|
||||||
|
if (settings.mode === "interval") {
|
||||||
|
var timer = monitorTimers[screenName]
|
||||||
|
if (!timer && monitorTimerComponent && monitorTimerComponent.status === Component.Ready) {
|
||||||
|
var newTimers = Object.assign({}, monitorTimers)
|
||||||
|
newTimers[screenName] = monitorTimerComponent.createObject(root)
|
||||||
|
newTimers[screenName].targetScreen = screenName
|
||||||
|
monitorTimers = newTimers
|
||||||
|
timer = monitorTimers[screenName]
|
||||||
|
}
|
||||||
|
if (timer) {
|
||||||
|
timer.interval = settings.interval * 1000
|
||||||
|
timer.start()
|
||||||
|
}
|
||||||
|
} else if (settings.mode === "time") {
|
||||||
|
var newChecks = Object.assign({}, monitorLastTimeChecks)
|
||||||
|
newChecks[screenName] = ""
|
||||||
|
monitorLastTimeChecks = newChecks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopMonitorCycling(screenName) {
|
||||||
|
var timer = monitorTimers[screenName]
|
||||||
|
if (timer) {
|
||||||
|
timer.stop()
|
||||||
|
timer.destroy()
|
||||||
|
var newTimers = Object.assign({}, monitorTimers)
|
||||||
|
delete newTimers[screenName]
|
||||||
|
monitorTimers = newTimers
|
||||||
|
}
|
||||||
|
|
||||||
|
var process = monitorProcesses[screenName]
|
||||||
|
if (process) {
|
||||||
|
process.destroy()
|
||||||
|
var newProcesses = Object.assign({}, monitorProcesses)
|
||||||
|
delete newProcesses[screenName]
|
||||||
|
monitorProcesses = newProcesses
|
||||||
|
}
|
||||||
|
|
||||||
|
var newChecks = Object.assign({}, monitorLastTimeChecks)
|
||||||
|
delete newChecks[screenName]
|
||||||
|
monitorLastTimeChecks = newChecks
|
||||||
|
}
|
||||||
|
|
||||||
function cycleToNextWallpaper(screenName, wallpaperPath) {
|
function cycleToNextWallpaper(screenName, wallpaperPath) {
|
||||||
const currentWallpaper = wallpaperPath || SessionData.wallpaperPath
|
const currentWallpaper = wallpaperPath || SessionData.wallpaperPath
|
||||||
if (!currentWallpaper) return
|
if (!currentWallpaper) return
|
||||||
|
|
||||||
const wallpaperDir = currentWallpaper.substring(0, currentWallpaper.lastIndexOf('/'))
|
const wallpaperDir = currentWallpaper.substring(0, currentWallpaper.lastIndexOf('/'))
|
||||||
cyclingProcess.command = ["sh", "-c", `ls -1 "${wallpaperDir}"/*.jpg "${wallpaperDir}"/*.jpeg "${wallpaperDir}"/*.png "${wallpaperDir}"/*.bmp "${wallpaperDir}"/*.gif "${wallpaperDir}"/*.webp 2>/dev/null | sort`]
|
|
||||||
cyclingProcess.targetScreenName = screenName || ""
|
if (screenName && monitorProcessComponent && monitorProcessComponent.status === Component.Ready) {
|
||||||
cyclingProcess.currentWallpaper = currentWallpaper
|
// Use per-monitor process
|
||||||
cyclingProcess.running = true
|
var process = monitorProcesses[screenName]
|
||||||
|
if (!process) {
|
||||||
|
var newProcesses = Object.assign({}, monitorProcesses)
|
||||||
|
newProcesses[screenName] = monitorProcessComponent.createObject(root)
|
||||||
|
monitorProcesses = newProcesses
|
||||||
|
process = monitorProcesses[screenName]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process) {
|
||||||
|
process.command = ["sh", "-c", `ls -1 "${wallpaperDir}"/*.jpg "${wallpaperDir}"/*.jpeg "${wallpaperDir}"/*.png "${wallpaperDir}"/*.bmp "${wallpaperDir}"/*.gif "${wallpaperDir}"/*.webp 2>/dev/null | sort`]
|
||||||
|
process.targetScreenName = screenName
|
||||||
|
process.currentWallpaper = currentWallpaper
|
||||||
|
process.goToPrevious = false
|
||||||
|
process.running = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Use global process for fallback
|
||||||
|
cyclingProcess.command = ["sh", "-c", `ls -1 "${wallpaperDir}"/*.jpg "${wallpaperDir}"/*.jpeg "${wallpaperDir}"/*.png "${wallpaperDir}"/*.bmp "${wallpaperDir}"/*.gif "${wallpaperDir}"/*.webp 2>/dev/null | sort`]
|
||||||
|
cyclingProcess.targetScreenName = screenName || ""
|
||||||
|
cyclingProcess.currentWallpaper = currentWallpaper
|
||||||
|
cyclingProcess.running = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function cycleToPrevWallpaper(screenName, wallpaperPath) {
|
function cycleToPrevWallpaper(screenName, wallpaperPath) {
|
||||||
@@ -88,10 +238,31 @@ Singleton {
|
|||||||
if (!currentWallpaper) return
|
if (!currentWallpaper) return
|
||||||
|
|
||||||
const wallpaperDir = currentWallpaper.substring(0, currentWallpaper.lastIndexOf('/'))
|
const wallpaperDir = currentWallpaper.substring(0, currentWallpaper.lastIndexOf('/'))
|
||||||
prevCyclingProcess.command = ["sh", "-c", `ls -1 "${wallpaperDir}"/*.jpg "${wallpaperDir}"/*.jpeg "${wallpaperDir}"/*.png "${wallpaperDir}"/*.bmp "${wallpaperDir}"/*.gif "${wallpaperDir}"/*.webp 2>/dev/null | sort`]
|
|
||||||
prevCyclingProcess.targetScreenName = screenName || ""
|
if (screenName && monitorProcessComponent && monitorProcessComponent.status === Component.Ready) {
|
||||||
prevCyclingProcess.currentWallpaper = currentWallpaper
|
// Use per-monitor process (same as next, but with prev flag)
|
||||||
prevCyclingProcess.running = true
|
var process = monitorProcesses[screenName]
|
||||||
|
if (!process) {
|
||||||
|
var newProcesses = Object.assign({}, monitorProcesses)
|
||||||
|
newProcesses[screenName] = monitorProcessComponent.createObject(root)
|
||||||
|
monitorProcesses = newProcesses
|
||||||
|
process = monitorProcesses[screenName]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process) {
|
||||||
|
process.command = ["sh", "-c", `ls -1 "${wallpaperDir}"/*.jpg "${wallpaperDir}"/*.jpeg "${wallpaperDir}"/*.png "${wallpaperDir}"/*.bmp "${wallpaperDir}"/*.gif "${wallpaperDir}"/*.webp 2>/dev/null | sort`]
|
||||||
|
process.targetScreenName = screenName
|
||||||
|
process.currentWallpaper = currentWallpaper
|
||||||
|
process.goToPrevious = true
|
||||||
|
process.running = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Use global process for fallback
|
||||||
|
prevCyclingProcess.command = ["sh", "-c", `ls -1 "${wallpaperDir}"/*.jpg "${wallpaperDir}"/*.jpeg "${wallpaperDir}"/*.png "${wallpaperDir}"/*.bmp "${wallpaperDir}"/*.gif "${wallpaperDir}"/*.webp 2>/dev/null | sort`]
|
||||||
|
prevCyclingProcess.targetScreenName = screenName || ""
|
||||||
|
prevCyclingProcess.currentWallpaper = currentWallpaper
|
||||||
|
prevCyclingProcess.running = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function cycleNextManually() {
|
function cycleNextManually() {
|
||||||
@@ -141,12 +312,41 @@ Singleton {
|
|||||||
function checkTimeBasedCycling() {
|
function checkTimeBasedCycling() {
|
||||||
const currentTime = Qt.formatTime(systemClock.date, "hh:mm")
|
const currentTime = Qt.formatTime(systemClock.date, "hh:mm")
|
||||||
|
|
||||||
if (currentTime === cachedCyclingTime
|
if (!SessionData.perMonitorWallpaper) {
|
||||||
&& currentTime !== lastTimeCheck) {
|
if (currentTime === cachedCyclingTime && currentTime !== lastTimeCheck) {
|
||||||
lastTimeCheck = currentTime
|
lastTimeCheck = currentTime
|
||||||
cycleToNextWallpaper()
|
cycleToNextWallpaper()
|
||||||
} else if (currentTime !== cachedCyclingTime) {
|
} else if (currentTime !== cachedCyclingTime) {
|
||||||
lastTimeCheck = ""
|
lastTimeCheck = ""
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
checkPerMonitorTimeBasedCycling(currentTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPerMonitorTimeBasedCycling(currentTime) {
|
||||||
|
if (typeof Quickshell === "undefined") return
|
||||||
|
|
||||||
|
var screens = Quickshell.screens
|
||||||
|
for (var i = 0; i < screens.length; i++) {
|
||||||
|
var screenName = screens[i].name
|
||||||
|
var settings = SessionData.getMonitorCyclingSettings(screenName)
|
||||||
|
var wallpaper = SessionData.getMonitorWallpaper(screenName)
|
||||||
|
|
||||||
|
if (settings.enabled && settings.mode === "time" && wallpaper && !wallpaper.startsWith("#") && !wallpaper.startsWith("we:")) {
|
||||||
|
var lastCheck = monitorLastTimeChecks[screenName] || ""
|
||||||
|
|
||||||
|
if (currentTime === settings.time && currentTime !== lastCheck) {
|
||||||
|
var newChecks = Object.assign({}, monitorLastTimeChecks)
|
||||||
|
newChecks[screenName] = currentTime
|
||||||
|
monitorLastTimeChecks = newChecks
|
||||||
|
cycleNextForMonitor(screenName)
|
||||||
|
} else if (currentTime !== settings.time) {
|
||||||
|
var newChecks = Object.assign({}, monitorLastTimeChecks)
|
||||||
|
newChecks[screenName] = ""
|
||||||
|
monitorLastTimeChecks = newChecks
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +362,7 @@ Singleton {
|
|||||||
id: systemClock
|
id: systemClock
|
||||||
precision: SystemClock.Minutes
|
precision: SystemClock.Minutes
|
||||||
onDateChanged: {
|
onDateChanged: {
|
||||||
if (SessionData.wallpaperCyclingMode === "time" && cyclingActive) {
|
if ((SessionData.wallpaperCyclingMode === "time" && cyclingActive) || SessionData.perMonitorWallpaper) {
|
||||||
checkTimeBasedCycling()
|
checkTimeBasedCycling()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
#version 450
|
|
||||||
|
|
||||||
layout(location = 0) in vec2 qt_TexCoord0;
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
|
||||||
|
|
||||||
layout(binding = 1) uniform sampler2D source;
|
|
||||||
|
|
||||||
layout(std140, binding = 0) uniform buf {
|
|
||||||
mat4 qt_Matrix;
|
|
||||||
float qt_Opacity;
|
|
||||||
float imageOpacity;
|
|
||||||
} ubuf;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
// Center coordinates around (0, 0)
|
|
||||||
vec2 uv = qt_TexCoord0 - 0.5;
|
|
||||||
|
|
||||||
// Calculate distance from center
|
|
||||||
float distance = length(uv);
|
|
||||||
|
|
||||||
// Create circular mask - anything beyond radius 0.5 is transparent
|
|
||||||
float mask = 1.0 - smoothstep(0.48, 0.52, distance);
|
|
||||||
|
|
||||||
// Sample the texture
|
|
||||||
vec4 color = texture(source, qt_TexCoord0);
|
|
||||||
|
|
||||||
// Apply the circular mask and opacity
|
|
||||||
float finalAlpha = color.a * mask * ubuf.imageOpacity * ubuf.qt_Opacity;
|
|
||||||
fragColor = vec4(color.rgb * finalAlpha, finalAlpha);
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
#version 450
|
|
||||||
|
|
||||||
layout(location = 0) in vec2 qt_TexCoord0;
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
|
||||||
|
|
||||||
layout(binding = 1) uniform sampler2D source;
|
|
||||||
|
|
||||||
layout(std140, binding = 0) uniform buf {
|
|
||||||
mat4 qt_Matrix;
|
|
||||||
float qt_Opacity;
|
|
||||||
// Custom properties with non-conflicting names
|
|
||||||
float itemWidth;
|
|
||||||
float itemHeight;
|
|
||||||
float cornerRadius;
|
|
||||||
float imageOpacity;
|
|
||||||
} ubuf;
|
|
||||||
|
|
||||||
// Function to calculate the signed distance from a point to a rounded box
|
|
||||||
float roundedBoxSDF(vec2 centerPos, vec2 boxSize, float radius) {
|
|
||||||
vec2 d = abs(centerPos) - boxSize + radius;
|
|
||||||
return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0) - radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
// Get size from uniforms
|
|
||||||
vec2 itemSize = vec2(ubuf.itemWidth, ubuf.itemHeight);
|
|
||||||
float cornerRadius = ubuf.cornerRadius;
|
|
||||||
float itemOpacity = ubuf.imageOpacity;
|
|
||||||
|
|
||||||
// Normalize coordinates to [-0.5, 0.5] range
|
|
||||||
vec2 uv = qt_TexCoord0 - 0.5;
|
|
||||||
|
|
||||||
// Scale by aspect ratio to maintain uniform rounding
|
|
||||||
vec2 aspectRatio = itemSize / max(itemSize.x, itemSize.y);
|
|
||||||
uv *= aspectRatio;
|
|
||||||
|
|
||||||
// Calculate half size in normalized space
|
|
||||||
vec2 halfSize = 0.5 * aspectRatio;
|
|
||||||
|
|
||||||
// Normalize the corner radius
|
|
||||||
float normalizedRadius = cornerRadius / max(itemSize.x, itemSize.y);
|
|
||||||
|
|
||||||
// Calculate distance to rounded rectangle
|
|
||||||
float distance = roundedBoxSDF(uv, halfSize, normalizedRadius);
|
|
||||||
|
|
||||||
// Create smooth alpha mask
|
|
||||||
float smoothedAlpha = 1.0 - smoothstep(0.0, fwidth(distance), distance);
|
|
||||||
|
|
||||||
// Sample the texture
|
|
||||||
vec4 color = texture(source, qt_TexCoord0);
|
|
||||||
|
|
||||||
// Apply the rounded mask and opacity
|
|
||||||
// Make sure areas outside the rounded rect are completely transparent
|
|
||||||
float finalAlpha = color.a * smoothedAlpha * itemOpacity * ubuf.qt_Opacity;
|
|
||||||
fragColor = vec4(color.rgb * finalAlpha, finalAlpha);
|
|
||||||
}
|
|
||||||
100
Shaders/frag/wp_iris_bloom.frag
Normal file
100
Shaders/frag/wp_iris_bloom.frag
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// ===== wp_iris_bloom.frag =====
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 qt_TexCoord0;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
layout(binding = 1) uniform sampler2D source1; // Current wallpaper
|
||||||
|
layout(binding = 2) uniform sampler2D source2; // Next wallpaper
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform buf {
|
||||||
|
mat4 qt_Matrix;
|
||||||
|
float qt_Opacity;
|
||||||
|
float progress; // 0.0 -> 1.0
|
||||||
|
float centerX; // 0..1
|
||||||
|
float centerY; // 0..1
|
||||||
|
float smoothness; // 0..1 (edge softness)
|
||||||
|
float aspectRatio; // width / height
|
||||||
|
|
||||||
|
// Fill mode parameters
|
||||||
|
float fillMode; // 0=no(center), 1=crop(fill), 2=fit(contain), 3=stretch
|
||||||
|
float imageWidth1;
|
||||||
|
float imageHeight1;
|
||||||
|
float imageWidth2;
|
||||||
|
float imageHeight2;
|
||||||
|
float screenWidth;
|
||||||
|
float screenHeight;
|
||||||
|
vec4 fillColor;
|
||||||
|
} ubuf;
|
||||||
|
|
||||||
|
vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) {
|
||||||
|
vec2 transformedUV = uv;
|
||||||
|
|
||||||
|
if (ubuf.fillMode < 0.5) {
|
||||||
|
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
|
||||||
|
vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5;
|
||||||
|
vec2 imagePixel = screenPixel - imageOffset;
|
||||||
|
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
|
||||||
|
}
|
||||||
|
else if (ubuf.fillMode < 1.5) {
|
||||||
|
float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
|
||||||
|
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
|
||||||
|
vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize;
|
||||||
|
transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5;
|
||||||
|
}
|
||||||
|
else if (ubuf.fillMode < 2.5) {
|
||||||
|
float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
|
||||||
|
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
|
||||||
|
vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(scaledImageSize)) * 0.5;
|
||||||
|
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
|
||||||
|
vec2 imagePixel = (screenPixel - offset) / scale;
|
||||||
|
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
|
||||||
|
}
|
||||||
|
// else stretch
|
||||||
|
|
||||||
|
return transformedUV;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight) {
|
||||||
|
vec2 tuv = calculateUV(uv, imgWidth, imgHeight);
|
||||||
|
if (tuv.x < 0.0 || tuv.x > 1.0 || tuv.y < 0.0 || tuv.y > 1.0) {
|
||||||
|
return ubuf.fillColor;
|
||||||
|
}
|
||||||
|
return texture(tex, tuv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 uv = qt_TexCoord0;
|
||||||
|
|
||||||
|
vec4 color1 = sampleWithFillMode(source1, uv, ubuf.imageWidth1, ubuf.imageHeight1);
|
||||||
|
vec4 color2 = sampleWithFillMode(source2, uv, ubuf.imageWidth2, ubuf.imageHeight2);
|
||||||
|
|
||||||
|
// Edge softness mapping
|
||||||
|
float edgeSoft = mix(0.001, 0.45, ubuf.smoothness * ubuf.smoothness);
|
||||||
|
|
||||||
|
// Aspect-corrected coordinates so the iris stays circular
|
||||||
|
vec2 center = vec2(ubuf.centerX, ubuf.centerY);
|
||||||
|
vec2 acUv = vec2(uv.x * ubuf.aspectRatio, uv.y);
|
||||||
|
vec2 acCenter = vec2(center.x * ubuf.aspectRatio, center.y);
|
||||||
|
float dist = length(acUv - acCenter);
|
||||||
|
|
||||||
|
// Max radius needed to cover the screen from the chosen center
|
||||||
|
float maxDistX = max(center.x * ubuf.aspectRatio, (1.0 - center.x) * ubuf.aspectRatio);
|
||||||
|
float maxDistY = max(center.y, 1.0 - center.y);
|
||||||
|
float maxDist = length(vec2(maxDistX, maxDistY));
|
||||||
|
|
||||||
|
float p = ubuf.progress;
|
||||||
|
p = p * p * (3.0 - 2.0 * p);
|
||||||
|
|
||||||
|
float radius = p * maxDist - edgeSoft;
|
||||||
|
|
||||||
|
// Soft circular edge: inside -> color2 (new), outside -> color1 (old)
|
||||||
|
float t = smoothstep(radius - edgeSoft, radius + edgeSoft, dist);
|
||||||
|
vec4 col = mix(color2, color1, t);
|
||||||
|
|
||||||
|
// Exact snaps at ends
|
||||||
|
if (ubuf.progress <= 0.0) col = color1;
|
||||||
|
if (ubuf.progress >= 1.0) col = color2;
|
||||||
|
|
||||||
|
fragColor = col * ubuf.qt_Opacity;
|
||||||
|
}
|
||||||
99
Shaders/frag/wp_pixelate.frag
Normal file
99
Shaders/frag/wp_pixelate.frag
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
// ===== wp_pixelate.frag =====
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 qt_TexCoord0;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
layout(binding = 1) uniform sampler2D source1; // Current wallpaper (underlay)
|
||||||
|
layout(binding = 2) uniform sampler2D source2; // Next wallpaper (pixelated overlay → sharp)
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform buf {
|
||||||
|
mat4 qt_Matrix;
|
||||||
|
float qt_Opacity;
|
||||||
|
float progress; // 0..1
|
||||||
|
float centerX; // (unused, API compat)
|
||||||
|
float centerY; // (unused)
|
||||||
|
float smoothness; // controls starting block size (0..1)
|
||||||
|
float aspectRatio; // (unused)
|
||||||
|
|
||||||
|
// Fill mode parameters
|
||||||
|
float fillMode; // 0=no(center), 1=crop, 2=fit, 3=stretch
|
||||||
|
float imageWidth1;
|
||||||
|
float imageHeight1;
|
||||||
|
float imageWidth2;
|
||||||
|
float imageHeight2;
|
||||||
|
float screenWidth;
|
||||||
|
float screenHeight;
|
||||||
|
vec4 fillColor;
|
||||||
|
} ubuf;
|
||||||
|
|
||||||
|
vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) {
|
||||||
|
vec2 transformedUV = uv;
|
||||||
|
if (ubuf.fillMode < 0.5) {
|
||||||
|
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
|
||||||
|
vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5;
|
||||||
|
vec2 imagePixel = screenPixel - imageOffset;
|
||||||
|
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
|
||||||
|
} else if (ubuf.fillMode < 1.5) {
|
||||||
|
float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
|
||||||
|
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
|
||||||
|
vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize;
|
||||||
|
transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5;
|
||||||
|
} else if (ubuf.fillMode < 2.5) {
|
||||||
|
float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
|
||||||
|
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
|
||||||
|
vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5;
|
||||||
|
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
|
||||||
|
vec2 imagePixel = (screenPixel - offset) / scale;
|
||||||
|
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
|
||||||
|
}
|
||||||
|
return transformedUV;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float w, float h) {
|
||||||
|
vec2 tuv = calculateUV(uv, w, h);
|
||||||
|
if (tuv.x < 0.0 || tuv.x > 1.0 || tuv.y < 0.0 || tuv.y > 1.0) return ubuf.fillColor;
|
||||||
|
return texture(tex, tuv);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 quantizeUV(vec2 uv, float cellPx) {
|
||||||
|
vec2 screenSize = vec2(max(1.0, ubuf.screenWidth), max(1.0, ubuf.screenHeight));
|
||||||
|
float cell = max(1.0, ceil(cellPx)); // integer pixel cells
|
||||||
|
vec2 grid = floor(uv * screenSize / cell) * cell + 0.5 * cell;
|
||||||
|
return grid / screenSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 uv = qt_TexCoord0;
|
||||||
|
|
||||||
|
vec4 oldCol = sampleWithFillMode(source1, uv, ubuf.imageWidth1, ubuf.imageHeight1);
|
||||||
|
|
||||||
|
float p = clamp(ubuf.progress, 0.0, 1.0);
|
||||||
|
float pe = p * p * (3.0 - 2.0 * p); // smootherstep for opacity
|
||||||
|
|
||||||
|
// Screen-relative starting cell size:
|
||||||
|
// smoothness=0 → ~10% of min(screen), smoothness=1 → ~80% of min(screen)
|
||||||
|
float s = clamp(ubuf.smoothness, 0.0, 1.0);
|
||||||
|
float minSide = min(max(1.0, ubuf.screenWidth), max(1.0, ubuf.screenHeight));
|
||||||
|
float startPx = mix(minSide * 0.10, minSide * 0.80, s); // big and obvious even on small screens
|
||||||
|
|
||||||
|
// Cell size shrinks continuously from startPx → 1 as p grows
|
||||||
|
float cellPx = mix(startPx, 1.0, p);
|
||||||
|
|
||||||
|
// Sample next as pixelated overlay
|
||||||
|
vec2 uvq = quantizeUV(uv, cellPx);
|
||||||
|
vec4 newPix = sampleWithFillMode(source2, uvq, ubuf.imageWidth2, ubuf.imageHeight2);
|
||||||
|
|
||||||
|
// As we approach the end, sharpen the next from pixelated → full-res
|
||||||
|
float sharpen = smoothstep(0.75, 1.0, p); // only near the end
|
||||||
|
vec4 newFull = sampleWithFillMode(source2, uv, ubuf.imageWidth2, ubuf.imageHeight2);
|
||||||
|
vec4 newCol = mix(newPix, newFull, sharpen);
|
||||||
|
|
||||||
|
vec4 outColor = mix(oldCol, newCol, pe);
|
||||||
|
|
||||||
|
// Snaps
|
||||||
|
if (p <= 0.0) outColor = oldCol;
|
||||||
|
if (p >= 1.0) outColor = newFull;
|
||||||
|
|
||||||
|
fragColor = outColor * ubuf.qt_Opacity;
|
||||||
|
}
|
||||||
103
Shaders/frag/wp_portal.frag
Normal file
103
Shaders/frag/wp_portal.frag
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
// ===== wp_portal.frag =====
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 qt_TexCoord0;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
layout(binding = 1) uniform sampler2D source1; // Current wallpaper (shrinks away)
|
||||||
|
layout(binding = 2) uniform sampler2D source2; // Next wallpaper (underneath)
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform buf {
|
||||||
|
mat4 qt_Matrix;
|
||||||
|
float qt_Opacity;
|
||||||
|
float progress; // 0..1
|
||||||
|
float centerX; // 0..1
|
||||||
|
float centerY; // 0..1
|
||||||
|
float smoothness; // 0..1 (edge softness)
|
||||||
|
float aspectRatio; // width / height
|
||||||
|
|
||||||
|
// Fill mode parameters
|
||||||
|
float fillMode; // 0=no(center), 1=crop(fill), 2=fit(contain), 3=stretch
|
||||||
|
float imageWidth1;
|
||||||
|
float imageHeight1;
|
||||||
|
float imageWidth2;
|
||||||
|
float imageHeight2;
|
||||||
|
float screenWidth;
|
||||||
|
float screenHeight;
|
||||||
|
vec4 fillColor;
|
||||||
|
} ubuf;
|
||||||
|
|
||||||
|
vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) {
|
||||||
|
vec2 transformedUV = uv;
|
||||||
|
|
||||||
|
if (ubuf.fillMode < 0.5) {
|
||||||
|
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
|
||||||
|
vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5;
|
||||||
|
vec2 imagePixel = screenPixel - imageOffset;
|
||||||
|
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
|
||||||
|
}
|
||||||
|
else if (ubuf.fillMode < 1.5) {
|
||||||
|
float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
|
||||||
|
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
|
||||||
|
vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize;
|
||||||
|
transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5;
|
||||||
|
}
|
||||||
|
else if (ubuf.fillMode < 2.5) {
|
||||||
|
float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
|
||||||
|
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
|
||||||
|
vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5;
|
||||||
|
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
|
||||||
|
vec2 imagePixel = (screenPixel - offset) / scale;
|
||||||
|
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
|
||||||
|
}
|
||||||
|
// else: stretch
|
||||||
|
|
||||||
|
return transformedUV;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float w, float h) {
|
||||||
|
vec2 tuv = calculateUV(uv, w, h);
|
||||||
|
if (tuv.x < 0.0 || tuv.x > 1.0 || tuv.y < 0.0 || tuv.y > 1.0) return ubuf.fillColor;
|
||||||
|
return texture(tex, tuv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 uv = qt_TexCoord0;
|
||||||
|
|
||||||
|
vec4 oldCol = sampleWithFillMode(source1, uv, ubuf.imageWidth1, ubuf.imageHeight1);
|
||||||
|
vec4 newCol = sampleWithFillMode(source2, uv, ubuf.imageWidth2, ubuf.imageHeight2);
|
||||||
|
|
||||||
|
// Edge softness
|
||||||
|
float edgeSoft = mix(0.001, 0.45, ubuf.smoothness * ubuf.smoothness);
|
||||||
|
|
||||||
|
// Aspect-corrected distance from center (keep circle round)
|
||||||
|
vec2 center = vec2(ubuf.centerX, ubuf.centerY);
|
||||||
|
vec2 acUv = vec2(uv.x * ubuf.aspectRatio, uv.y);
|
||||||
|
vec2 acCenter = vec2(center.x * ubuf.aspectRatio, center.y);
|
||||||
|
float dist = length(acUv - acCenter);
|
||||||
|
|
||||||
|
// Max radius from center to cover screen
|
||||||
|
float maxDistX = max(center.x * ubuf.aspectRatio, (1.0 - center.x) * ubuf.aspectRatio);
|
||||||
|
float maxDistY = max(center.y, 1.0 - center.y);
|
||||||
|
float maxDist = length(vec2(maxDistX, maxDistY));
|
||||||
|
|
||||||
|
// Smooth easing for a friendly feel
|
||||||
|
float p = ubuf.progress;
|
||||||
|
p = p * p * (3.0 - 2.0 * p);
|
||||||
|
|
||||||
|
// Portal radius shrinks from full to zero (bias by edgeSoft so it vanishes cleanly)
|
||||||
|
float radius = (1.0 - p) * (maxDist + edgeSoft) - edgeSoft;
|
||||||
|
|
||||||
|
// Inside circle = old wallpaper; outside = new wallpaper
|
||||||
|
float t = smoothstep(radius - edgeSoft, radius + edgeSoft, dist);
|
||||||
|
// When radius is large: t ~ 0 inside (old), ~1 outside (new)
|
||||||
|
// As radius shrinks, old area collapses to center.
|
||||||
|
|
||||||
|
vec4 col = mix(oldCol, newCol, t);
|
||||||
|
|
||||||
|
// Snaps
|
||||||
|
if (ubuf.progress <= 0.0) col = oldCol; // full old at start
|
||||||
|
if (ubuf.progress >= 1.0) col = newCol; // full new at end
|
||||||
|
|
||||||
|
fragColor = col * ubuf.qt_Opacity;
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
BIN
Shaders/qsb/wp_iris_bloom.frag.qsb
Normal file
BIN
Shaders/qsb/wp_iris_bloom.frag.qsb
Normal file
Binary file not shown.
BIN
Shaders/qsb/wp_pixelate.frag.qsb
Normal file
BIN
Shaders/qsb/wp_pixelate.frag.qsb
Normal file
Binary file not shown.
BIN
Shaders/qsb/wp_portal.frag.qsb
Normal file
BIN
Shaders/qsb/wp_portal.frag.qsb
Normal file
Binary file not shown.
@@ -159,8 +159,8 @@ Item {
|
|||||||
|
|
||||||
imageSource: artUrl || lastValidArtUrl || ""
|
imageSource: artUrl || lastValidArtUrl || ""
|
||||||
fallbackIcon: "album"
|
fallbackIcon: "album"
|
||||||
borderColor: Theme.primary
|
border.color: Theme.primary
|
||||||
borderWidth: 2
|
border.width: 2
|
||||||
|
|
||||||
onImageSourceChanged: {
|
onImageSourceChanged: {
|
||||||
if (imageSource && imageStatus !== Image.Error) {
|
if (imageSource && imageStatus !== Image.Error) {
|
||||||
|
|||||||
@@ -1,92 +1,84 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Item {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string imageSource: ""
|
property string imageSource: ""
|
||||||
property string fallbackIcon: "notifications"
|
property string fallbackIcon: "notifications"
|
||||||
property string fallbackText: ""
|
property string fallbackText: ""
|
||||||
property bool hasImage: imageSource !== ""
|
property bool hasImage: imageSource !== ""
|
||||||
property alias imageStatus: sourceImage.status
|
property alias imageStatus: internalImage.status
|
||||||
property color borderColor: "transparent"
|
|
||||||
property real borderWidth: 0
|
|
||||||
property real imageOpacity: 1.0
|
|
||||||
|
|
||||||
width: 64
|
radius: width / 2
|
||||||
height: 64
|
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1)
|
||||||
|
border.color: "transparent"
|
||||||
|
border.width: 0
|
||||||
|
|
||||||
Rectangle {
|
Image {
|
||||||
id: background
|
id: internalImage
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1)
|
anchors.margins: 2
|
||||||
radius: width * 0.5
|
asynchronous: true
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
smooth: true
|
||||||
|
mipmap: true
|
||||||
|
cache: true
|
||||||
|
visible: false
|
||||||
|
source: root.imageSource
|
||||||
|
|
||||||
Image {
|
Component.onCompleted: {
|
||||||
id: sourceImage
|
sourceSize.width = 128
|
||||||
anchors.fill: parent
|
sourceSize.height = 128
|
||||||
anchors.margins: 2
|
|
||||||
source: root.imageSource
|
|
||||||
visible: false
|
|
||||||
fillMode: Image.PreserveAspectCrop
|
|
||||||
smooth: true
|
|
||||||
mipmap: true
|
|
||||||
asynchronous: true
|
|
||||||
antialiasing: true
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
sourceSize.width = 128
|
|
||||||
sourceSize.height = 128
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ShaderEffect {
|
MultiEffect {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 2
|
anchors.margins: 2
|
||||||
visible: sourceImage.status === Image.Ready && root.imageSource !== ""
|
source: internalImage
|
||||||
|
maskEnabled: true
|
||||||
|
maskSource: circularMask
|
||||||
|
visible: internalImage.status === Image.Ready && root.imageSource !== ""
|
||||||
|
maskThresholdMin: 0.5
|
||||||
|
maskSpreadAtMin: 1
|
||||||
|
}
|
||||||
|
|
||||||
property var source: ShaderEffectSource {
|
Item {
|
||||||
sourceItem: sourceImage
|
id: circularMask
|
||||||
hideSource: true
|
width: parent.width - 4
|
||||||
live: true
|
height: parent.height - 4
|
||||||
recursive: false
|
anchors.centerIn: parent
|
||||||
format: ShaderEffectSource.RGBA
|
layer.enabled: true
|
||||||
}
|
layer.smooth: true
|
||||||
|
visible: false
|
||||||
property real imageOpacity: root.imageOpacity
|
|
||||||
|
|
||||||
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/circled_image.frag.qsb")
|
|
||||||
supportsAtlasTextures: false
|
|
||||||
blending: true
|
|
||||||
}
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
name: root.fallbackIcon
|
|
||||||
size: parent.width * 0.5
|
|
||||||
color: Theme.surfaceVariantText
|
|
||||||
visible: sourceImage.status !== Image.Ready && root.imageSource === "" && root.fallbackIcon !== ""
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
visible: root.imageSource === "" && root.fallbackIcon === "" && root.fallbackText !== ""
|
|
||||||
text: root.fallbackText
|
|
||||||
font.pixelSize: Math.max(12, parent.width * 0.36)
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.primaryText
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: width / 2
|
radius: width / 2
|
||||||
color: "transparent"
|
color: "black"
|
||||||
border.color: root.borderColor !== "transparent" ? root.borderColor : Theme.popupBackground()
|
|
||||||
border.width: root.hasImage && sourceImage.status === Image.Ready ? (root.borderWidth > 0 ? root.borderWidth : 3) : 0
|
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: root.fallbackIcon
|
||||||
|
size: parent.width * 0.5
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
visible: internalImage.status !== Image.Ready && root.imageSource === "" && root.fallbackIcon !== ""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: root.imageSource === "" && root.fallbackIcon === "" && root.fallbackText !== ""
|
||||||
|
text: root.fallbackText
|
||||||
|
font.pixelSize: Math.max(12, parent.width * 0.36)
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.primaryText
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -34,6 +34,8 @@ ShellRoot {
|
|||||||
PortalService.init()
|
PortalService.init()
|
||||||
// Initialize DisplayService night mode functionality
|
// Initialize DisplayService night mode functionality
|
||||||
DisplayService.nightModeEnabled
|
DisplayService.nightModeEnabled
|
||||||
|
// Initialize WallpaperCyclingService
|
||||||
|
WallpaperCyclingService.cyclingActive
|
||||||
}
|
}
|
||||||
|
|
||||||
WallpaperBackground {}
|
WallpaperBackground {}
|
||||||
|
|||||||
Reference in New Issue
Block a user