1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-04 12:52:06 -04:00
Files
DankMaterialShell/quickshell/Modules/Settings/WallpaperTab.qml
Youseffo13 86dfe7dd3f Added Missing i18n strings (#1729)
* inverted dock visibility and position option

* added missing I18n strings

* added missing i18n strings

* added i18n strings

* Added missing i18n strings

* updated translations

* Update it.json
2026-02-18 18:28:57 -05:00

1357 lines
68 KiB
QML

import QtQuick
import QtQuick.Effects
import Quickshell
import qs.Common
import qs.Modals.FileBrowser
import qs.Services
import qs.Widgets
import qs.Modules.Settings.Widgets
Item {
id: root
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
property var parentModal: null
property string selectedMonitorName: {
var screens = Quickshell.screens;
return screens.length > 0 ? screens[0].name : "";
}
property string currentWallpaper: {
if (!SessionData.perMonitorWallpaper)
return SessionData.wallpaperPath;
var map = SessionData.monitorWallpapers;
var screens = Quickshell.screens;
for (var i = 0; i < screens.length; i++) {
if (screens[i].name !== selectedMonitorName)
continue;
var screen = screens[i];
if (map[screen.name] !== undefined)
return map[screen.name];
if (screen.model && map[screen.model] !== undefined)
return map[screen.model];
var displayName = SettingsData.getScreenDisplayName(screen);
if (displayName && map[displayName] !== undefined)
return map[displayName];
break;
}
return SessionData.wallpaperPath;
}
Component.onCompleted: {
WallpaperCyclingService.cyclingActive;
}
DankFlickable {
anchors.fill: parent
clip: true
contentHeight: mainColumn.height + Theme.spacingXL
contentWidth: width
Column {
id: mainColumn
topPadding: 4
width: Math.min(550, parent.width - Theme.spacingL * 2)
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXL
SettingsCard {
tab: "wallpaper"
tags: ["background", "image", "picture"]
title: I18n.tr("Wallpaper")
settingKey: "wallpaper"
iconName: "wallpaper"
Row {
width: parent.width
spacing: Theme.spacingL
StyledRect {
id: wallpaperPreview
width: 160
height: 90
radius: Theme.cornerRadius
color: Theme.surfaceVariant
Image {
anchors.fill: parent
anchors.margins: 1
source: {
var wp = root.currentWallpaper;
if (wp === "" || wp.startsWith("#"))
return "";
if (wp.startsWith("file://"))
wp = wp.substring(7);
return "file://" + wp.split('/').map(s => encodeURIComponent(s)).join('/');
}
fillMode: Image.PreserveAspectCrop
visible: root.currentWallpaper !== "" && !root.currentWallpaper.startsWith("#")
sourceSize.width: 160
sourceSize.height: 160
asynchronous: true
layer.enabled: true
layer.effect: MultiEffect {
maskEnabled: true
maskSource: wallpaperMask
maskThresholdMin: 0.5
maskSpreadAtMin: 1
}
}
Rectangle {
anchors.fill: parent
anchors.margins: 1
radius: Theme.cornerRadius - 1
color: root.currentWallpaper.startsWith("#") ? root.currentWallpaper : "transparent"
visible: root.currentWallpaper !== "" && root.currentWallpaper.startsWith("#")
}
Rectangle {
id: wallpaperMask
anchors.fill: parent
anchors.margins: 1
radius: Theme.cornerRadius - 1
color: "black"
visible: false
layer.enabled: true
}
DankIcon {
anchors.centerIn: parent
name: "image"
size: Theme.iconSizeLarge + 8
color: Theme.surfaceVariantText
visible: root.currentWallpaper === ""
}
Rectangle {
anchors.fill: parent
anchors.margins: 1
radius: Theme.cornerRadius - 1
color: Qt.rgba(0, 0, 0, 0.7)
visible: wallpaperMouseArea.containsMouse
Row {
anchors.centerIn: parent
spacing: 4
Rectangle {
width: 32
height: 32
radius: 16
color: Qt.rgba(255, 255, 255, 0.9)
DankIcon {
anchors.centerIn: parent
name: "folder_open"
size: 18
color: "black"
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.openMainWallpaperBrowser()
}
}
Rectangle {
width: 32
height: 32
radius: 16
color: Qt.rgba(255, 255, 255, 0.9)
DankIcon {
anchors.centerIn: parent
name: "palette"
size: 18
color: "black"
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
if (!PopoutService.colorPickerModal)
return;
PopoutService.colorPickerModal.selectedColor = root.currentWallpaper.startsWith("#") ? root.currentWallpaper : Theme.primary;
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Choose Wallpaper Color", "wallpaper color picker title");
PopoutService.colorPickerModal.onColorSelectedCallback = function (selectedColor) {
if (SessionData.perMonitorWallpaper) {
SessionData.setMonitorWallpaper(selectedMonitorName, selectedColor);
} else {
SessionData.setWallpaperColor(selectedColor);
}
};
PopoutService.colorPickerModal.show();
}
}
}
Rectangle {
width: 32
height: 32
radius: 16
color: Qt.rgba(255, 255, 255, 0.9)
visible: root.currentWallpaper !== ""
DankIcon {
anchors.centerIn: parent
name: "clear"
size: 18
color: "black"
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
if (SessionData.perMonitorWallpaper) {
SessionData.setMonitorWallpaper(selectedMonitorName, "");
} else {
if (Theme.currentTheme === Theme.dynamic)
Theme.switchTheme("blue");
SessionData.clearWallpaper();
}
}
}
}
}
}
MouseArea {
id: wallpaperMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
propagateComposedEvents: true
acceptedButtons: Qt.NoButton
}
}
Column {
width: parent.width - 160 - Theme.spacingL
spacing: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
StyledText {
text: root.currentWallpaper ? root.currentWallpaper.split('/').pop() : I18n.tr("No wallpaper selected")
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
elide: Text.ElideMiddle
maximumLineCount: 1
width: parent.width
horizontalAlignment: Text.AlignLeft
}
StyledText {
text: root.currentWallpaper
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
elide: Text.ElideMiddle
maximumLineCount: 1
width: parent.width
horizontalAlignment: Text.AlignLeft
visible: root.currentWallpaper !== ""
}
Row {
anchors.left: parent.left
spacing: Theme.spacingS
layoutDirection: I18n.isRtl ? Qt.RightToLeft : Qt.LeftToRight
visible: root.currentWallpaper !== ""
DankActionButton {
buttonSize: 32
iconName: "skip_previous"
iconSize: Theme.iconSizeSmall
enabled: root.currentWallpaper && !root.currentWallpaper.startsWith("#") && !root.currentWallpaper.startsWith("we")
opacity: enabled ? 1 : 0.5
backgroundColor: Theme.surfaceContainerHigh
iconColor: Theme.surfaceText
onClicked: {
if (SessionData.perMonitorWallpaper) {
WallpaperCyclingService.cyclePrevForMonitor(selectedMonitorName);
} else {
WallpaperCyclingService.cyclePrevManually();
}
}
}
DankActionButton {
buttonSize: 32
iconName: "skip_next"
iconSize: Theme.iconSizeSmall
enabled: root.currentWallpaper && !root.currentWallpaper.startsWith("#") && !root.currentWallpaper.startsWith("we")
opacity: enabled ? 1 : 0.5
backgroundColor: Theme.surfaceContainerHigh
iconColor: Theme.surfaceText
onClicked: {
if (SessionData.perMonitorWallpaper) {
WallpaperCyclingService.cycleNextForMonitor(selectedMonitorName);
} else {
WallpaperCyclingService.cycleNextManually();
}
}
}
}
}
}
Item {
width: parent.width
height: fillModeGroup.height
visible: root.currentWallpaper !== "" && !root.currentWallpaper.startsWith("#")
DankButtonGroup {
id: fillModeGroup
property var internalModes: ["Stretch", "Fit", "Fill", "Tile", "TileVertically", "TileHorizontally", "Pad"]
anchors.horizontalCenter: parent.horizontalCenter
model: [I18n.tr("Stretch", "wallpaper fill mode"), I18n.tr("Fit", "wallpaper fill mode"), I18n.tr("Fill", "wallpaper fill mode"), I18n.tr("Tile", "wallpaper fill mode"), I18n.tr("Tile V", "wallpaper fill mode"), I18n.tr("Tile H", "wallpaper fill mode"), I18n.tr("Pad", "wallpaper fill mode")]
selectionMode: "single"
buttonHeight: 28
minButtonWidth: 48
buttonPadding: Theme.spacingS
checkIconSize: 0
textSize: Theme.fontSizeSmall
checkEnabled: false
currentIndex: {
var mode = SessionData.perMonitorWallpaper ? SessionData.getMonitorWallpaperFillMode(selectedMonitorName) : SettingsData.wallpaperFillMode;
return internalModes.indexOf(mode);
}
onSelectionChanged: (index, selected) => {
if (!selected)
return;
if (SessionData.perMonitorWallpaper) {
SessionData.setMonitorWallpaperFillMode(selectedMonitorName, internalModes[index]);
} else {
SettingsData.set("wallpaperFillMode", internalModes[index]);
}
}
Connections {
target: SettingsData
function onWallpaperFillModeChanged() {
if (SessionData.perMonitorWallpaper)
return;
fillModeGroup.currentIndex = fillModeGroup.internalModes.indexOf(SettingsData.wallpaperFillMode);
}
}
Connections {
target: root
function onSelectedMonitorNameChanged() {
if (!SessionData.perMonitorWallpaper)
return;
fillModeGroup.currentIndex = Qt.binding(() => {
var mode = SessionData.perMonitorWallpaper ? SessionData.getMonitorWallpaperFillMode(selectedMonitorName) : SettingsData.wallpaperFillMode;
return fillModeGroup.internalModes.indexOf(mode);
});
}
}
}
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.2
visible: SessionData.wallpaperPath !== ""
}
SettingsToggleRow {
tab: "wallpaper"
tags: ["per-mode", "light", "dark", "theme"]
settingKey: "perModeWallpaper"
visible: SessionData.wallpaperPath !== ""
text: I18n.tr("Per-Mode Wallpapers")
description: I18n.tr("Set different wallpapers for light and dark mode")
checked: SessionData.perModeWallpaper
onToggled: toggled => SessionData.setPerModeWallpaper(toggled)
}
Column {
width: parent.width
spacing: Theme.spacingM
visible: SessionData.perModeWallpaper
leftPadding: Theme.spacingM
rightPadding: Theme.spacingM
Row {
width: parent.width - Theme.spacingM * 2
spacing: Theme.spacingL
Column {
width: (parent.width - Theme.spacingL) / 2
spacing: Theme.spacingS
StyledText {
text: I18n.tr("Light Mode")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
}
StyledRect {
width: parent.width
height: width * 9 / 16
radius: Theme.cornerRadius
color: Theme.surfaceVariant
CachingImage {
anchors.fill: parent
anchors.margins: 1
imagePath: {
var lightWallpaper = SessionData.wallpaperPathLight;
return (lightWallpaper !== "" && !lightWallpaper.startsWith("#")) ? lightWallpaper : "";
}
fillMode: Image.PreserveAspectCrop
visible: {
var lightWallpaper = SessionData.wallpaperPathLight;
return lightWallpaper !== "" && !lightWallpaper.startsWith("#");
}
maxCacheSize: 160
layer.enabled: true
layer.effect: MultiEffect {
maskEnabled: true
maskSource: lightMask
maskThresholdMin: 0.5
maskSpreadAtMin: 1
}
}
Rectangle {
anchors.fill: parent
anchors.margins: 1
radius: Theme.cornerRadius - 1
color: {
var lightWallpaper = SessionData.wallpaperPathLight;
return lightWallpaper.startsWith("#") ? lightWallpaper : "transparent";
}
visible: {
var lightWallpaper = SessionData.wallpaperPathLight;
return lightWallpaper !== "" && lightWallpaper.startsWith("#");
}
}
Rectangle {
id: lightMask
anchors.fill: parent
anchors.margins: 1
radius: Theme.cornerRadius - 1
color: "black"
visible: false
layer.enabled: true
}
DankIcon {
anchors.centerIn: parent
name: "light_mode"
size: Theme.iconSizeLarge
color: Theme.surfaceVariantText
visible: SessionData.wallpaperPathLight === ""
}
Rectangle {
anchors.fill: parent
anchors.margins: 1
radius: Theme.cornerRadius - 1
color: Qt.rgba(0, 0, 0, 0.7)
visible: lightModeMouseArea.containsMouse
Row {
anchors.centerIn: parent
spacing: 4
Rectangle {
width: 28
height: 28
radius: 14
color: Qt.rgba(255, 255, 255, 0.9)
DankIcon {
anchors.centerIn: parent
name: "folder_open"
size: 16
color: "black"
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.openLightWallpaperBrowser()
}
}
Rectangle {
width: 28
height: 28
radius: 14
color: Qt.rgba(255, 255, 255, 0.9)
DankIcon {
anchors.centerIn: parent
name: "palette"
size: 16
color: "black"
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
if (!PopoutService.colorPickerModal)
return;
var lightWallpaper = SessionData.wallpaperPathLight;
PopoutService.colorPickerModal.selectedColor = lightWallpaper.startsWith("#") ? lightWallpaper : Theme.primary;
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Choose Light Mode Color", "light mode wallpaper color picker title");
PopoutService.colorPickerModal.onColorSelectedCallback = function (selectedColor) {
SessionData.wallpaperPathLight = selectedColor;
SessionData.syncWallpaperForCurrentMode();
SessionData.saveSettings();
};
PopoutService.colorPickerModal.show();
}
}
}
Rectangle {
width: 28
height: 28
radius: 14
color: Qt.rgba(255, 255, 255, 0.9)
visible: SessionData.wallpaperPathLight !== ""
DankIcon {
anchors.centerIn: parent
name: "clear"
size: 16
color: "black"
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
SessionData.wallpaperPathLight = "";
SessionData.syncWallpaperForCurrentMode();
SessionData.saveSettings();
}
}
}
}
}
MouseArea {
id: lightModeMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
propagateComposedEvents: true
acceptedButtons: Qt.NoButton
}
}
StyledText {
text: {
var lightWallpaper = SessionData.wallpaperPathLight;
return lightWallpaper ? lightWallpaper.split('/').pop() : I18n.tr("Not set", "wallpaper not set label");
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
elide: Text.ElideMiddle
maximumLineCount: 1
width: parent.width
}
}
Column {
width: (parent.width - Theme.spacingL) / 2
spacing: Theme.spacingS
StyledText {
text: I18n.tr("Dark Mode")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
}
StyledRect {
width: parent.width
height: width * 9 / 16
radius: Theme.cornerRadius
color: Theme.surfaceVariant
CachingImage {
anchors.fill: parent
anchors.margins: 1
imagePath: {
var darkWallpaper = SessionData.wallpaperPathDark;
return (darkWallpaper !== "" && !darkWallpaper.startsWith("#")) ? darkWallpaper : "";
}
fillMode: Image.PreserveAspectCrop
visible: {
var darkWallpaper = SessionData.wallpaperPathDark;
return darkWallpaper !== "" && !darkWallpaper.startsWith("#");
}
maxCacheSize: 160
layer.enabled: true
layer.effect: MultiEffect {
maskEnabled: true
maskSource: darkMask
maskThresholdMin: 0.5
maskSpreadAtMin: 1
}
}
Rectangle {
anchors.fill: parent
anchors.margins: 1
radius: Theme.cornerRadius - 1
color: {
var darkWallpaper = SessionData.wallpaperPathDark;
return darkWallpaper.startsWith("#") ? darkWallpaper : "transparent";
}
visible: {
var darkWallpaper = SessionData.wallpaperPathDark;
return darkWallpaper !== "" && darkWallpaper.startsWith("#");
}
}
Rectangle {
id: darkMask
anchors.fill: parent
anchors.margins: 1
radius: Theme.cornerRadius - 1
color: "black"
visible: false
layer.enabled: true
}
DankIcon {
anchors.centerIn: parent
name: "dark_mode"
size: Theme.iconSizeLarge
color: Theme.surfaceVariantText
visible: SessionData.wallpaperPathDark === ""
}
Rectangle {
anchors.fill: parent
anchors.margins: 1
radius: Theme.cornerRadius - 1
color: Qt.rgba(0, 0, 0, 0.7)
visible: darkModeMouseArea.containsMouse
Row {
anchors.centerIn: parent
spacing: 4
Rectangle {
width: 28
height: 28
radius: 14
color: Qt.rgba(255, 255, 255, 0.9)
DankIcon {
anchors.centerIn: parent
name: "folder_open"
size: 16
color: "black"
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.openDarkWallpaperBrowser()
}
}
Rectangle {
width: 28
height: 28
radius: 14
color: Qt.rgba(255, 255, 255, 0.9)
DankIcon {
anchors.centerIn: parent
name: "palette"
size: 16
color: "black"
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
if (!PopoutService.colorPickerModal)
return;
var darkWallpaper = SessionData.wallpaperPathDark;
PopoutService.colorPickerModal.selectedColor = darkWallpaper.startsWith("#") ? darkWallpaper : Theme.primary;
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Choose Dark Mode Color", "dark mode wallpaper color picker title");
PopoutService.colorPickerModal.onColorSelectedCallback = function (selectedColor) {
SessionData.wallpaperPathDark = selectedColor;
SessionData.syncWallpaperForCurrentMode();
SessionData.saveSettings();
};
PopoutService.colorPickerModal.show();
}
}
}
Rectangle {
width: 28
height: 28
radius: 14
color: Qt.rgba(255, 255, 255, 0.9)
visible: SessionData.wallpaperPathDark !== ""
DankIcon {
anchors.centerIn: parent
name: "clear"
size: 16
color: "black"
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
SessionData.wallpaperPathDark = "";
SessionData.syncWallpaperForCurrentMode();
SessionData.saveSettings();
}
}
}
}
}
MouseArea {
id: darkModeMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
propagateComposedEvents: true
acceptedButtons: Qt.NoButton
}
}
StyledText {
text: {
var darkWallpaper = SessionData.wallpaperPathDark;
return darkWallpaper ? darkWallpaper.split('/').pop() : I18n.tr("Not set", "wallpaper not set label");
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
elide: Text.ElideMiddle
maximumLineCount: 1
width: parent.width
}
}
}
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.2
visible: CompositorService.isNiri
}
SettingsToggleRow {
tab: "wallpaper"
tags: ["blur", "overview", "niri"]
settingKey: "blurWallpaperOnOverview"
visible: CompositorService.isNiri
text: I18n.tr("Blur on Overview")
description: I18n.tr("Blur wallpaper when niri overview is open")
checked: SettingsData.blurWallpaperOnOverview
onToggled: checked => SettingsData.set("blurWallpaperOnOverview", checked)
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.2
visible: SessionData.wallpaperPath !== ""
}
SettingsToggleRow {
tab: "wallpaper"
tags: ["per-monitor", "multi-monitor", "display"]
settingKey: "perMonitorWallpaper"
visible: SessionData.wallpaperPath !== ""
text: I18n.tr("Per-Monitor Wallpapers")
description: I18n.tr("Set different wallpapers for each connected monitor")
checked: SessionData.perMonitorWallpaper
onToggled: toggled => SessionData.setPerMonitorWallpaper(toggled)
}
Column {
width: parent.width
spacing: Theme.spacingM
visible: SessionData.perMonitorWallpaper
leftPadding: Theme.spacingM
rightPadding: Theme.spacingM
SettingsDropdownRow {
tab: "wallpaper"
tags: ["monitor", "display", "screen"]
settingKey: "selectedMonitor"
width: parent.width - Theme.spacingM * 2
text: I18n.tr("Wallpaper Monitor")
description: I18n.tr("Select monitor to configure wallpaper")
currentValue: {
var screens = Quickshell.screens;
for (var i = 0; i < screens.length; i++) {
if (screens[i].name === selectedMonitorName) {
return SettingsData.getScreenDisplayName(screens[i]);
}
}
return I18n.tr("No monitors", "no monitors available label");
}
options: {
var screenNames = [];
var screens = Quickshell.screens;
for (var i = 0; i < screens.length; i++) {
screenNames.push(SettingsData.getScreenDisplayName(screens[i]));
}
return screenNames;
}
onValueChanged: value => {
var screens = Quickshell.screens;
for (var i = 0; i < screens.length; i++) {
if (SettingsData.getScreenDisplayName(screens[i]) === value) {
selectedMonitorName = screens[i].name;
return;
}
}
}
}
SettingsDropdownRow {
tab: "wallpaper"
tags: ["matugen", "target", "monitor", "theming"]
settingKey: "matugenTargetMonitor"
width: parent.width - Theme.spacingM * 2
text: I18n.tr("Matugen Target Monitor")
description: I18n.tr("Monitor whose wallpaper drives dynamic theming colors")
currentValue: {
var screens = Quickshell.screens;
if (!SettingsData.matugenTargetMonitor || SettingsData.matugenTargetMonitor === "") {
return screens.length > 0 ? SettingsData.getScreenDisplayName(screens[0]) + " " + I18n.tr("(Default)", "default monitor label suffix") : I18n.tr("No monitors", "no monitors available label");
}
for (var i = 0; i < screens.length; i++) {
if (screens[i].name === SettingsData.matugenTargetMonitor) {
return SettingsData.getScreenDisplayName(screens[i]);
}
}
return SettingsData.matugenTargetMonitor;
}
options: {
var screenNames = [];
var screens = Quickshell.screens;
for (var i = 0; i < screens.length; i++) {
var label = SettingsData.getScreenDisplayName(screens[i]);
if (i === 0 && (!SettingsData.matugenTargetMonitor || SettingsData.matugenTargetMonitor === "")) {
label += " " + I18n.tr("(Default)", "default monitor label suffix");
}
screenNames.push(label);
}
return screenNames;
}
onValueChanged: value => {
var cleanValue = value.replace(" " + I18n.tr("(Default)", "default monitor label suffix"), "");
var screens = Quickshell.screens;
for (var i = 0; i < screens.length; i++) {
if (SettingsData.getScreenDisplayName(screens[i]) === cleanValue) {
SettingsData.setMatugenTargetMonitor(screens[i].name);
return;
}
}
}
}
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.2
visible: (SessionData.wallpaperPath !== "" || SessionData.perMonitorWallpaper) && !SessionData.perModeWallpaper
}
SettingsToggleRow {
id: cyclingToggle
tab: "wallpaper"
tags: ["cycling", "automatic", "rotate", "slideshow"]
settingKey: "wallpaperCyclingEnabled"
visible: (SessionData.wallpaperPath !== "" || SessionData.perMonitorWallpaper) && !SessionData.perModeWallpaper
text: I18n.tr("Automatic Cycling")
description: I18n.tr("Automatically cycle through wallpapers in the same folder")
checked: SessionData.perMonitorWallpaper ? SessionData.getMonitorCyclingSettings(selectedMonitorName).enabled : SessionData.wallpaperCyclingEnabled
onToggled: toggled => {
if (SessionData.perMonitorWallpaper) {
SessionData.setMonitorCyclingEnabled(selectedMonitorName, toggled);
} else {
SessionData.setWallpaperCyclingEnabled(toggled);
}
}
Connections {
target: root
function onSelectedMonitorNameChanged() {
cyclingToggle.checked = Qt.binding(() => {
return SessionData.perMonitorWallpaper ? SessionData.getMonitorCyclingSettings(selectedMonitorName).enabled : SessionData.wallpaperCyclingEnabled;
});
}
}
}
Column {
width: parent.width
spacing: Theme.spacingM
visible: SessionData.perMonitorWallpaper ? SessionData.getMonitorCyclingSettings(selectedMonitorName).enabled : SessionData.wallpaperCyclingEnabled
leftPadding: Theme.spacingM
rightPadding: Theme.spacingM
Row {
spacing: Theme.spacingL
width: parent.width - Theme.spacingM * 2
StyledText {
text: I18n.tr("Mode:")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Item {
width: 200
height: 45 + Theme.spacingM
DankTabBar {
id: modeTabBar
width: 200
height: 45
model: [
{
"text": I18n.tr("Interval", "wallpaper cycling mode tab"),
"icon": "schedule"
},
{
"text": I18n.tr("Time", "wallpaper cycling mode tab"),
"icon": "access_time"
}
]
currentIndex: {
if (SessionData.perMonitorWallpaper) {
return SessionData.getMonitorCyclingSettings(selectedMonitorName).mode === "time" ? 1 : 0;
}
return SessionData.wallpaperCyclingMode === "time" ? 1 : 0;
}
onTabClicked: index => {
if (SessionData.perMonitorWallpaper) {
SessionData.setMonitorCyclingMode(selectedMonitorName, index === 1 ? "time" : "interval");
} else {
SessionData.setWallpaperCyclingMode(index === 1 ? "time" : "interval");
}
}
Connections {
target: root
function onSelectedMonitorNameChanged() {
modeTabBar.currentIndex = Qt.binding(() => {
if (SessionData.perMonitorWallpaper) {
return SessionData.getMonitorCyclingSettings(selectedMonitorName).mode === "time" ? 1 : 0;
}
return SessionData.wallpaperCyclingMode === "time" ? 1 : 0;
});
Qt.callLater(modeTabBar.updateIndicator);
}
}
}
}
}
SettingsDropdownRow {
id: intervalDropdown
property var intervalOptions: [I18n.tr("5 seconds", "wallpaper interval"), I18n.tr("10 seconds", "wallpaper interval"), I18n.tr("15 seconds", "wallpaper interval"), I18n.tr("20 seconds", "wallpaper interval"), I18n.tr("25 seconds", "wallpaper interval"), I18n.tr("30 seconds", "wallpaper interval"), I18n.tr("35 seconds", "wallpaper interval"), I18n.tr("40 seconds", "wallpaper interval"), I18n.tr("45 seconds", "wallpaper interval"), I18n.tr("50 seconds", "wallpaper interval"), I18n.tr("55 seconds", "wallpaper interval"), I18n.tr("1 minute", "wallpaper interval"), I18n.tr("5 minutes", "wallpaper interval"), I18n.tr("15 minutes", "wallpaper interval"), I18n.tr("30 minutes", "wallpaper interval"), I18n.tr("1 hour", "wallpaper interval"), I18n.tr("1 hour 30 minutes", "wallpaper interval"), I18n.tr("2 hours", "wallpaper interval"), I18n.tr("3 hours", "wallpaper interval"), I18n.tr("4 hours", "wallpaper interval"), I18n.tr("6 hours", "wallpaper interval"), I18n.tr("8 hours", "wallpaper interval"), I18n.tr("12 hours", "wallpaper interval")]
property var intervalValues: [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 300, 900, 1800, 3600, 5400, 7200, 10800, 14400, 21600, 28800, 43200]
tab: "wallpaper"
tags: ["interval", "cycling", "time", "frequency"]
settingKey: "wallpaperCyclingInterval"
width: parent.width - Theme.spacingM * 2
visible: {
if (SessionData.perMonitorWallpaper) {
return SessionData.getMonitorCyclingSettings(selectedMonitorName).mode === "interval";
}
return SessionData.wallpaperCyclingMode === "interval";
}
text: I18n.tr("Interval")
description: I18n.tr("How often to change wallpaper")
options: intervalOptions
currentValue: {
var currentSeconds;
if (SessionData.perMonitorWallpaper) {
currentSeconds = SessionData.getMonitorCyclingSettings(selectedMonitorName).interval;
} else {
currentSeconds = SessionData.wallpaperCyclingInterval;
}
const index = intervalValues.indexOf(currentSeconds);
return index >= 0 ? intervalOptions[index] : I18n.tr("5 minutes", "wallpaper interval");
}
onValueChanged: value => {
const index = intervalOptions.indexOf(value);
if (index < 0)
return;
if (SessionData.perMonitorWallpaper) {
SessionData.setMonitorCyclingInterval(selectedMonitorName, intervalValues[index]);
} else {
SessionData.setWallpaperCyclingInterval(intervalValues[index]);
}
}
Connections {
target: root
function onSelectedMonitorNameChanged() {
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] : I18n.tr("5 minutes", "wallpaper interval");
});
}
}
}
Row {
spacing: Theme.spacingM
visible: {
if (SessionData.perMonitorWallpaper) {
return SessionData.getMonitorCyclingSettings(selectedMonitorName).mode === "time";
}
return SessionData.wallpaperCyclingMode === "time";
}
width: parent.width - Theme.spacingM * 2
StyledText {
text: I18n.tr("Daily at:")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
DankTextField {
id: timeTextField
width: 100
height: 40
text: {
if (SessionData.perMonitorWallpaper) {
return SessionData.getMonitorCyclingSettings(selectedMonitorName).time;
}
return SessionData.wallpaperCyclingTime;
}
placeholderText: "00:00"
maximumLength: 5
topPadding: Theme.spacingS
bottomPadding: Theme.spacingS
onAccepted: {
var isValid = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/.test(text);
if (isValid) {
if (SessionData.perMonitorWallpaper) {
SessionData.setMonitorCyclingTime(selectedMonitorName, text);
} else {
SessionData.setWallpaperCyclingTime(text);
}
} else {
if (SessionData.perMonitorWallpaper) {
text = SessionData.getMonitorCyclingSettings(selectedMonitorName).time;
} else {
text = SessionData.wallpaperCyclingTime;
}
}
}
onEditingFinished: {
var isValid = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/.test(text);
if (isValid) {
if (SessionData.perMonitorWallpaper) {
SessionData.setMonitorCyclingTime(selectedMonitorName, text);
} else {
SessionData.setWallpaperCyclingTime(text);
}
} else {
if (SessionData.perMonitorWallpaper) {
text = SessionData.getMonitorCyclingSettings(selectedMonitorName).time;
} else {
text = SessionData.wallpaperCyclingTime;
}
}
}
anchors.verticalCenter: parent.verticalCenter
validator: RegularExpressionValidator {
regularExpression: /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/
}
Connections {
target: root
function onSelectedMonitorNameChanged() {
Qt.callLater(() => {
if (SessionData.perMonitorWallpaper) {
timeTextField.text = SessionData.getMonitorCyclingSettings(selectedMonitorName).time;
} else {
timeTextField.text = SessionData.wallpaperCyclingTime;
}
});
}
}
}
StyledText {
text: I18n.tr("24-hour format")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
}
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.2
}
SettingsDropdownRow {
tab: "wallpaper"
tags: ["transition", "effect", "animation", "change"]
settingKey: "wallpaperTransition"
text: I18n.tr("Transition Effect")
description: I18n.tr("Visual effect used when wallpaper changes")
function getTransitionLabel(t) {
switch (t) {
case "random":
return I18n.tr("Random", "wallpaper transition option");
case "none":
return I18n.tr("None", "wallpaper transition option");
case "fade":
return I18n.tr("Fade", "wallpaper transition option");
case "wipe":
return I18n.tr("Wipe", "wallpaper transition option");
case "disc":
return I18n.tr("Disc", "wallpaper transition option");
case "stripes":
return I18n.tr("Stripes", "wallpaper transition option");
case "iris bloom":
return I18n.tr("Iris Bloom", "wallpaper transition option");
case "pixelate":
return I18n.tr("Pixelate", "wallpaper transition option");
case "portal":
return I18n.tr("Portal", "wallpaper transition option");
default:
return t.charAt(0).toUpperCase() + t.slice(1);
}
}
currentValue: getTransitionLabel(SessionData.wallpaperTransition)
options: [I18n.tr("Random", "wallpaper transition option")].concat(SessionData.availableWallpaperTransitions.map(t => getTransitionLabel(t)))
onValueChanged: value => {
const transitionMap = {};
transitionMap[I18n.tr("Random", "wallpaper transition option")] = "random";
SessionData.availableWallpaperTransitions.forEach(t => {
transitionMap[getTransitionLabel(t)] = t;
});
SessionData.setWallpaperTransition(transitionMap[value] || value.toLowerCase());
}
}
Column {
width: parent.width
spacing: Theme.spacingS
visible: SessionData.wallpaperTransition === "random"
leftPadding: Theme.spacingM
rightPadding: Theme.spacingM
StyledText {
text: I18n.tr("Include Transitions")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
}
StyledText {
text: I18n.tr("Select which transitions to include in randomization")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
width: parent.width - Theme.spacingM * 2
}
DankButtonGroup {
id: transitionGroup
width: parent.width - Theme.spacingM * 2
selectionMode: "multi"
model: SessionData.availableWallpaperTransitions.filter(t => t !== "none")
initialSelection: SessionData.includedTransitions
currentSelection: SessionData.includedTransitions
onSelectionChanged: (index, selected) => {
const transition = model[index];
let newIncluded = SessionData.includedTransitions.slice();
if (selected && !newIncluded.includes(transition)) {
newIncluded.push(transition);
} else if (!selected && newIncluded.includes(transition)) {
newIncluded = newIncluded.filter(t => t !== transition);
}
SessionData.includedTransitions = newIncluded;
}
}
}
}
SettingsCard {
tab: "wallpaper"
tags: ["external", "disable", "swww", "hyprpaper", "swaybg"]
title: I18n.tr("External Wallpaper Management", "wallpaper settings external management")
settingKey: "disableWallpaper"
iconName: "wallpaper"
SettingsToggleRow {
tab: "wallpaper"
tags: ["disable", "external", "management"]
settingKey: "disableWallpapers"
text: I18n.tr("Disable Built-in Wallpapers", "wallpaper settings disable toggle")
description: I18n.tr("Use an external wallpaper manager like swww, hyprpaper, or swaybg.", "wallpaper settings disable description")
checked: {
var prefs = SettingsData.screenPreferences?.wallpaper;
if (!prefs)
return false;
if (Array.isArray(prefs) && prefs.length === 0)
return true;
return false;
}
onToggled: checked => {
var prefs = SettingsData.screenPreferences || {};
var newPrefs = Object.assign({}, prefs);
newPrefs.wallpaper = checked ? [] : ["all"];
SettingsData.set("screenPreferences", newPrefs);
}
}
}
SettingsCard {
tab: "wallpaper"
tags: ["blur", "layer", "niri", "compositor"]
title: I18n.tr("Blur Wallpaper Layer")
settingKey: "blurWallpaper"
visible: CompositorService.isNiri
SettingsToggleRow {
tab: "wallpaper"
tags: ["blur", "duplicate", "layer", "compositor"]
settingKey: "blurredWallpaperLayer"
text: I18n.tr("Duplicate Wallpaper with Blur")
description: I18n.tr("Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.")
checked: SettingsData.blurredWallpaperLayer
onToggled: checked => SettingsData.set("blurredWallpaperLayer", checked)
}
}
}
}
function openMainWallpaperBrowser() {
mainWallpaperBrowserLoader.active = true;
if (mainWallpaperBrowserLoader.item)
mainWallpaperBrowserLoader.item.open();
}
function openLightWallpaperBrowser() {
lightWallpaperBrowserLoader.active = true;
if (lightWallpaperBrowserLoader.item)
lightWallpaperBrowserLoader.item.open();
}
function openDarkWallpaperBrowser() {
darkWallpaperBrowserLoader.active = true;
if (darkWallpaperBrowserLoader.item)
darkWallpaperBrowserLoader.item.open();
}
LazyLoader {
id: mainWallpaperBrowserLoader
active: false
FileBrowserModal {
parentModal: root.parentModal
browserTitle: I18n.tr("Select Wallpaper", "wallpaper file browser title")
browserIcon: "wallpaper"
browserType: "wallpaper"
showHiddenFiles: true
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp", "*.jxl", "*.avif", "*.heif", "*.exr"]
onFileSelected: path => {
if (SessionData.perMonitorWallpaper) {
SessionData.setMonitorWallpaper(selectedMonitorName, path);
} else {
SessionData.setWallpaper(path);
}
close();
}
}
}
LazyLoader {
id: lightWallpaperBrowserLoader
active: false
FileBrowserModal {
parentModal: root.parentModal
browserTitle: I18n.tr("Select Wallpaper", "light mode wallpaper file browser title")
browserIcon: "light_mode"
browserType: "wallpaper"
showHiddenFiles: true
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp", "*.jxl", "*.avif", "*.heif", "*.exr"]
onFileSelected: path => {
SessionData.wallpaperPathLight = path;
SessionData.syncWallpaperForCurrentMode();
SessionData.saveSettings();
close();
}
}
}
LazyLoader {
id: darkWallpaperBrowserLoader
active: false
FileBrowserModal {
parentModal: root.parentModal
browserTitle: I18n.tr("Select Wallpaper", "dark mode wallpaper file browser title")
browserIcon: "dark_mode"
browserType: "wallpaper"
showHiddenFiles: true
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp", "*.jxl", "*.avif", "*.heif", "*.exr"]
onFileSelected: path => {
SessionData.wallpaperPathDark = path;
SessionData.syncWallpaperForCurrentMode();
SessionData.saveSettings();
close();
}
}
}
}