mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-18 09:14:01 -04:00
1130 lines
47 KiB
QML
1130 lines
47 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import qs.Common
|
|
import qs.Modals.Common
|
|
import qs.Modals.FileBrowser
|
|
import qs.Services
|
|
import qs.DCommon.Widgets
|
|
import qs.Modules.Settings.Widgets
|
|
import "../../Common/Format.js" as Format
|
|
import "../../Common/ThemePalette.js" as ThemePalette
|
|
import "../../DCommon/Common/MaterialWallpaper.js" as Art
|
|
|
|
Column {
|
|
id: root
|
|
|
|
property var parentModal: null
|
|
property var cachedIconThemes: SettingsData.availableIconThemes
|
|
property var cachedCursorThemes: SettingsData.availableCursorThemes
|
|
|
|
readonly property bool perMonitor: SessionData.perMonitorWallpaper
|
|
readonly property bool perMode: SessionData.perModeWallpaper
|
|
readonly property string selectedScreen: SettingsUiState.selectedWallpaperScreen || firstScreenName()
|
|
readonly property string currentWallpaper: {
|
|
SessionData.monitorWallpapers;
|
|
return perMonitor ? SessionData.getMonitorWallpaper(selectedScreen) : SessionData.wallpaperPath;
|
|
}
|
|
readonly property var materialTarget: SessionData.materialWallpaperTarget(selectedScreen)
|
|
readonly property var materialEntry: SessionData.materialWallpaperEntry(materialTarget)
|
|
readonly property string materialPreset: Art.resolvePreset(materialEntry.preset, SessionData.materialWallpaperProfiles)
|
|
readonly property bool materialWallpaper: currentWallpaper === ""
|
|
readonly property string pendingWallpaperInstall: PopoutService.pendingWallpaperInstall
|
|
readonly property bool dynamicTheme: Theme.currentTheme === Theme.dynamic
|
|
readonly property var selectedDisplay: Quickshell.screens.find(screen => screen.name === selectedScreen) ?? Quickshell.screens[0]
|
|
readonly property real displayAspectRatio: selectedDisplay ? selectedDisplay.width / selectedDisplay.height : Art.designWidth / Art.designHeight
|
|
readonly property bool hasWallpaper: currentWallpaper !== ""
|
|
readonly property bool wallpaperIsImage: hasWallpaper && !currentWallpaper.startsWith("#")
|
|
readonly property bool cyclingEnabled: {
|
|
SessionData.monitorCyclingSettings;
|
|
return perMonitor ? SessionData.getMonitorCyclingSettings(selectedScreen).enabled : SessionData.wallpaperCyclingEnabled;
|
|
}
|
|
readonly property var themePalette: ThemePalette.pick({
|
|
"primary": Theme.primary,
|
|
"secondary": Theme.secondary,
|
|
"tertiary": Theme.tertiary,
|
|
"primaryContainer": Theme.primaryContainer,
|
|
"info": Theme.info,
|
|
"error": Theme.error,
|
|
"warning": Theme.warning
|
|
})
|
|
readonly property string colorModeStatus: {
|
|
if (SettingsData.matugenSmartMode)
|
|
return I18n.tr("Wallpaper");
|
|
if (!SessionData.themeModeAutoEnabled)
|
|
return I18n.tr("Manual");
|
|
const label = SessionData.themeModeAutoMode === "location" ? I18n.tr("Location") : I18n.tr("Schedule");
|
|
if (!SessionData.themeModeNextTransition)
|
|
return label;
|
|
return label + " · " + Format.formatIsoTime(SessionData.themeModeNextTransition);
|
|
}
|
|
|
|
width: parent?.width ?? 0
|
|
spacing: Theme.spacingL
|
|
|
|
Component.onCompleted: {
|
|
WallpaperCyclingService.cyclingActive;
|
|
SettingsData.detectAvailableIconThemes();
|
|
SettingsData.detectAvailableCursorThemes();
|
|
}
|
|
|
|
function firstScreenName() {
|
|
const screens = Quickshell.screens;
|
|
return screens.length > 0 ? screens[0].name : "";
|
|
}
|
|
|
|
function screenNames() {
|
|
const names = [];
|
|
const screens = Quickshell.screens;
|
|
for (let i = 0; i < screens.length; i++)
|
|
names.push(SettingsData.getScreenDisplayName(screens[i]));
|
|
return names;
|
|
}
|
|
|
|
function screenForDisplayName(value) {
|
|
const screens = Quickshell.screens;
|
|
for (let i = 0; i < screens.length; i++) {
|
|
if (SettingsData.getScreenDisplayName(screens[i]) === value)
|
|
return screens[i].name;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function displayNameForScreen(name) {
|
|
const screens = Quickshell.screens;
|
|
for (let i = 0; i < screens.length; i++) {
|
|
if (screens[i].name === name)
|
|
return SettingsData.getScreenDisplayName(screens[i]);
|
|
}
|
|
return I18n.tr("No displays");
|
|
}
|
|
|
|
function openBrowser() {
|
|
wallpaperBrowserLoader.active = true;
|
|
if (wallpaperBrowserLoader.item)
|
|
wallpaperBrowserLoader.item.open();
|
|
}
|
|
|
|
function applyWallpaper(path) {
|
|
if (perMonitor) {
|
|
SessionData.setMonitorWallpaper(selectedScreen, path);
|
|
SessionData.setMonitorCyclingFolderPath(selectedScreen, "");
|
|
return;
|
|
}
|
|
SessionData.setWallpaper(path);
|
|
SessionData.wallpaperCyclingFolderPath = "";
|
|
SessionData.saveSettings();
|
|
}
|
|
|
|
function pickColor() {
|
|
const picker = PopoutService.colorPickerModal;
|
|
if (!picker)
|
|
return;
|
|
picker.selectedColor = currentWallpaper.startsWith("#") ? currentWallpaper : Theme.primary;
|
|
picker.pickerTitle = I18n.tr("Choose Wallpaper Color", "wallpaper color picker title");
|
|
picker.onColorSelectedCallback = function (color) {
|
|
root.applyWallpaper(color.toString());
|
|
};
|
|
picker.show();
|
|
}
|
|
|
|
function clearWallpaper() {
|
|
SessionData.setMaterialWallpaper(materialTarget, materialEntry);
|
|
}
|
|
|
|
onPendingWallpaperInstallChanged: Qt.callLater(offerPendingInstall)
|
|
|
|
function offerPendingInstall() {
|
|
const payload = PopoutService.pendingWallpaperInstall;
|
|
if (!payload)
|
|
return;
|
|
PopoutService.pendingWallpaperInstall = "";
|
|
const layout = Art.parseInstallPayload(payload);
|
|
if (!layout) {
|
|
ToastService.showError(I18n.tr("Invalid wallpaper layout", "material wallpaper import error"));
|
|
return;
|
|
}
|
|
const name = layout.name || Art.profileId("", SessionData.materialWallpaperProfiles);
|
|
profileConfirm.showWithOptions({
|
|
"title": I18n.tr("Install wallpaper", "material wallpaper install dialog title"),
|
|
"message": I18n.tr("Install wallpaper '%1'?", "material wallpaper install confirmation").arg(name),
|
|
"confirmText": I18n.tr("Install", "install action button"),
|
|
"cancelText": I18n.tr("Cancel"),
|
|
"onConfirm": () => root.installLayout(layout, "")
|
|
});
|
|
}
|
|
|
|
function installLayout(layout, fallbackName) {
|
|
const id = SessionData.addMaterialWallpaperProfile(layout, fallbackName);
|
|
if (!id) {
|
|
ToastService.showError(I18n.tr("Invalid wallpaper layout", "material wallpaper import error"));
|
|
return;
|
|
}
|
|
SessionData.setMaterialWallpaperPreset(materialTarget, id);
|
|
}
|
|
|
|
function importLayoutFile(path) {
|
|
layoutFile.path = "";
|
|
layoutFile.path = path;
|
|
}
|
|
|
|
function openLayoutBrowser() {
|
|
layoutBrowserLoader.active = true;
|
|
if (layoutBrowserLoader.item)
|
|
layoutBrowserLoader.item.open();
|
|
}
|
|
|
|
function confirmRemoveProfile(id, label) {
|
|
profileConfirm.showWithOptions({
|
|
"title": I18n.tr("Delete"),
|
|
"message": I18n.tr("Delete wallpaper '%1'?", "material wallpaper delete confirmation").arg(label),
|
|
"confirmText": I18n.tr("Delete"),
|
|
"cancelText": I18n.tr("Cancel"),
|
|
"confirmColor": Theme.error,
|
|
"onConfirm": () => SessionData.removeMaterialWallpaperProfile(id)
|
|
});
|
|
}
|
|
|
|
function selectSeed(seed) {
|
|
if (!SessionData.setMaterialWallpaperSeed(materialTarget, seed))
|
|
return;
|
|
SettingsData.setMatugenSeedColor("");
|
|
}
|
|
|
|
function pickSeed() {
|
|
const picker = PopoutService.colorPickerModal;
|
|
if (!picker)
|
|
return;
|
|
picker.selectedColor = materialEntry.seed;
|
|
picker.pickerTitle = I18n.tr("Seed color", "color picker title, the color the dynamic palette is built from");
|
|
picker.onColorSelectedCallback = function (color) {
|
|
root.selectSeed(Theme.withAlpha(color, 1).toString());
|
|
};
|
|
picker.show();
|
|
}
|
|
|
|
ConfigInclude {
|
|
id: cursorInclude
|
|
includeKind: "cursor"
|
|
onFixed: SettingsData.updateCompositorCursor()
|
|
}
|
|
|
|
function setCursorHideTimeout(value) {
|
|
const updated = JSON.parse(JSON.stringify(SettingsData.cursorSettings));
|
|
if (CompositorService.isNiri) {
|
|
updated.niri = updated.niri || {};
|
|
updated.niri.hideAfterInactiveMs = value;
|
|
} else if (CompositorService.isHyprland) {
|
|
updated.hyprland = updated.hyprland || {};
|
|
updated.hyprland.inactiveTimeout = value;
|
|
} else {
|
|
updated.mango = updated.mango || {};
|
|
updated.mango.cursorHideTimeout = value;
|
|
}
|
|
SettingsData.set("cursorSettings", updated);
|
|
}
|
|
|
|
function warnIfMissingQtTheme() {
|
|
if (Quickshell.env("QT_QPA_PLATFORMTHEME") === "gtk3" || Quickshell.env("QT_QPA_PLATFORMTHEME") === "qt6ct" || Quickshell.env("QT_QPA_PLATFORMTHEME_QT6") === "qt6ct" || SettingsData.qtengineActive || Quickshell.env("QT_QPA_PLATFORMTHEME") === "kde")
|
|
return;
|
|
ToastService.showError(I18n.tr("Missing Environment Variables", "qt theme env error title"), I18n.tr("You need to set one of:\nQT_QPA_PLATFORMTHEME=gtk3 OR\nQT_QPA_PLATFORMTHEME=qt6ct OR\nQT_QPA_PLATFORMTHEME=qtengine\nas environment variables, and then restart the shell.\n\nOnly qt6ct requires qt6ct-kde to be installed.", "qt theme env error body"));
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "wallpaper"
|
|
tags: ["background", "image", "picture", "light", "dark", "mode", "theme", "color", "palette"]
|
|
title: I18n.tr("Appearance")
|
|
settingKey: "wallpaper"
|
|
SettingsDropdownRow {
|
|
tab: "wallpaper"
|
|
tags: ["monitor", "display", "screen"]
|
|
settingKey: "selectedMonitor"
|
|
visible: root.perMonitor
|
|
text: I18n.tr("Display")
|
|
currentValue: root.displayNameForScreen(root.selectedScreen)
|
|
options: root.screenNames()
|
|
onValueChanged: value => SettingsUiState.selectedWallpaperScreen = root.screenForDisplayName(value)
|
|
}
|
|
|
|
SettingsRow {
|
|
settingKey: "colorMode"
|
|
tags: ["light", "dark", "mode", "theme", "color", "palette"]
|
|
paddingH: SettingsMetrics.heroPadding
|
|
paddingV: SettingsMetrics.heroPadding
|
|
body: Flow {
|
|
id: hero
|
|
|
|
readonly property bool stacked: width < SettingsMetrics.wallpaperHeroStackWidth
|
|
readonly property real thumbWidth: stacked ? width : Math.round(Math.min(width, SettingsMetrics.mediaMaxWidth) * SettingsMetrics.wallpaperHeroSplit)
|
|
|
|
width: parent.width
|
|
spacing: Theme.spacingL
|
|
|
|
SettingsWallpaperThumb {
|
|
id: thumb
|
|
width: hero.thumbWidth
|
|
path: root.currentWallpaper
|
|
height: width / root.displayAspectRatio
|
|
showMaterial: true
|
|
materialComposition: SessionData.getMonitorMaterialWallpaper(root.selectedScreen)
|
|
onBrowse: root.openBrowser()
|
|
onPickColor: root.pickColor()
|
|
onClear: root.clearWallpaper()
|
|
}
|
|
|
|
Column {
|
|
id: side
|
|
|
|
readonly property real navRowHeight: (thumb.height - modeGroup.height - spacing - navGroup.spacing) / 2
|
|
|
|
width: hero.stacked ? hero.width : hero.width - hero.thumbWidth - hero.spacing
|
|
spacing: Theme.spacingM
|
|
|
|
DButtonGroup {
|
|
id: modeGroup
|
|
arrowKeysSelect: false
|
|
width: parent.width
|
|
fillWidth: true
|
|
checkEnabled: false
|
|
model: [
|
|
{
|
|
"text": I18n.tr("Light", "adjective, wallpaper thumbnail label for light mode"),
|
|
"icon": "light_mode"
|
|
},
|
|
{
|
|
"text": I18n.tr("Dark", "adjective, wallpaper thumbnail label for dark mode"),
|
|
"icon": "dark_mode"
|
|
}
|
|
]
|
|
currentIndex: SessionData.isLightMode ? 0 : 1
|
|
selectionMode: "single"
|
|
onSelectionChanged: (index, selected) => {
|
|
if (!selected)
|
|
return;
|
|
const light = index === 0;
|
|
if (light === SessionData.isLightMode)
|
|
return;
|
|
Theme.screenTransition();
|
|
Theme.setLightMode(light);
|
|
}
|
|
}
|
|
|
|
SettingsGroup {
|
|
id: navGroup
|
|
width: parent.width
|
|
slotColor: SettingsMetrics.controlColor
|
|
|
|
SettingsNavRow {
|
|
tab: "wallpaper"
|
|
tags: ["light", "dark", "mode", "schedule", "location", "automatic"]
|
|
settingKey: "themeScheduleNav"
|
|
title: I18n.tr("Dark mode")
|
|
hint: root.colorModeStatus
|
|
iconName: "schedule"
|
|
iconBox: true
|
|
height: hero.stacked ? implicitHeight : Math.max(implicitHeight, side.navRowHeight)
|
|
paddingH: Theme.spacingM
|
|
paddingV: Theme.spacingM
|
|
onClicked: keyboard => root.parentModal?.navigateTo("theme_schedule", keyboard)
|
|
}
|
|
|
|
SettingsNavRow {
|
|
tab: "wallpaper"
|
|
tags: ["theme", "color", "palette", "matugen", "dynamic", "custom", "registry"]
|
|
settingKey: "themeNav"
|
|
title: I18n.tr("Theme & colors")
|
|
hint: Theme.currentThemeLabel
|
|
height: hero.stacked ? implicitHeight : Math.max(implicitHeight, side.navRowHeight)
|
|
paddingH: Theme.spacingM
|
|
paddingV: Theme.spacingM
|
|
onClicked: keyboard => root.parentModal?.navigateTo("theme", keyboard)
|
|
|
|
leading: DPaletteSwatch {
|
|
width: SettingsMetrics.heroLeadingSize
|
|
height: width
|
|
primaryColor: root.themePalette.primary
|
|
secondaryColor: root.themePalette.secondary
|
|
tertiaryColor: root.themePalette.tertiary
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "wallpaper"
|
|
tags: ["background", "image", "picture", "fill", "cycling", "transition", "monitor", "display"]
|
|
title: I18n.tr("Wallpaper")
|
|
settingKey: "wallpaperOptions"
|
|
|
|
SettingsRow {
|
|
tab: "wallpaper"
|
|
settingKey: "materialPresets"
|
|
tags: ["material", "shape", "preset", "dank", "bloom", "orbit", "garden", "petal", "dune"]
|
|
visible: root.materialWallpaper
|
|
title: I18n.tr("Material", "wallpaper type")
|
|
subtitle: I18n.tr("Shown while no image or color is set", "Material wallpaper presets description")
|
|
modified: root.materialPreset !== Art.defaultPreset
|
|
onResetRequested: SessionData.setMaterialWallpaperPreset(root.materialTarget, Art.defaultPreset)
|
|
body: MaterialWallpaperPresets {
|
|
preset: root.materialPreset
|
|
seed: root.materialEntry.seed
|
|
aspectRatio: root.displayAspectRatio
|
|
onSelected: preset => SessionData.setMaterialWallpaperPreset(root.materialTarget, preset)
|
|
onRemoveRequested: (preset, label) => root.confirmRemoveProfile(preset, label)
|
|
onImportRequested: root.openLayoutBrowser()
|
|
}
|
|
|
|
DButton {
|
|
visible: Art.hiddenBuiltins(SessionData.materialWallpaperProfiles).length > 0
|
|
text: I18n.tr("Restore")
|
|
iconName: "restore"
|
|
onClicked: SessionData.restoreMaterialWallpaperProfiles()
|
|
}
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
id: fillModeRow
|
|
|
|
readonly property var fillModes: ["Stretch", "Fit", "Fill", "Scrolling", "Tile", "TileVertically", "TileHorizontally", "Pad"]
|
|
readonly property var fillModeLabels: [I18n.tr("Stretch", "wallpaper fill mode"), I18n.tr("Fit", "wallpaper fill mode"), I18n.tr("Fill", "wallpaper fill mode"), I18n.tr("Scroll", "wallpaper fill mode"), I18n.tr("Tile", "wallpaper fill mode"), I18n.tr("Tile Vertically", "wallpaper fill mode"), I18n.tr("Tile Horizontally", "wallpaper fill mode"), I18n.tr("Pad", "wallpaper fill mode")]
|
|
|
|
tab: "wallpaper"
|
|
tags: ["background", "fill", "fit", "stretch", "tile", "scale"]
|
|
settingKey: "wallpaperFillMode"
|
|
resetKeys: root.perMonitor ? [] : ["wallpaperFillMode"]
|
|
text: I18n.tr("Fill mode")
|
|
visible: root.wallpaperIsImage
|
|
dropdownWidth: 190
|
|
options: fillModeLabels
|
|
optionIcons: ["aspect_ratio", "fit_screen", "zoom_out_map", "swipe", "grid_view", "view_agenda", "view_column", "padding"]
|
|
currentValue: {
|
|
SessionData.monitorWallpaperFillModes;
|
|
const mode = root.perMonitor ? SessionData.getMonitorWallpaperFillMode(root.selectedScreen) : SettingsData.wallpaperFillMode;
|
|
const idx = fillModes.indexOf(mode);
|
|
return idx >= 0 ? fillModeLabels[idx] : "";
|
|
}
|
|
onValueChanged: value => {
|
|
const idx = fillModeLabels.indexOf(value);
|
|
if (idx < 0)
|
|
return;
|
|
if (root.perMonitor) {
|
|
SessionData.setMonitorWallpaperFillMode(root.selectedScreen, fillModes[idx]);
|
|
return;
|
|
}
|
|
SettingsData.set("wallpaperFillMode", fillModes[idx]);
|
|
}
|
|
}
|
|
|
|
ColorDropdownRow {
|
|
tab: "wallpaper"
|
|
tags: ["background", "color", "fill", "fit", "custom"]
|
|
settingKey: "wallpaperBackgroundColorMode"
|
|
resetKeys: ["wallpaperBackgroundColorMode", "wallpaperBackgroundCustomColor"]
|
|
text: I18n.tr("Background color")
|
|
visible: root.wallpaperIsImage
|
|
dropdownWidth: 220
|
|
options: [
|
|
{
|
|
"value": "black",
|
|
"previewColor": SettingsData.wallpaperBackgroundColorFor("black"),
|
|
"label": I18n.tr("Black", "wallpaper background color option")
|
|
},
|
|
{
|
|
"value": "white",
|
|
"previewColor": SettingsData.wallpaperBackgroundColorFor("white"),
|
|
"label": I18n.tr("White", "wallpaper background color option")
|
|
},
|
|
{
|
|
"value": "primary",
|
|
"previewColor": SettingsData.wallpaperBackgroundColorFor("primary"),
|
|
"label": I18n.tr("Primary")
|
|
},
|
|
{
|
|
"value": "surface",
|
|
"previewColor": SettingsData.wallpaperBackgroundColorFor("surface"),
|
|
"label": I18n.tr("Surface Container")
|
|
},
|
|
{
|
|
"value": "custom",
|
|
"label": I18n.tr("Custom")
|
|
}
|
|
]
|
|
currentMode: SettingsData.wallpaperBackgroundColorMode
|
|
customColor: SettingsData.wallpaperBackgroundCustomColor || "#000000"
|
|
pickerTitle: I18n.tr("Background color")
|
|
onModeSelected: mode => SettingsData.set("wallpaperBackgroundColorMode", mode)
|
|
onCustomColorSelected: selectedColor => SettingsData.set("wallpaperBackgroundCustomColor", selectedColor.toString())
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "wallpaper"
|
|
tags: ["per-mode", "light", "dark", "theme"]
|
|
settingKey: "perModeWallpaper"
|
|
text: I18n.tr("Separate light and dark")
|
|
checked: SessionData.perModeWallpaper
|
|
onToggled: toggled => SessionData.setPerModeWallpaper(toggled)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "wallpaper"
|
|
tags: ["per-monitor", "multi-monitor", "display", "monitor"]
|
|
settingKey: "perMonitorWallpaper"
|
|
text: I18n.tr("Separate per display")
|
|
checked: SessionData.perMonitorWallpaper
|
|
onToggled: toggled => SessionData.setPerMonitorWallpaper(toggled)
|
|
}
|
|
|
|
SettingsRow {
|
|
visible: root.perMonitor
|
|
title: I18n.tr("Use default wallpaper", "inherit the global wallpaper on this display")
|
|
DButton {
|
|
text: I18n.tr("Reset")
|
|
onClicked: SessionData.setMonitorWallpaper(root.selectedScreen, "")
|
|
}
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
tab: "wallpaper"
|
|
tags: ["matugen", "target", "monitor", "theming", "dynamic", "colors"]
|
|
settingKey: "matugenTargetMonitor"
|
|
visible: root.perMonitor
|
|
text: I18n.tr("Matugen source display")
|
|
description: I18n.tr("Theme colors come from this display's wallpaper", "matugen source display dropdown description")
|
|
currentValue: {
|
|
if (!SettingsData.matugenTargetMonitor)
|
|
return root.displayNameForScreen(root.firstScreenName()) + " (" + I18n.tr("Default") + ")";
|
|
return root.displayNameForScreen(SettingsData.matugenTargetMonitor);
|
|
}
|
|
options: root.screenNames()
|
|
onValueChanged: value => {
|
|
const screen = root.screenForDisplayName(value);
|
|
if (screen)
|
|
SettingsData.setMatugenTargetMonitor(screen);
|
|
}
|
|
}
|
|
|
|
SettingsSplitRow {
|
|
tab: "wallpaper"
|
|
tags: ["cycling", "automatic", "rotate", "slideshow", "folder", "interval"]
|
|
settingKey: "wallpaperCyclingEnabled"
|
|
visible: (SessionData.wallpaperPath !== "" || root.perMonitor) && !root.perMode
|
|
title: I18n.tr("Automatic cycling")
|
|
subtitle: SettingsTabs.page("wallpaper_cycling")?.hint ?? ""
|
|
checked: root.cyclingEnabled
|
|
onNavigated: keyboard => root.parentModal?.navigateTo("wallpaper_cycling", keyboard)
|
|
onToggled: toggled => {
|
|
if (root.perMonitor) {
|
|
SessionData.setMonitorCyclingEnabled(root.selectedScreen, toggled);
|
|
return;
|
|
}
|
|
SessionData.setWallpaperCyclingEnabled(toggled);
|
|
}
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
tab: "wallpaper"
|
|
tags: ["transition", "effect", "animation", "change"]
|
|
settingKey: "wallpaperTransition"
|
|
resetStore: SessionData
|
|
resetKeys: ["wallpaperTransition"]
|
|
text: I18n.tr("Transition", "noun, wallpaper change animation setting label")
|
|
|
|
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());
|
|
}
|
|
}
|
|
|
|
SettingsRow {
|
|
visible: SessionData.wallpaperTransition === "random"
|
|
body: Column {
|
|
width: parent.width
|
|
spacing: Theme.spacingS
|
|
|
|
StyledText {
|
|
text: I18n.tr("Include transitions")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Theme.fontWeightMedium
|
|
color: Theme.surfaceText
|
|
}
|
|
|
|
DFilterChips {
|
|
width: parent.width
|
|
multiSelect: true
|
|
model: SessionData.availableWallpaperTransitions.filter(t => t !== "none").map(t => ({
|
|
"value": t,
|
|
"label": t.replace(/\b\w/g, c => c.toUpperCase())
|
|
}))
|
|
selectedValues: SessionData.includedTransitions
|
|
onSelectionToggled: (index, selected) => {
|
|
const transition = model[index].value;
|
|
let included = SessionData.includedTransitions.slice();
|
|
if (selected && !included.includes(transition))
|
|
included.push(transition);
|
|
else if (!selected)
|
|
included = included.filter(t => t !== transition);
|
|
SessionData.includedTransitions = included;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsNavRow {
|
|
tab: "wallpaper"
|
|
tags: ["lock", "screen", "background"]
|
|
iconName: "lock"
|
|
title: I18n.tr("Lock screen")
|
|
hint: SettingsData.lockScreenWallpaperPath ? SettingsData.lockScreenWallpaperPath.split("/").pop() : I18n.tr("Use desktop wallpaper")
|
|
onClicked: keyboard => {
|
|
SettingsSearchService.navigateToSection("lockScreenWallpaperPath");
|
|
root.parentModal?.navigateTo("lock_screen", keyboard);
|
|
}
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "wallpaper"
|
|
tags: ["blur", "overview", "niri"]
|
|
settingKey: "blurWallpaperOnOverview"
|
|
visible: CompositorService.isNiri
|
|
text: I18n.tr("Blur on overview")
|
|
checked: SettingsData.blurWallpaperOnOverview
|
|
onToggled: checked => SettingsData.set("blurWallpaperOnOverview", checked)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "wallpaper"
|
|
tags: ["material", "seed", "color", "palette", "hue", "matugen", "dynamic", "theme"]
|
|
title: I18n.tr("Theme color", "Material wallpaper seed card")
|
|
settingKey: "materialSeed"
|
|
visible: root.materialWallpaper
|
|
|
|
SettingsNoteRow {
|
|
visible: !Theme.matugenAvailable
|
|
text: I18n.tr("Install matugen package for dynamic theming", "matugen installation hint")
|
|
}
|
|
|
|
SettingsRow {
|
|
visible: Theme.matugenAvailable && !root.dynamicTheme
|
|
title: I18n.tr("Dynamic theme is off", "Material seed card status")
|
|
subtitle: I18n.tr("Shapes use the %1 theme's colors", "Material seed card status, %1 is a theme name").arg(Theme.currentThemeLabel)
|
|
DButton {
|
|
text: I18n.tr("Use Dynamic", "switch the shell theme to the dynamic palette")
|
|
onClicked: Theme.switchTheme(Theme.dynamic)
|
|
}
|
|
}
|
|
|
|
SettingsRow {
|
|
visible: root.dynamicTheme && SettingsData.matugenSeedColor !== ""
|
|
title: I18n.tr("A custom seed color overrides this", "Material seed card status")
|
|
subtitle: I18n.tr("Set under Theme & colors, Derived color", "Material seed card status")
|
|
leading: DColorSwatch {
|
|
width: SettingsMetrics.heroLeadingSize
|
|
height: width
|
|
swatchColor: SettingsData.matugenSeedColor
|
|
}
|
|
DButton {
|
|
text: I18n.tr("Remove override", "clear the custom matugen seed color")
|
|
onClicked: SettingsData.setMatugenSeedColor("")
|
|
}
|
|
}
|
|
|
|
SettingsRow {
|
|
tab: "wallpaper"
|
|
tags: ["seed", "hue", "color", "material", "palette"]
|
|
settingKey: "materialSeedColor"
|
|
visible: root.dynamicTheme
|
|
enabled: Theme.matugenAvailable
|
|
title: I18n.tr("Seed color", "color picker title, the color the dynamic palette is built from")
|
|
subtitle: I18n.tr("The shell palette is built from this color. Shapes take their colors from that palette.", "Material seed picker description")
|
|
modified: root.materialEntry.seed !== Art.defaultSeed
|
|
onResetRequested: root.selectSeed(Art.defaultSeed)
|
|
DActionButton {
|
|
iconName: "colorize"
|
|
iconColor: Theme.primary
|
|
backgroundColor: SettingsMetrics.controlColor
|
|
enabled: Theme.matugenAvailable
|
|
Accessible.name: I18n.tr("Custom color")
|
|
tooltipText: Accessible.name
|
|
onClicked: root.pickSeed()
|
|
}
|
|
body: MaterialSeedPicker {
|
|
id: seedPicker
|
|
seed: root.materialEntry.seed
|
|
enabled: Theme.matugenAvailable
|
|
onSeedSelected: seed => root.selectSeed(seed)
|
|
}
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
tab: "wallpaper"
|
|
tags: ["hue", "seed", "color", "material", "warm", "cool"]
|
|
settingKey: "materialSeedHue"
|
|
visible: root.dynamicTheme
|
|
enabled: Theme.matugenAvailable
|
|
text: I18n.tr("Hue", "Hue angle in the color picker")
|
|
description: I18n.tr("Fine-tune the seed around the color wheel", "Material seed hue slider description")
|
|
minimum: 0
|
|
maximum: 359
|
|
unit: "°"
|
|
value: Math.round(Art.hueOf(root.materialEntry.seed)) % 360
|
|
onSliderDragFinished: finalValue => root.selectSeed(Art.seedForHue(finalValue, root.materialEntry.seed))
|
|
trackGradient: Gradient {
|
|
orientation: Gradient.Horizontal
|
|
GradientStop {
|
|
position: 0
|
|
color: Art.seedForHue(0)
|
|
}
|
|
GradientStop {
|
|
position: 1 / 12
|
|
color: Art.seedForHue(30)
|
|
}
|
|
GradientStop {
|
|
position: 2 / 12
|
|
color: Art.seedForHue(60)
|
|
}
|
|
GradientStop {
|
|
position: 3 / 12
|
|
color: Art.seedForHue(90)
|
|
}
|
|
GradientStop {
|
|
position: 4 / 12
|
|
color: Art.seedForHue(120)
|
|
}
|
|
GradientStop {
|
|
position: 5 / 12
|
|
color: Art.seedForHue(150)
|
|
}
|
|
GradientStop {
|
|
position: 6 / 12
|
|
color: Art.seedForHue(180)
|
|
}
|
|
GradientStop {
|
|
position: 7 / 12
|
|
color: Art.seedForHue(210)
|
|
}
|
|
GradientStop {
|
|
position: 8 / 12
|
|
color: Art.seedForHue(240)
|
|
}
|
|
GradientStop {
|
|
position: 9 / 12
|
|
color: Art.seedForHue(270)
|
|
}
|
|
GradientStop {
|
|
position: 10 / 12
|
|
color: Art.seedForHue(300)
|
|
}
|
|
GradientStop {
|
|
position: 11 / 12
|
|
color: Art.seedForHue(330)
|
|
}
|
|
GradientStop {
|
|
position: 1
|
|
color: Art.seedForHue(360)
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsRow {
|
|
visible: root.dynamicTheme && MatugenPreviewService.seedPreviewFailed
|
|
title: I18n.tr("Color previews unavailable", "Material seed card status")
|
|
subtitle: I18n.tr("Swatches show the seed color itself", "Material seed card status")
|
|
DButton {
|
|
text: I18n.tr("Retry")
|
|
onClicked: seedPicker.retry()
|
|
}
|
|
}
|
|
|
|
SettingsNavRow {
|
|
tab: "wallpaper"
|
|
tags: ["scheme", "palette", "vibrant", "neutral", "expressive", "matugen"]
|
|
settingKey: "paletteStyleNav"
|
|
visible: root.dynamicTheme
|
|
title: I18n.tr("Palette style", "matugen scheme navigation row")
|
|
hint: Theme.getMatugenScheme(SettingsData.matugenScheme).label
|
|
leading: DPaletteSwatch {
|
|
width: SettingsMetrics.heroLeadingSize
|
|
height: width
|
|
primaryColor: root.themePalette.primary
|
|
secondaryColor: root.themePalette.secondary
|
|
tertiaryColor: root.themePalette.tertiary
|
|
}
|
|
onClicked: keyboard => {
|
|
SettingsSearchService.navigateToSection("matugenScheme");
|
|
root.parentModal?.navigateTo("theme", keyboard);
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "wallpaper"
|
|
tags: ["surface", "color", "bar", "popout", "modal", "card", "chip", "background", "elevation"]
|
|
title: I18n.tr("Surfaces")
|
|
settingKey: "surfaceColors"
|
|
|
|
SettingsRow {
|
|
settingKey: "surfaceColorsPreview"
|
|
body: SettingsSurfacePreview {
|
|
width: parent.width
|
|
}
|
|
}
|
|
|
|
Repeater {
|
|
model: [
|
|
{
|
|
"key": "hostSurfaceColor",
|
|
"customKey": "hostSurfaceCustomColor",
|
|
"fallback": "s",
|
|
"text": I18n.tr("Host"),
|
|
"tags": ["host", "bar", "popout", "modal", "window"]
|
|
},
|
|
{
|
|
"key": "cardSurfaceColor",
|
|
"customKey": "cardSurfaceCustomColor",
|
|
"fallback": "sc",
|
|
"text": I18n.tr("Cards"),
|
|
"tags": ["card", "row", "section"]
|
|
},
|
|
{
|
|
"key": "chipSurfaceColor",
|
|
"customKey": "chipSurfaceCustomColor",
|
|
"fallback": "sch",
|
|
"text": I18n.tr("Chip", "surface role"),
|
|
"tags": ["chip", "field", "dropdown", "input"]
|
|
},
|
|
{
|
|
"key": "chipSurfaceNestedColor",
|
|
"customKey": "chipSurfaceNestedCustomColor",
|
|
"fallback": "schh",
|
|
"text": I18n.tr("Nested chip", "surface role"),
|
|
"tags": ["nested", "chip", "control"]
|
|
}
|
|
]
|
|
|
|
SurfaceColorRow {
|
|
required property var modelData
|
|
|
|
tab: "wallpaper"
|
|
tags: ["surface", "color"].concat(modelData.tags)
|
|
settingKey: modelData.key
|
|
resetKeys: [modelData.key, modelData.customKey]
|
|
text: modelData.text
|
|
defaultColor: Theme.roleColor(modelData.fallback)
|
|
currentMode: SettingsData[modelData.key]
|
|
customColor: SettingsData[modelData.customKey]
|
|
pickerTitle: modelData.text
|
|
onModeSelected: mode => SettingsData.set(modelData.key, mode)
|
|
onCustomColorSelected: selectedColor => SettingsData.set(modelData.customKey, selectedColor.toString())
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "theme"
|
|
tags: ["icon", "theme", "system"]
|
|
title: I18n.tr("Icons", "settings card title for icon theme and icon options")
|
|
settingKey: "iconTheme"
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["icon", "theme", "light", "dark", "mode"]
|
|
settingKey: "iconThemePerMode"
|
|
text: I18n.tr("Separate light and dark themes")
|
|
checked: SettingsData.iconThemePerMode
|
|
onToggled: checked => SettingsData.setIconThemePerMode(checked)
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
enabled: !SettingsData.iconThemePerMode
|
|
tab: "theme"
|
|
tags: ["icon", "theme", "system"]
|
|
settingKey: "iconTheme"
|
|
resetKeys: ["iconThemeDark"]
|
|
resetByKeys: false
|
|
onResetRequested: SettingsData.setIconThemeForMode(SettingsData.specDefault("iconThemeDark"), false)
|
|
text: I18n.tr("Theme")
|
|
description: I18n.tr("Requires restart")
|
|
currentValue: SettingsData.iconThemeDark
|
|
enableFuzzySearch: true
|
|
popupWidthOffset: 100
|
|
maxPopupHeight: 236
|
|
options: root.cachedIconThemes
|
|
onValueChanged: value => {
|
|
SettingsData.setIconThemeForMode(value, false);
|
|
root.warnIfMissingQtTheme();
|
|
}
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
enabled: SettingsData.iconThemePerMode
|
|
tab: "theme"
|
|
tags: ["icon", "theme", "system", "dark"]
|
|
settingKey: "iconThemeDark"
|
|
resetByKeys: false
|
|
onResetRequested: SettingsData.setIconThemeForMode(SettingsData.specDefault("iconThemeDark"), false)
|
|
text: I18n.tr("Dark mode theme")
|
|
description: I18n.tr("Requires restart")
|
|
currentValue: SettingsData.iconThemeDark
|
|
enableFuzzySearch: true
|
|
popupWidthOffset: 100
|
|
maxPopupHeight: 236
|
|
options: root.cachedIconThemes
|
|
onValueChanged: value => {
|
|
SettingsData.setIconThemeForMode(value, false);
|
|
root.warnIfMissingQtTheme();
|
|
}
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
enabled: SettingsData.iconThemePerMode
|
|
tab: "theme"
|
|
tags: ["icon", "theme", "system", "light"]
|
|
settingKey: "iconThemeLight"
|
|
resetByKeys: false
|
|
onResetRequested: SettingsData.setIconThemeForMode(SettingsData.specDefault("iconThemeLight"), true)
|
|
text: I18n.tr("Light mode theme")
|
|
description: I18n.tr("Requires restart")
|
|
currentValue: SettingsData.iconThemeLight
|
|
enableFuzzySearch: true
|
|
popupWidthOffset: 100
|
|
maxPopupHeight: 236
|
|
options: root.cachedIconThemes
|
|
onValueChanged: value => {
|
|
SettingsData.setIconThemeForMode(value, true);
|
|
root.warnIfMissingQtTheme();
|
|
}
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
width: parent.width
|
|
active: CompositorService.isAqueous
|
|
sourceComponent: AqueousAppearanceSettings {
|
|
cursor: true
|
|
settingKey: "aqueousCursor"
|
|
title: I18n.tr("Aqueous cursor", "Aqueous compositor cursor synchronization settings")
|
|
visible: CompositorService.isAqueous
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "theme"
|
|
tags: ["cursor", "mouse", "pointer", "theme", "size", "hide", "typing", "touch", "timeout"]
|
|
title: I18n.tr("Cursor", "settings card title for mouse cursor theme")
|
|
settingKey: "cursorTheme"
|
|
visible: CompositorService.supportsCursorConfig
|
|
|
|
IncludeSetupBanner {
|
|
include: cursorInclude
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
tab: "theme"
|
|
tags: ["cursor", "mouse", "pointer", "theme"]
|
|
settingKey: "cursorTheme"
|
|
text: I18n.tr("Theme")
|
|
currentValue: SettingsData.cursorSettings.theme
|
|
enableFuzzySearch: true
|
|
popupWidthOffset: 100
|
|
maxPopupHeight: 236
|
|
options: root.cachedCursorThemes
|
|
modified: SettingsData.cursorSettings.theme !== SettingsData.specDefault("cursorSettings").theme
|
|
onResetRequested: SettingsData.setCursorTheme(SettingsData.specDefault("cursorSettings").theme)
|
|
onValueChanged: value => SettingsData.setCursorTheme(value)
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
tab: "theme"
|
|
tags: ["cursor", "mouse", "pointer", "size"]
|
|
settingKey: "cursorSize"
|
|
text: I18n.tr("Size")
|
|
value: SettingsData.cursorSettings.size
|
|
minimum: 12
|
|
maximum: 128
|
|
unit: "px"
|
|
modified: SettingsData.cursorSettings.size !== SettingsData.specDefault("cursorSettings").size
|
|
onResetRequested: SettingsData.setCursorSize(SettingsData.specDefault("cursorSettings").size)
|
|
onSliderValueChanged: newValue => SettingsData.setCursorSize(newValue)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["cursor", "hide", "typing", "keyboard"]
|
|
settingKey: "cursorHideWhenTyping"
|
|
text: I18n.tr("Hide cursor when typing")
|
|
visible: CompositorService.isNiri || CompositorService.isHyprland
|
|
modified: checked
|
|
checked: CompositorService.isNiri ? (SettingsData.cursorSettings.niri?.hideWhenTyping || false) : (SettingsData.cursorSettings.hyprland?.hideOnKeyPress || false)
|
|
onToggled: checked => {
|
|
const updated = JSON.parse(JSON.stringify(SettingsData.cursorSettings));
|
|
if (CompositorService.isNiri) {
|
|
updated.niri = updated.niri || {};
|
|
updated.niri.hideWhenTyping = checked;
|
|
} else {
|
|
updated.hyprland = updated.hyprland || {};
|
|
updated.hyprland.hideOnKeyPress = checked;
|
|
}
|
|
SettingsData.set("cursorSettings", updated);
|
|
}
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["cursor", "hide", "touch"]
|
|
settingKey: "cursorHideOnTouch"
|
|
text: I18n.tr("Hide cursor on touch")
|
|
visible: CompositorService.isHyprland
|
|
modified: checked
|
|
checked: SettingsData.cursorSettings.hyprland?.hideOnTouch || false
|
|
onToggled: checked => {
|
|
const updated = JSON.parse(JSON.stringify(SettingsData.cursorSettings));
|
|
updated.hyprland = updated.hyprland || {};
|
|
updated.hyprland.hideOnTouch = checked;
|
|
SettingsData.set("cursorSettings", updated);
|
|
}
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
tab: "theme"
|
|
tags: ["cursor", "hide", "timeout", "inactive"]
|
|
settingKey: "cursorHideAfterInactive"
|
|
text: I18n.tr("Cursor auto-hide timeout")
|
|
minimumLabel: I18n.tr("Off")
|
|
visible: CompositorService.supportsCursorConfig
|
|
value: {
|
|
if (CompositorService.isNiri)
|
|
return SettingsData.cursorSettings.niri?.hideAfterInactiveMs || 0;
|
|
if (CompositorService.isHyprland)
|
|
return SettingsData.cursorSettings.hyprland?.inactiveTimeout || 0;
|
|
return SettingsData.cursorSettings.mango?.cursorHideTimeout || 0;
|
|
}
|
|
minimum: 0
|
|
maximum: CompositorService.isNiri ? 5000 : 10
|
|
unit: CompositorService.isNiri ? "ms" : "s"
|
|
modified: value !== 0
|
|
onResetRequested: root.setCursorHideTimeout(0)
|
|
onSliderValueChanged: newValue => root.setCursorHideTimeout(newValue)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "wallpaper"
|
|
tags: ["blur", "layer", "external", "disable", "swww", "hyprpaper", "swaybg"]
|
|
title: I18n.tr("Advanced")
|
|
settingKey: "wallpaperAdvanced"
|
|
collapsible: true
|
|
expanded: false
|
|
|
|
SettingsToggleRow {
|
|
tab: "wallpaper"
|
|
tags: ["disable", "external", "management", "swww", "hyprpaper", "swaybg"]
|
|
settingKey: "disableWallpapers"
|
|
text: I18n.tr("Use external manager")
|
|
description: I18n.tr("DMS stops drawing wallpapers on every display", "use external wallpaper manager toggle description")
|
|
checked: {
|
|
const prefs = SettingsData.screenPreferences?.wallpaper;
|
|
return Array.isArray(prefs) && prefs.length === 0;
|
|
}
|
|
onToggled: checked => {
|
|
const prefs = Object.assign({}, SettingsData.screenPreferences || {});
|
|
prefs.wallpaper = checked ? [] : ["all"];
|
|
SettingsData.set("screenPreferences", prefs);
|
|
}
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "wallpaper"
|
|
tags: ["blur", "duplicate", "layer", "compositor", "niri", "blurwallpaper"]
|
|
settingKey: "blurredWallpaperLayer"
|
|
visible: CompositorService.isNiri
|
|
text: I18n.tr("Blur layer")
|
|
description: I18n.tr("Layer namespace dms:blurwallpaper, needs a niri blur rule")
|
|
checked: SettingsData.blurredWallpaperLayer
|
|
onToggled: checked => SettingsData.set("blurredWallpaperLayer", checked)
|
|
}
|
|
}
|
|
|
|
ConfirmModal {
|
|
id: profileConfirm
|
|
}
|
|
|
|
FileView {
|
|
id: layoutFile
|
|
blockLoading: false
|
|
onLoaded: {
|
|
try {
|
|
root.installLayout(JSON.parse(text()), Paths.strip(path).split("/").pop().replace(/\.json$/i, ""));
|
|
} catch (error) {
|
|
ToastService.showError(I18n.tr("Invalid JSON format: %1", "custom theme file error toast, %1 is the error message").arg(error.message));
|
|
}
|
|
}
|
|
onLoadFailed: error => ToastService.showError(I18n.tr("Invalid wallpaper layout", "material wallpaper import error"))
|
|
}
|
|
|
|
LazyLoader {
|
|
id: layoutBrowserLoader
|
|
active: false
|
|
|
|
FileBrowserModal {
|
|
parentModal: root.parentModal
|
|
browserTitle: I18n.tr("Import")
|
|
bucket: "wallpaper"
|
|
showHiddenFiles: true
|
|
filters: ["*.json"]
|
|
onAccepted: paths => root.importLayoutFile(paths[0])
|
|
}
|
|
}
|
|
|
|
LazyLoader {
|
|
id: wallpaperBrowserLoader
|
|
active: false
|
|
|
|
FileBrowserModal {
|
|
parentModal: root.parentModal
|
|
browserTitle: I18n.tr("Select Wallpaper", "wallpaper file browser title")
|
|
bucket: "wallpaper"
|
|
showHiddenFiles: true
|
|
startPath: root.currentWallpaper
|
|
filters: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp", "*.jxl", "*.avif", "*.heif", "*.exr", "*.svg"]
|
|
onAccepted: paths => root.applyWallpaper(paths[0])
|
|
}
|
|
}
|
|
}
|