mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 13:32:50 -05:00
* fix: Add browserPickerViewMode persistence to settings spec The BrowserPickerModal (used by xdg-open feature) was not persisting view mode selection between sessions. While the modal had code to save the view mode preference, the browserPickerViewMode property was not registered in SettingsSpec.js, preventing it from being saved to disk. Added browserPickerViewMode and browserUsageHistory to SettingsSpec.js to ensure user's view preference (list/grid) is properly persisted. Fixes view mode reverting to grid after restarting DMS/QuickShell. * fix: Add view mode persistence for both browser and file pickers Extended the fix to include both picker modals used by xdg-open: BrowserPickerModal (URLs): - Added browserPickerViewMode and browserUsageHistory to SettingsSpec.js - Already had save logic in BrowserPickerModal.qml AppPickerModal/filePickerModal (files): - Added appPickerViewMode and filePickerUsageHistory to SettingsSpec.js - Added appPickerViewMode and filePickerUsageHistory properties to SettingsData.qml - Added viewMode binding and onViewModeChanged handler to filePickerModal Both modals now properly persist user's view preference (list/grid) and usage history between sessions. Fixes view mode reverting to default grid after restarting DMS/QuickShell for both 'dms open https://...' and 'dms open file.pdf' workflows.
2378 lines
87 KiB
QML
2378 lines
87 KiB
QML
pragma Singleton
|
|
pragma ComponentBehavior: Bound
|
|
|
|
import QtCore
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import qs.Common
|
|
import qs.Common.settings
|
|
import qs.Services
|
|
import "settings/SettingsSpec.js" as Spec
|
|
import "settings/SettingsStore.js" as Store
|
|
|
|
Singleton {
|
|
id: root
|
|
|
|
readonly property int settingsConfigVersion: 5
|
|
|
|
readonly property bool isGreeterMode: Quickshell.env("DMS_RUN_GREETER") === "1" || Quickshell.env("DMS_RUN_GREETER") === "true"
|
|
|
|
enum Position {
|
|
Top,
|
|
Bottom,
|
|
Left,
|
|
Right,
|
|
TopCenter,
|
|
BottomCenter,
|
|
LeftCenter,
|
|
RightCenter
|
|
}
|
|
|
|
enum AnimationSpeed {
|
|
None,
|
|
Short,
|
|
Medium,
|
|
Long,
|
|
Custom
|
|
}
|
|
|
|
enum SuspendBehavior {
|
|
Suspend,
|
|
Hibernate,
|
|
SuspendThenHibernate
|
|
}
|
|
|
|
enum WidgetColorMode {
|
|
Default,
|
|
Colorful
|
|
}
|
|
|
|
readonly property string _homeUrl: StandardPaths.writableLocation(StandardPaths.HomeLocation)
|
|
readonly property string _configUrl: StandardPaths.writableLocation(StandardPaths.ConfigLocation)
|
|
readonly property string _configDir: Paths.strip(_configUrl)
|
|
readonly property string pluginSettingsPath: _configDir + "/DankMaterialShell/plugin_settings.json"
|
|
|
|
property bool _loading: false
|
|
property bool _pluginSettingsLoading: false
|
|
property bool _parseError: false
|
|
property bool _pluginParseError: false
|
|
property bool _hasLoaded: false
|
|
property bool _isReadOnly: false
|
|
property bool _hasUnsavedChanges: false
|
|
property var _loadedSettingsSnapshot: null
|
|
property var pluginSettings: ({})
|
|
property var builtInPluginSettings: ({})
|
|
|
|
function getBuiltInPluginSetting(pluginId, key, defaultValue) {
|
|
if (!builtInPluginSettings[pluginId])
|
|
return defaultValue;
|
|
return builtInPluginSettings[pluginId][key] !== undefined ? builtInPluginSettings[pluginId][key] : defaultValue;
|
|
}
|
|
|
|
function setBuiltInPluginSetting(pluginId, key, value) {
|
|
const updated = JSON.parse(JSON.stringify(builtInPluginSettings));
|
|
if (!updated[pluginId])
|
|
updated[pluginId] = {};
|
|
updated[pluginId][key] = value;
|
|
builtInPluginSettings = updated;
|
|
saveSettings();
|
|
}
|
|
|
|
property var launcherPluginVisibility: ({})
|
|
|
|
function getPluginAllowWithoutTrigger(pluginId) {
|
|
if (!launcherPluginVisibility[pluginId])
|
|
return true;
|
|
return launcherPluginVisibility[pluginId].allowWithoutTrigger !== false;
|
|
}
|
|
|
|
function setPluginAllowWithoutTrigger(pluginId, allow) {
|
|
const updated = JSON.parse(JSON.stringify(launcherPluginVisibility));
|
|
if (!updated[pluginId])
|
|
updated[pluginId] = {};
|
|
updated[pluginId].allowWithoutTrigger = allow;
|
|
launcherPluginVisibility = updated;
|
|
saveSettings();
|
|
}
|
|
|
|
property var launcherPluginOrder: []
|
|
onLauncherPluginOrderChanged: saveSettings()
|
|
|
|
function setLauncherPluginOrder(order) {
|
|
launcherPluginOrder = order;
|
|
}
|
|
|
|
function getOrderedLauncherPlugins(allPlugins) {
|
|
if (!launcherPluginOrder || launcherPluginOrder.length === 0)
|
|
return allPlugins;
|
|
const orderMap = {};
|
|
for (let i = 0; i < launcherPluginOrder.length; i++)
|
|
orderMap[launcherPluginOrder[i]] = i;
|
|
return allPlugins.slice().sort((a, b) => {
|
|
const aOrder = orderMap[a.id] ?? 9999;
|
|
const bOrder = orderMap[b.id] ?? 9999;
|
|
if (aOrder !== bOrder)
|
|
return aOrder - bOrder;
|
|
return a.name.localeCompare(b.name);
|
|
});
|
|
}
|
|
|
|
property alias dankBarLeftWidgetsModel: leftWidgetsModel
|
|
property alias dankBarCenterWidgetsModel: centerWidgetsModel
|
|
property alias dankBarRightWidgetsModel: rightWidgetsModel
|
|
|
|
property string currentThemeName: "purple"
|
|
property string currentThemeCategory: "generic"
|
|
property string customThemeFile: ""
|
|
property var registryThemeVariants: ({})
|
|
property string matugenScheme: "scheme-tonal-spot"
|
|
property bool runUserMatugenTemplates: true
|
|
property string matugenTargetMonitor: ""
|
|
property real popupTransparency: 1.0
|
|
property real dockTransparency: 1
|
|
property string widgetBackgroundColor: "sch"
|
|
property string widgetColorMode: "default"
|
|
property real cornerRadius: 12
|
|
property int niriLayoutGapsOverride: -1
|
|
property int niriLayoutRadiusOverride: -1
|
|
property int niriLayoutBorderSize: -1
|
|
property int hyprlandLayoutGapsOverride: -1
|
|
property int hyprlandLayoutRadiusOverride: -1
|
|
property int hyprlandLayoutBorderSize: -1
|
|
property int mangoLayoutGapsOverride: -1
|
|
property int mangoLayoutRadiusOverride: -1
|
|
property int mangoLayoutBorderSize: -1
|
|
|
|
property bool use24HourClock: true
|
|
property bool showSeconds: false
|
|
property bool padHours12Hour: false
|
|
property bool useFahrenheit: false
|
|
property string windSpeedUnit: "kmh"
|
|
property bool nightModeEnabled: false
|
|
property int animationSpeed: SettingsData.AnimationSpeed.Short
|
|
property int customAnimationDuration: 500
|
|
property string wallpaperFillMode: "Fill"
|
|
property bool blurredWallpaperLayer: false
|
|
property bool blurWallpaperOnOverview: false
|
|
|
|
property bool showLauncherButton: true
|
|
property bool showWorkspaceSwitcher: true
|
|
property bool showFocusedWindow: true
|
|
property bool showWeather: true
|
|
property bool showMusic: true
|
|
property bool showClipboard: true
|
|
property bool showCpuUsage: true
|
|
property bool showMemUsage: true
|
|
property bool showCpuTemp: true
|
|
property bool showGpuTemp: true
|
|
property int selectedGpuIndex: 0
|
|
property var enabledGpuPciIds: []
|
|
property bool showSystemTray: true
|
|
property bool showClock: true
|
|
property bool showNotificationButton: true
|
|
property bool showBattery: true
|
|
property bool showControlCenterButton: true
|
|
property bool showCapsLockIndicator: true
|
|
|
|
property bool controlCenterShowNetworkIcon: true
|
|
property bool controlCenterShowBluetoothIcon: true
|
|
property bool controlCenterShowAudioIcon: true
|
|
property bool controlCenterShowAudioPercent: false
|
|
property bool controlCenterShowVpnIcon: true
|
|
property bool controlCenterShowBrightnessIcon: false
|
|
property bool controlCenterShowBrightnessPercent: false
|
|
property bool controlCenterShowMicIcon: false
|
|
property bool controlCenterShowMicPercent: true
|
|
property bool controlCenterShowBatteryIcon: false
|
|
property bool controlCenterShowPrinterIcon: false
|
|
property bool controlCenterShowScreenSharingIcon: true
|
|
property bool showPrivacyButton: true
|
|
property bool privacyShowMicIcon: false
|
|
property bool privacyShowCameraIcon: false
|
|
property bool privacyShowScreenShareIcon: false
|
|
|
|
property var controlCenterWidgets: [
|
|
{
|
|
"id": "volumeSlider",
|
|
"enabled": true,
|
|
"width": 50
|
|
},
|
|
{
|
|
"id": "brightnessSlider",
|
|
"enabled": true,
|
|
"width": 50
|
|
},
|
|
{
|
|
"id": "wifi",
|
|
"enabled": true,
|
|
"width": 50
|
|
},
|
|
{
|
|
"id": "bluetooth",
|
|
"enabled": true,
|
|
"width": 50
|
|
},
|
|
{
|
|
"id": "audioOutput",
|
|
"enabled": true,
|
|
"width": 50
|
|
},
|
|
{
|
|
"id": "audioInput",
|
|
"enabled": true,
|
|
"width": 50
|
|
},
|
|
{
|
|
"id": "nightMode",
|
|
"enabled": true,
|
|
"width": 50
|
|
},
|
|
{
|
|
"id": "darkMode",
|
|
"enabled": true,
|
|
"width": 50
|
|
}
|
|
]
|
|
|
|
property bool showWorkspaceIndex: false
|
|
property bool showWorkspaceName: false
|
|
property bool showWorkspacePadding: false
|
|
property bool workspaceScrolling: false
|
|
property bool showWorkspaceApps: false
|
|
property bool groupWorkspaceApps: true
|
|
property int maxWorkspaceIcons: 3
|
|
property bool workspaceFollowFocus: false
|
|
property bool showOccupiedWorkspacesOnly: false
|
|
property bool reverseScrolling: false
|
|
property bool dwlShowAllTags: false
|
|
property string workspaceColorMode: "default"
|
|
property string workspaceOccupiedColorMode: "none"
|
|
property string workspaceUnfocusedColorMode: "default"
|
|
property string workspaceUrgentColorMode: "default"
|
|
property bool workspaceFocusedBorderEnabled: false
|
|
property string workspaceFocusedBorderColor: "primary"
|
|
property int workspaceFocusedBorderThickness: 2
|
|
property var workspaceNameIcons: ({})
|
|
property bool waveProgressEnabled: true
|
|
property bool scrollTitleEnabled: true
|
|
property bool audioVisualizerEnabled: true
|
|
property string audioScrollMode: "volume"
|
|
property bool clockCompactMode: false
|
|
property bool focusedWindowCompactMode: false
|
|
property bool runningAppsCompactMode: true
|
|
property bool keyboardLayoutNameCompactMode: false
|
|
property bool runningAppsCurrentWorkspace: false
|
|
property bool runningAppsGroupByApp: false
|
|
property var appIdSubstitutions: []
|
|
property string centeringMode: "index"
|
|
property string clockDateFormat: ""
|
|
property string lockDateFormat: ""
|
|
property int mediaSize: 1
|
|
|
|
property string appLauncherViewMode: "list"
|
|
property string spotlightModalViewMode: "list"
|
|
property string browserPickerViewMode: "grid"
|
|
property var browserUsageHistory: ({})
|
|
property string appPickerViewMode: "grid"
|
|
property var filePickerUsageHistory: ({})
|
|
property bool sortAppsAlphabetically: false
|
|
property int appLauncherGridColumns: 4
|
|
property bool spotlightCloseNiriOverview: true
|
|
property var spotlightSectionViewModes: ({})
|
|
onSpotlightSectionViewModesChanged: saveSettings()
|
|
property var appDrawerSectionViewModes: ({})
|
|
onAppDrawerSectionViewModesChanged: saveSettings()
|
|
property bool niriOverviewOverlayEnabled: true
|
|
property string dankLauncherV2Size: "compact"
|
|
property bool dankLauncherV2BorderEnabled: false
|
|
property int dankLauncherV2BorderThickness: 2
|
|
property string dankLauncherV2BorderColor: "primary"
|
|
property bool dankLauncherV2ShowFooter: true
|
|
|
|
property string _legacyWeatherLocation: "New York, NY"
|
|
property string _legacyWeatherCoordinates: "40.7128,-74.0060"
|
|
readonly property string weatherLocation: SessionData.weatherLocation
|
|
readonly property string weatherCoordinates: SessionData.weatherCoordinates
|
|
property bool useAutoLocation: false
|
|
property bool weatherEnabled: true
|
|
|
|
property string networkPreference: "auto"
|
|
property string vpnLastConnected: ""
|
|
|
|
property string iconTheme: "System Default"
|
|
property var availableIconThemes: ["System Default"]
|
|
property string systemDefaultIconTheme: ""
|
|
property bool qt5ctAvailable: false
|
|
property bool qt6ctAvailable: false
|
|
property bool gtkAvailable: false
|
|
|
|
property var cursorSettings: ({
|
|
"theme": "System Default",
|
|
"size": 24,
|
|
"niri": {
|
|
"hideWhenTyping": false,
|
|
"hideAfterInactiveMs": 0
|
|
},
|
|
"hyprland": {
|
|
"hideOnKeyPress": false,
|
|
"hideOnTouch": false,
|
|
"inactiveTimeout": 0
|
|
},
|
|
"dwl": {
|
|
"cursorHideTimeout": 0
|
|
}
|
|
})
|
|
property var availableCursorThemes: ["System Default"]
|
|
property string systemDefaultCursorTheme: ""
|
|
|
|
property string launcherLogoMode: "apps"
|
|
property string launcherLogoCustomPath: ""
|
|
property string launcherLogoColorOverride: ""
|
|
property bool launcherLogoColorInvertOnMode: false
|
|
property real launcherLogoBrightness: 0.5
|
|
property real launcherLogoContrast: 1
|
|
property int launcherLogoSizeOffset: 0
|
|
|
|
property string fontFamily: "Inter Variable"
|
|
property string monoFontFamily: "Fira Code"
|
|
property int fontWeight: Font.Normal
|
|
property real fontScale: 1.0
|
|
property real dankBarFontScale: 1.0
|
|
|
|
property bool notepadUseMonospace: true
|
|
property string notepadFontFamily: ""
|
|
property real notepadFontSize: 14
|
|
property bool notepadShowLineNumbers: false
|
|
property real notepadTransparencyOverride: -1
|
|
property real notepadLastCustomTransparency: 0.7
|
|
|
|
onNotepadUseMonospaceChanged: saveSettings()
|
|
onNotepadFontFamilyChanged: saveSettings()
|
|
onNotepadFontSizeChanged: saveSettings()
|
|
onNotepadShowLineNumbersChanged: saveSettings()
|
|
// onCenteringModeChanged: saveSettings()
|
|
onNotepadTransparencyOverrideChanged: {
|
|
if (notepadTransparencyOverride > 0) {
|
|
notepadLastCustomTransparency = notepadTransparencyOverride;
|
|
}
|
|
saveSettings();
|
|
}
|
|
onNotepadLastCustomTransparencyChanged: saveSettings()
|
|
|
|
property bool soundsEnabled: true
|
|
property bool useSystemSoundTheme: false
|
|
property bool soundNewNotification: true
|
|
property bool soundVolumeChanged: true
|
|
property bool soundPluggedIn: true
|
|
|
|
property int acMonitorTimeout: 0
|
|
property int acLockTimeout: 0
|
|
property int acSuspendTimeout: 0
|
|
property int acSuspendBehavior: SettingsData.SuspendBehavior.Suspend
|
|
property string acProfileName: ""
|
|
property int batteryMonitorTimeout: 0
|
|
property int batteryLockTimeout: 0
|
|
property int batterySuspendTimeout: 0
|
|
property int batterySuspendBehavior: SettingsData.SuspendBehavior.Suspend
|
|
property string batteryProfileName: ""
|
|
property int batteryChargeLimit: 100
|
|
property bool lockBeforeSuspend: false
|
|
property bool loginctlLockIntegration: true
|
|
property bool fadeToLockEnabled: true
|
|
property int fadeToLockGracePeriod: 5
|
|
property bool fadeToDpmsEnabled: true
|
|
property int fadeToDpmsGracePeriod: 5
|
|
property string launchPrefix: ""
|
|
property var brightnessDevicePins: ({})
|
|
property var wifiNetworkPins: ({})
|
|
property var bluetoothDevicePins: ({})
|
|
property var audioInputDevicePins: ({})
|
|
property var audioOutputDevicePins: ({})
|
|
|
|
property bool gtkThemingEnabled: false
|
|
property bool qtThemingEnabled: false
|
|
property bool syncModeWithPortal: true
|
|
property bool terminalsAlwaysDark: false
|
|
|
|
property bool runDmsMatugenTemplates: true
|
|
property bool matugenTemplateGtk: true
|
|
property bool matugenTemplateNiri: true
|
|
property bool matugenTemplateHyprland: true
|
|
property bool matugenTemplateMangowc: true
|
|
property bool matugenTemplateQt5ct: true
|
|
property bool matugenTemplateQt6ct: true
|
|
property bool matugenTemplateFirefox: true
|
|
property bool matugenTemplatePywalfox: true
|
|
property bool matugenTemplateZenBrowser: true
|
|
property bool matugenTemplateVesktop: true
|
|
property bool matugenTemplateEquibop: true
|
|
property bool matugenTemplateGhostty: true
|
|
property bool matugenTemplateKitty: true
|
|
property bool matugenTemplateFoot: true
|
|
property bool matugenTemplateNeovim: true
|
|
property bool matugenTemplateAlacritty: true
|
|
property bool matugenTemplateWezterm: true
|
|
property bool matugenTemplateDgop: true
|
|
property bool matugenTemplateKcolorscheme: true
|
|
property bool matugenTemplateVscode: true
|
|
property bool matugenTemplateEmacs: true
|
|
|
|
property bool showDock: false
|
|
property bool dockAutoHide: false
|
|
property bool dockSmartAutoHide: false
|
|
property bool dockGroupByApp: false
|
|
property bool dockOpenOnOverview: false
|
|
property int dockPosition: SettingsData.Position.Bottom
|
|
property real dockSpacing: 4
|
|
property real dockBottomGap: 0
|
|
property real dockMargin: 0
|
|
property real dockIconSize: 40
|
|
property string dockIndicatorStyle: "circle"
|
|
property bool dockBorderEnabled: false
|
|
property string dockBorderColor: "surfaceText"
|
|
property real dockBorderOpacity: 1.0
|
|
property int dockBorderThickness: 1
|
|
property bool dockIsolateDisplays: false
|
|
property bool dockLauncherEnabled: false
|
|
property string dockLauncherLogoMode: "apps"
|
|
property string dockLauncherLogoCustomPath: ""
|
|
property string dockLauncherLogoColorOverride: ""
|
|
property int dockLauncherLogoSizeOffset: 0
|
|
property real dockLauncherLogoBrightness: 0.5
|
|
property real dockLauncherLogoContrast: 1
|
|
|
|
property bool notificationOverlayEnabled: false
|
|
property int overviewRows: 2
|
|
property int overviewColumns: 5
|
|
property real overviewScale: 0.16
|
|
|
|
property bool modalDarkenBackground: true
|
|
|
|
property bool lockScreenShowPowerActions: true
|
|
property bool lockScreenShowSystemIcons: true
|
|
property bool lockScreenShowTime: true
|
|
property bool lockScreenShowDate: true
|
|
property bool lockScreenShowProfileImage: true
|
|
property bool lockScreenShowPasswordField: true
|
|
property bool lockScreenShowMediaPlayer: true
|
|
property bool lockScreenPowerOffMonitorsOnLock: false
|
|
|
|
property bool enableFprint: false
|
|
property int maxFprintTries: 15
|
|
property bool fprintdAvailable: false
|
|
property string lockScreenActiveMonitor: "all"
|
|
property string lockScreenInactiveColor: "#000000"
|
|
property int lockScreenNotificationMode: 0
|
|
property bool hideBrightnessSlider: false
|
|
|
|
property int notificationTimeoutLow: 5000
|
|
property int notificationTimeoutNormal: 5000
|
|
property int notificationTimeoutCritical: 0
|
|
property bool notificationCompactMode: false
|
|
property int notificationPopupPosition: SettingsData.Position.Top
|
|
property bool notificationHistoryEnabled: true
|
|
property int notificationHistoryMaxCount: 50
|
|
property int notificationHistoryMaxAgeDays: 7
|
|
property bool notificationHistorySaveLow: true
|
|
property bool notificationHistorySaveNormal: true
|
|
property bool notificationHistorySaveCritical: true
|
|
|
|
property bool osdAlwaysShowValue: false
|
|
property int osdPosition: SettingsData.Position.BottomCenter
|
|
property bool osdVolumeEnabled: true
|
|
property bool osdMediaVolumeEnabled: true
|
|
property bool osdBrightnessEnabled: true
|
|
property bool osdIdleInhibitorEnabled: true
|
|
property bool osdMicMuteEnabled: true
|
|
property bool osdCapsLockEnabled: true
|
|
property bool osdPowerProfileEnabled: true
|
|
property bool osdAudioOutputEnabled: true
|
|
|
|
property bool powerActionConfirm: true
|
|
property real powerActionHoldDuration: 0.5
|
|
property var powerMenuActions: ["reboot", "logout", "poweroff", "lock", "suspend", "restart"]
|
|
property string powerMenuDefaultAction: "logout"
|
|
property bool powerMenuGridLayout: false
|
|
property string customPowerActionLock: ""
|
|
property string customPowerActionLogout: ""
|
|
property string customPowerActionSuspend: ""
|
|
property string customPowerActionHibernate: ""
|
|
property string customPowerActionReboot: ""
|
|
property string customPowerActionPowerOff: ""
|
|
|
|
property bool updaterHideWidget: false
|
|
property bool updaterUseCustomCommand: false
|
|
property string updaterCustomCommand: ""
|
|
property string updaterTerminalAdditionalParams: ""
|
|
|
|
property string displayNameMode: "system"
|
|
property var screenPreferences: ({})
|
|
property var showOnLastDisplay: ({})
|
|
property var niriOutputSettings: ({})
|
|
property var hyprlandOutputSettings: ({})
|
|
|
|
property var barConfigs: [
|
|
{
|
|
"id": "default",
|
|
"name": "Main Bar",
|
|
"enabled": true,
|
|
"position": 0,
|
|
"screenPreferences": ["all"],
|
|
"showOnLastDisplay": true,
|
|
"leftWidgets": ["launcherButton", "workspaceSwitcher", "focusedWindow"],
|
|
"centerWidgets": ["music", "clock", "weather"],
|
|
"rightWidgets": ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"],
|
|
"spacing": 4,
|
|
"innerPadding": 4,
|
|
"bottomGap": 0,
|
|
"transparency": 1.0,
|
|
"widgetTransparency": 1.0,
|
|
"squareCorners": false,
|
|
"noBackground": false,
|
|
"gothCornersEnabled": false,
|
|
"gothCornerRadiusOverride": false,
|
|
"gothCornerRadiusValue": 12,
|
|
"borderEnabled": false,
|
|
"borderColor": "surfaceText",
|
|
"borderOpacity": 1.0,
|
|
"borderThickness": 1,
|
|
"widgetOutlineEnabled": false,
|
|
"widgetOutlineColor": "primary",
|
|
"widgetOutlineOpacity": 1.0,
|
|
"widgetOutlineThickness": 1,
|
|
"fontScale": 1.0,
|
|
"autoHide": false,
|
|
"autoHideDelay": 250,
|
|
"showOnWindowsOpen": false,
|
|
"openOnOverview": false,
|
|
"visible": true,
|
|
"popupGapsAuto": true,
|
|
"popupGapsManual": 4,
|
|
"maximizeDetection": true,
|
|
"scrollEnabled": true,
|
|
"scrollXBehavior": "column",
|
|
"scrollYBehavior": "workspace",
|
|
"shadowIntensity": 0,
|
|
"shadowOpacity": 60,
|
|
"shadowColorMode": "text",
|
|
"shadowCustomColor": "#000000",
|
|
"clickThrough": false
|
|
}
|
|
]
|
|
|
|
property bool desktopClockEnabled: false
|
|
property string desktopClockStyle: "analog"
|
|
property real desktopClockTransparency: 0.8
|
|
property string desktopClockColorMode: "primary"
|
|
property color desktopClockCustomColor: "#ffffff"
|
|
property bool desktopClockShowDate: true
|
|
property bool desktopClockShowAnalogNumbers: false
|
|
property bool desktopClockShowAnalogSeconds: true
|
|
property real desktopClockX: -1
|
|
property real desktopClockY: -1
|
|
property real desktopClockWidth: 280
|
|
property real desktopClockHeight: 180
|
|
property var desktopClockDisplayPreferences: ["all"]
|
|
|
|
property bool systemMonitorEnabled: false
|
|
property bool systemMonitorShowHeader: true
|
|
property real systemMonitorTransparency: 0.8
|
|
property string systemMonitorColorMode: "primary"
|
|
property color systemMonitorCustomColor: "#ffffff"
|
|
property bool systemMonitorShowCpu: true
|
|
property bool systemMonitorShowCpuGraph: true
|
|
property bool systemMonitorShowCpuTemp: true
|
|
property bool systemMonitorShowGpuTemp: false
|
|
property string systemMonitorGpuPciId: ""
|
|
property bool systemMonitorShowMemory: true
|
|
property bool systemMonitorShowMemoryGraph: true
|
|
property bool systemMonitorShowNetwork: true
|
|
property bool systemMonitorShowNetworkGraph: true
|
|
property bool systemMonitorShowDisk: true
|
|
property bool systemMonitorShowTopProcesses: false
|
|
property int systemMonitorTopProcessCount: 3
|
|
property string systemMonitorTopProcessSortBy: "cpu"
|
|
property string systemMonitorLayoutMode: "auto"
|
|
property int systemMonitorGraphInterval: 60
|
|
property real systemMonitorX: -1
|
|
property real systemMonitorY: -1
|
|
property real systemMonitorWidth: 320
|
|
property real systemMonitorHeight: 480
|
|
property var systemMonitorDisplayPreferences: ["all"]
|
|
property var systemMonitorVariants: []
|
|
property var desktopWidgetPositions: ({})
|
|
property var desktopWidgetGridSettings: ({})
|
|
property var desktopWidgetInstances: []
|
|
property var desktopWidgetGroups: []
|
|
|
|
function getDesktopWidgetGridSetting(screenKey, property, defaultValue) {
|
|
const val = desktopWidgetGridSettings?.[screenKey]?.[property];
|
|
return val !== undefined ? val : defaultValue;
|
|
}
|
|
|
|
function setDesktopWidgetGridSetting(screenKey, property, value) {
|
|
const allSettings = JSON.parse(JSON.stringify(desktopWidgetGridSettings || {}));
|
|
if (!allSettings[screenKey])
|
|
allSettings[screenKey] = {};
|
|
allSettings[screenKey][property] = value;
|
|
desktopWidgetGridSettings = allSettings;
|
|
saveSettings();
|
|
}
|
|
|
|
function getDesktopWidgetPosition(pluginId, screenKey, property, defaultValue) {
|
|
const pos = desktopWidgetPositions?.[pluginId]?.[screenKey]?.[property];
|
|
return pos !== undefined ? pos : defaultValue;
|
|
}
|
|
|
|
function updateDesktopWidgetPosition(pluginId, screenKey, updates) {
|
|
const allPositions = JSON.parse(JSON.stringify(desktopWidgetPositions || {}));
|
|
if (!allPositions[pluginId])
|
|
allPositions[pluginId] = {};
|
|
allPositions[pluginId][screenKey] = Object.assign({}, allPositions[pluginId][screenKey] || {}, updates);
|
|
desktopWidgetPositions = allPositions;
|
|
saveSettings();
|
|
}
|
|
|
|
function getSystemMonitorVariants() {
|
|
return systemMonitorVariants || [];
|
|
}
|
|
|
|
function createSystemMonitorVariant(name, config) {
|
|
const id = "sysmon_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9);
|
|
const variant = {
|
|
id: id,
|
|
name: name,
|
|
config: config || getDefaultSystemMonitorConfig()
|
|
};
|
|
const variants = JSON.parse(JSON.stringify(systemMonitorVariants || []));
|
|
variants.push(variant);
|
|
systemMonitorVariants = variants;
|
|
saveSettings();
|
|
return variant;
|
|
}
|
|
|
|
function updateSystemMonitorVariant(variantId, updates) {
|
|
const variants = JSON.parse(JSON.stringify(systemMonitorVariants || []));
|
|
const idx = variants.findIndex(v => v.id === variantId);
|
|
if (idx === -1)
|
|
return;
|
|
Object.assign(variants[idx], updates);
|
|
systemMonitorVariants = variants;
|
|
saveSettings();
|
|
}
|
|
|
|
function removeSystemMonitorVariant(variantId) {
|
|
const variants = (systemMonitorVariants || []).filter(v => v.id !== variantId);
|
|
systemMonitorVariants = variants;
|
|
saveSettings();
|
|
}
|
|
|
|
function getSystemMonitorVariant(variantId) {
|
|
return (systemMonitorVariants || []).find(v => v.id === variantId) || null;
|
|
}
|
|
|
|
function getDefaultSystemMonitorConfig() {
|
|
return {
|
|
showHeader: true,
|
|
transparency: 0.8,
|
|
colorMode: "primary",
|
|
customColor: "#ffffff",
|
|
showCpu: true,
|
|
showCpuGraph: true,
|
|
showCpuTemp: true,
|
|
showGpuTemp: false,
|
|
gpuPciId: "",
|
|
showMemory: true,
|
|
showMemoryGraph: true,
|
|
showNetwork: true,
|
|
showNetworkGraph: true,
|
|
showDisk: true,
|
|
showTopProcesses: false,
|
|
topProcessCount: 3,
|
|
topProcessSortBy: "cpu",
|
|
layoutMode: "auto",
|
|
graphInterval: 60,
|
|
x: -1,
|
|
y: -1,
|
|
width: 320,
|
|
height: 480,
|
|
displayPreferences: ["all"]
|
|
};
|
|
}
|
|
|
|
function createDesktopWidgetInstance(widgetType, name, config) {
|
|
const id = "dw_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9);
|
|
const instance = {
|
|
id: id,
|
|
widgetType: widgetType,
|
|
name: name || widgetType,
|
|
enabled: true,
|
|
config: config || {},
|
|
positions: {}
|
|
};
|
|
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
|
instances.push(instance);
|
|
desktopWidgetInstances = instances;
|
|
saveSettings();
|
|
return instance;
|
|
}
|
|
|
|
function updateDesktopWidgetInstance(instanceId, updates) {
|
|
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
|
const idx = instances.findIndex(inst => inst.id === instanceId);
|
|
if (idx === -1)
|
|
return;
|
|
Object.assign(instances[idx], updates);
|
|
desktopWidgetInstances = instances;
|
|
saveSettings();
|
|
}
|
|
|
|
function updateDesktopWidgetInstanceConfig(instanceId, configUpdates) {
|
|
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
|
const idx = instances.findIndex(inst => inst.id === instanceId);
|
|
if (idx === -1)
|
|
return;
|
|
instances[idx].config = Object.assign({}, instances[idx].config || {}, configUpdates);
|
|
desktopWidgetInstances = instances;
|
|
saveSettings();
|
|
}
|
|
|
|
function updateDesktopWidgetInstancePosition(instanceId, screenKey, positionUpdates) {
|
|
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
|
const idx = instances.findIndex(inst => inst.id === instanceId);
|
|
if (idx === -1)
|
|
return;
|
|
if (!instances[idx].positions)
|
|
instances[idx].positions = {};
|
|
instances[idx].positions[screenKey] = Object.assign({}, instances[idx].positions[screenKey] || {}, positionUpdates);
|
|
desktopWidgetInstances = instances;
|
|
saveSettings();
|
|
}
|
|
|
|
function removeDesktopWidgetInstance(instanceId) {
|
|
const instances = (desktopWidgetInstances || []).filter(inst => inst.id !== instanceId);
|
|
desktopWidgetInstances = instances;
|
|
saveSettings();
|
|
}
|
|
|
|
function syncDesktopWidgetPositionToAllScreens(instanceId) {
|
|
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
|
const idx = instances.findIndex(inst => inst.id === instanceId);
|
|
if (idx === -1)
|
|
return;
|
|
const positions = instances[idx].positions || {};
|
|
const screenKeys = Object.keys(positions).filter(k => k !== "_synced");
|
|
if (screenKeys.length === 0)
|
|
return;
|
|
const sourceKey = screenKeys[0];
|
|
const sourcePos = positions[sourceKey];
|
|
if (!sourcePos)
|
|
return;
|
|
const screen = Array.from(Quickshell.screens.values()).find(s => getScreenDisplayName(s) === sourceKey);
|
|
if (!screen)
|
|
return;
|
|
const screenW = screen.width;
|
|
const screenH = screen.height;
|
|
const synced = {};
|
|
if (sourcePos.x !== undefined)
|
|
synced.x = sourcePos.x / screenW;
|
|
if (sourcePos.y !== undefined)
|
|
synced.y = sourcePos.y / screenH;
|
|
if (sourcePos.width !== undefined)
|
|
synced.width = sourcePos.width;
|
|
if (sourcePos.height !== undefined)
|
|
synced.height = sourcePos.height;
|
|
instances[idx].positions["_synced"] = synced;
|
|
desktopWidgetInstances = instances;
|
|
saveSettings();
|
|
}
|
|
|
|
function duplicateDesktopWidgetInstance(instanceId) {
|
|
const source = getDesktopWidgetInstance(instanceId);
|
|
if (!source)
|
|
return null;
|
|
const newId = "dw_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9);
|
|
const instance = {
|
|
id: newId,
|
|
widgetType: source.widgetType,
|
|
name: source.name + " (Copy)",
|
|
enabled: source.enabled,
|
|
config: JSON.parse(JSON.stringify(source.config || {})),
|
|
positions: {}
|
|
};
|
|
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
|
instances.push(instance);
|
|
desktopWidgetInstances = instances;
|
|
saveSettings();
|
|
return instance;
|
|
}
|
|
|
|
function getDesktopWidgetInstance(instanceId) {
|
|
return (desktopWidgetInstances || []).find(inst => inst.id === instanceId) || null;
|
|
}
|
|
|
|
function getDesktopWidgetInstancesOfType(widgetType) {
|
|
return (desktopWidgetInstances || []).filter(inst => inst.widgetType === widgetType);
|
|
}
|
|
|
|
function getEnabledDesktopWidgetInstances() {
|
|
return (desktopWidgetInstances || []).filter(inst => inst.enabled);
|
|
}
|
|
|
|
function moveDesktopWidgetInstance(instanceId, direction) {
|
|
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
|
const idx = instances.findIndex(inst => inst.id === instanceId);
|
|
if (idx === -1)
|
|
return false;
|
|
const targetIdx = direction === "up" ? idx - 1 : idx + 1;
|
|
if (targetIdx < 0 || targetIdx >= instances.length)
|
|
return false;
|
|
const temp = instances[idx];
|
|
instances[idx] = instances[targetIdx];
|
|
instances[targetIdx] = temp;
|
|
desktopWidgetInstances = instances;
|
|
saveSettings();
|
|
return true;
|
|
}
|
|
|
|
function reorderDesktopWidgetInstance(instanceId, newIndex) {
|
|
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
|
const idx = instances.findIndex(inst => inst.id === instanceId);
|
|
if (idx === -1 || newIndex < 0 || newIndex >= instances.length)
|
|
return false;
|
|
const [item] = instances.splice(idx, 1);
|
|
instances.splice(newIndex, 0, item);
|
|
desktopWidgetInstances = instances;
|
|
saveSettings();
|
|
return true;
|
|
}
|
|
|
|
function reorderDesktopWidgetInstanceInGroup(instanceId, groupId, newIndexInGroup) {
|
|
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
|
const groups = desktopWidgetGroups || [];
|
|
const groupMatches = inst => {
|
|
if (groupId === null)
|
|
return !inst.group || !groups.some(g => g.id === inst.group);
|
|
return inst.group === groupId;
|
|
};
|
|
const groupInstances = instances.filter(groupMatches);
|
|
const currentGroupIdx = groupInstances.findIndex(inst => inst.id === instanceId);
|
|
if (currentGroupIdx === -1 || currentGroupIdx === newIndexInGroup)
|
|
return false;
|
|
if (newIndexInGroup < 0 || newIndexInGroup >= groupInstances.length)
|
|
return false;
|
|
const globalIdx = instances.findIndex(inst => inst.id === instanceId);
|
|
if (globalIdx === -1)
|
|
return false;
|
|
const [item] = instances.splice(globalIdx, 1);
|
|
const targetInstance = groupInstances[newIndexInGroup];
|
|
let targetGlobalIdx = instances.findIndex(inst => inst.id === targetInstance.id);
|
|
if (newIndexInGroup > currentGroupIdx)
|
|
targetGlobalIdx++;
|
|
instances.splice(targetGlobalIdx, 0, item);
|
|
desktopWidgetInstances = instances;
|
|
saveSettings();
|
|
return true;
|
|
}
|
|
|
|
function createDesktopWidgetGroup(name) {
|
|
const id = "dwg_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9);
|
|
const group = {
|
|
id: id,
|
|
name: name,
|
|
collapsed: false
|
|
};
|
|
const groups = JSON.parse(JSON.stringify(desktopWidgetGroups || []));
|
|
groups.push(group);
|
|
desktopWidgetGroups = groups;
|
|
saveSettings();
|
|
return group;
|
|
}
|
|
|
|
function updateDesktopWidgetGroup(groupId, updates) {
|
|
const groups = JSON.parse(JSON.stringify(desktopWidgetGroups || []));
|
|
const idx = groups.findIndex(g => g.id === groupId);
|
|
if (idx === -1)
|
|
return;
|
|
Object.assign(groups[idx], updates);
|
|
desktopWidgetGroups = groups;
|
|
saveSettings();
|
|
}
|
|
|
|
function removeDesktopWidgetGroup(groupId) {
|
|
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
|
for (let i = 0; i < instances.length; i++) {
|
|
if (instances[i].group === groupId)
|
|
instances[i].group = null;
|
|
}
|
|
desktopWidgetInstances = instances;
|
|
const groups = (desktopWidgetGroups || []).filter(g => g.id !== groupId);
|
|
desktopWidgetGroups = groups;
|
|
saveSettings();
|
|
}
|
|
|
|
function getDesktopWidgetGroup(groupId) {
|
|
return (desktopWidgetGroups || []).find(g => g.id === groupId) || null;
|
|
}
|
|
|
|
function getDesktopWidgetInstancesByGroup(groupId) {
|
|
return (desktopWidgetInstances || []).filter(inst => inst.group === groupId);
|
|
}
|
|
|
|
function getUngroupedDesktopWidgetInstances() {
|
|
return (desktopWidgetInstances || []).filter(inst => !inst.group);
|
|
}
|
|
|
|
signal forceDankBarLayoutRefresh
|
|
signal forceDockLayoutRefresh
|
|
signal widgetDataChanged
|
|
signal workspaceIconsUpdated
|
|
|
|
Component.onCompleted: {
|
|
if (!isGreeterMode) {
|
|
Processes.settingsRoot = root;
|
|
loadSettings();
|
|
initializeListModels();
|
|
Processes.detectFprintd();
|
|
Processes.checkPluginSettings();
|
|
}
|
|
}
|
|
|
|
function applyStoredTheme() {
|
|
if (typeof Theme !== "undefined") {
|
|
Theme.currentThemeCategory = currentThemeCategory;
|
|
Theme.switchTheme(currentThemeName, false, false);
|
|
} else {
|
|
Qt.callLater(function () {
|
|
if (typeof Theme !== "undefined") {
|
|
Theme.currentThemeCategory = currentThemeCategory;
|
|
Theme.switchTheme(currentThemeName, false, false);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function regenSystemThemes() {
|
|
if (typeof Theme !== "undefined") {
|
|
Theme.generateSystemThemesFromCurrentTheme();
|
|
}
|
|
}
|
|
|
|
function updateCompositorLayout() {
|
|
if (typeof CompositorService === "undefined")
|
|
return;
|
|
if (CompositorService.isNiri && typeof NiriService !== "undefined")
|
|
NiriService.generateNiriLayoutConfig();
|
|
if (CompositorService.isHyprland && typeof HyprlandService !== "undefined")
|
|
HyprlandService.generateLayoutConfig();
|
|
if (CompositorService.isDwl && typeof DwlService !== "undefined")
|
|
DwlService.generateLayoutConfig();
|
|
}
|
|
|
|
function applyStoredIconTheme() {
|
|
updateGtkIconTheme();
|
|
updateQtIconTheme();
|
|
}
|
|
|
|
function updateGtkIconTheme() {
|
|
const gtkThemeName = (iconTheme === "System Default") ? systemDefaultIconTheme : iconTheme;
|
|
if (gtkThemeName === "System Default" || gtkThemeName === "")
|
|
return;
|
|
if (typeof DMSService !== "undefined" && DMSService.apiVersion >= 3 && typeof PortalService !== "undefined") {
|
|
PortalService.setSystemIconTheme(gtkThemeName);
|
|
}
|
|
|
|
const configScript = `mkdir -p ${_configDir}/gtk-3.0 ${_configDir}/gtk-4.0
|
|
|
|
for config_dir in ${_configDir}/gtk-3.0 ${_configDir}/gtk-4.0; do
|
|
settings_file="$config_dir/settings.ini"
|
|
if [ -f "$settings_file" ]; then
|
|
if grep -q "^gtk-icon-theme-name=" "$settings_file"; then
|
|
sed -i 's/^gtk-icon-theme-name=.*/gtk-icon-theme-name=${gtkThemeName}/' "$settings_file"
|
|
else
|
|
if grep -q "\\[Settings\\]" "$settings_file"; then
|
|
sed -i '/\\[Settings\\]/a gtk-icon-theme-name=${gtkThemeName}' "$settings_file"
|
|
else
|
|
echo -e '\\n[Settings]\\ngtk-icon-theme-name=${gtkThemeName}' >> "$settings_file"
|
|
fi
|
|
fi
|
|
else
|
|
echo -e '[Settings]\\ngtk-icon-theme-name=${gtkThemeName}' > "$settings_file"
|
|
fi
|
|
done
|
|
|
|
rm -rf ~/.cache/icon-cache ~/.cache/thumbnails 2>/dev/null || true
|
|
pkill -HUP -f 'gtk' 2>/dev/null || true`;
|
|
|
|
Quickshell.execDetached(["sh", "-lc", configScript]);
|
|
}
|
|
|
|
function updateQtIconTheme() {
|
|
const qtThemeName = (iconTheme === "System Default") ? "" : iconTheme;
|
|
if (!qtThemeName)
|
|
return;
|
|
const home = _homeUrl.replace("file://", "").replace(/'/g, "'\\''");
|
|
const qtThemeNameEscaped = qtThemeName.replace(/'/g, "'\\''");
|
|
|
|
const script = `mkdir -p ${_configDir}/qt5ct ${_configDir}/qt6ct ${_configDir}/environment.d 2>/dev/null || true
|
|
update_qt_icon_theme() {
|
|
local config_file="$1"
|
|
local theme_name="$2"
|
|
if [ -f "$config_file" ]; then
|
|
if grep -q "^\\[Appearance\\]" "$config_file"; then
|
|
if grep -q "^icon_theme=" "$config_file"; then
|
|
sed -i "s/^icon_theme=.*/icon_theme=$theme_name/" "$config_file"
|
|
else
|
|
sed -i "/^\\[Appearance\\]/a icon_theme=$theme_name" "$config_file"
|
|
fi
|
|
else
|
|
printf "\\n[Appearance]\\nicon_theme=%s\\n" "$theme_name" >> "$config_file"
|
|
fi
|
|
else
|
|
printf "[Appearance]\\nicon_theme=%s\\n" "$theme_name" > "$config_file"
|
|
fi
|
|
}
|
|
update_qt_icon_theme ${_configDir}/qt5ct/qt5ct.conf '${qtThemeNameEscaped}'
|
|
update_qt_icon_theme ${_configDir}/qt6ct/qt6ct.conf '${qtThemeNameEscaped}'
|
|
rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || true`;
|
|
|
|
Quickshell.execDetached(["sh", "-lc", script]);
|
|
}
|
|
|
|
readonly property var _hooks: ({
|
|
"applyStoredTheme": applyStoredTheme,
|
|
"regenSystemThemes": regenSystemThemes,
|
|
"updateCompositorLayout": updateCompositorLayout,
|
|
"applyStoredIconTheme": applyStoredIconTheme,
|
|
"updateBarConfigs": updateBarConfigs,
|
|
"updateCompositorCursor": updateCompositorCursor
|
|
})
|
|
|
|
function set(key, value) {
|
|
Spec.set(root, key, value, saveSettings, _hooks);
|
|
}
|
|
|
|
function loadSettings() {
|
|
_loading = true;
|
|
_parseError = false;
|
|
_hasUnsavedChanges = false;
|
|
_pendingMigration = null;
|
|
|
|
try {
|
|
const txt = settingsFile.text();
|
|
let obj = (txt && txt.trim()) ? JSON.parse(txt) : null;
|
|
|
|
const oldVersion = obj?.configVersion ?? 0;
|
|
if (oldVersion < settingsConfigVersion) {
|
|
const migrated = Store.migrateToVersion(obj, settingsConfigVersion);
|
|
if (migrated) {
|
|
_pendingMigration = migrated;
|
|
obj = migrated;
|
|
}
|
|
}
|
|
|
|
Store.parse(root, obj);
|
|
|
|
if (obj?.weatherLocation !== undefined)
|
|
_legacyWeatherLocation = obj.weatherLocation;
|
|
if (obj?.weatherCoordinates !== undefined)
|
|
_legacyWeatherCoordinates = obj.weatherCoordinates;
|
|
|
|
_loadedSettingsSnapshot = JSON.stringify(Store.toJson(root));
|
|
_hasLoaded = true;
|
|
applyStoredTheme();
|
|
applyStoredIconTheme();
|
|
updateCompositorCursor();
|
|
Processes.detectQtTools();
|
|
|
|
_checkSettingsWritable();
|
|
} catch (e) {
|
|
_parseError = true;
|
|
const msg = e.message;
|
|
console.error("SettingsData: Failed to parse settings.json - file will not be overwritten. Error:", msg);
|
|
Qt.callLater(() => ToastService.showError(I18n.tr("Failed to parse settings.json"), msg));
|
|
applyStoredTheme();
|
|
applyStoredIconTheme();
|
|
} finally {
|
|
_loading = false;
|
|
}
|
|
loadPluginSettings();
|
|
}
|
|
|
|
property var _pendingMigration: null
|
|
|
|
function _checkSettingsWritable() {
|
|
settingsWritableCheckProcess.running = true;
|
|
}
|
|
|
|
function _onWritableCheckComplete(writable) {
|
|
const wasReadOnly = _isReadOnly;
|
|
_isReadOnly = !writable;
|
|
if (_isReadOnly) {
|
|
_hasUnsavedChanges = _checkForUnsavedChanges();
|
|
if (!wasReadOnly)
|
|
console.info("SettingsData: settings.json is now read-only");
|
|
} else {
|
|
_loadedSettingsSnapshot = JSON.stringify(Store.toJson(root));
|
|
_hasUnsavedChanges = false;
|
|
if (wasReadOnly)
|
|
console.info("SettingsData: settings.json is now writable");
|
|
if (_pendingMigration)
|
|
settingsFile.setText(JSON.stringify(_pendingMigration, null, 2));
|
|
}
|
|
_pendingMigration = null;
|
|
}
|
|
|
|
function _checkForUnsavedChanges() {
|
|
if (!_hasLoaded || !_loadedSettingsSnapshot)
|
|
return false;
|
|
const current = JSON.stringify(Store.toJson(root));
|
|
return current !== _loadedSettingsSnapshot;
|
|
}
|
|
|
|
function getCurrentSettingsJson() {
|
|
return JSON.stringify(Store.toJson(root), null, 2);
|
|
}
|
|
|
|
function loadPluginSettings() {
|
|
_pluginSettingsLoading = true;
|
|
parsePluginSettings(pluginSettingsFile.text());
|
|
_pluginSettingsLoading = false;
|
|
}
|
|
|
|
function parsePluginSettings(content) {
|
|
_pluginSettingsLoading = true;
|
|
_pluginParseError = false;
|
|
try {
|
|
if (content && content.trim()) {
|
|
pluginSettings = JSON.parse(content);
|
|
} else {
|
|
pluginSettings = {};
|
|
}
|
|
} catch (e) {
|
|
_pluginParseError = true;
|
|
const msg = e.message;
|
|
console.error("SettingsData: Failed to parse plugin_settings.json - file will not be overwritten. Error:", msg);
|
|
Qt.callLater(() => ToastService.showError(I18n.tr("Failed to parse plugin_settings.json"), msg));
|
|
pluginSettings = {};
|
|
} finally {
|
|
_pluginSettingsLoading = false;
|
|
}
|
|
}
|
|
|
|
function saveSettings() {
|
|
if (_loading || _parseError || !_hasLoaded)
|
|
return;
|
|
settingsFile.setText(JSON.stringify(Store.toJson(root), null, 2));
|
|
if (_isReadOnly)
|
|
_checkSettingsWritable();
|
|
}
|
|
|
|
function savePluginSettings() {
|
|
if (_pluginSettingsLoading || _pluginParseError)
|
|
return;
|
|
pluginSettingsFile.setText(JSON.stringify(pluginSettings, null, 2));
|
|
}
|
|
|
|
function detectAvailableIconThemes() {
|
|
const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS") || "";
|
|
const localData = Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation));
|
|
const homeDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.HomeLocation));
|
|
|
|
const dataDirs = xdgDataDirs.trim() !== "" ? xdgDataDirs.split(":").concat([localData]) : ["/usr/share", "/usr/local/share", localData];
|
|
|
|
const iconPaths = dataDirs.map(d => d + "/icons").concat([homeDir + "/.icons"]);
|
|
const pathsArg = iconPaths.join(" ");
|
|
|
|
const script = `
|
|
echo "SYSDEFAULT:$(gsettings get org.gnome.desktop.interface icon-theme 2>/dev/null | sed "s/'//g" || echo '')"
|
|
for dir in ${pathsArg}; do
|
|
[ -d "$dir" ] || continue
|
|
for theme in "$dir"/*/; do
|
|
[ -d "$theme" ] || continue
|
|
basename "$theme"
|
|
done
|
|
done | grep -v '^icons$' | grep -v '^default$' | grep -v '^hicolor$' | grep -v '^locolor$' | sort -u
|
|
`;
|
|
|
|
Proc.runCommand("detectIconThemes", ["sh", "-c", script], (output, exitCode) => {
|
|
const themes = ["System Default"];
|
|
if (output && output.trim()) {
|
|
const lines = output.trim().split('\n');
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const line = lines[i].trim();
|
|
if (line.startsWith("SYSDEFAULT:")) {
|
|
systemDefaultIconTheme = line.substring(11).trim();
|
|
continue;
|
|
}
|
|
if (line)
|
|
themes.push(line);
|
|
}
|
|
}
|
|
availableIconThemes = themes;
|
|
});
|
|
}
|
|
|
|
function detectAvailableCursorThemes() {
|
|
const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS") || "";
|
|
const localData = Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation));
|
|
const homeDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.HomeLocation));
|
|
|
|
const dataDirs = xdgDataDirs.trim() !== "" ? xdgDataDirs.split(":").concat([localData]) : ["/usr/share", "/usr/local/share", localData];
|
|
|
|
const cursorPaths = dataDirs.map(d => d + "/icons").concat([homeDir + "/.icons", homeDir + "/.local/share/icons"]);
|
|
const pathsArg = cursorPaths.join(" ");
|
|
|
|
const script = `
|
|
echo "SYSDEFAULT:$(gsettings get org.gnome.desktop.interface cursor-theme 2>/dev/null | sed "s/'//g" || echo '')"
|
|
for dir in ${pathsArg}; do
|
|
[ -d "$dir" ] || continue
|
|
for theme in "$dir"/*/; do
|
|
[ -d "$theme" ] || continue
|
|
[ -d "$theme/cursors" ] || continue
|
|
basename "$theme"
|
|
done
|
|
done | grep -v '^icons$' | grep -v '^default$' | sort -u
|
|
`;
|
|
|
|
Proc.runCommand("detectCursorThemes", ["sh", "-c", script], (output, exitCode) => {
|
|
const themes = ["System Default"];
|
|
if (output && output.trim()) {
|
|
const lines = output.trim().split('\n');
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const line = lines[i].trim();
|
|
if (line.startsWith("SYSDEFAULT:")) {
|
|
systemDefaultCursorTheme = line.substring(11).trim();
|
|
continue;
|
|
}
|
|
if (line)
|
|
themes.push(line);
|
|
}
|
|
}
|
|
availableCursorThemes = themes;
|
|
});
|
|
}
|
|
|
|
function getEffectiveTimeFormat() {
|
|
if (use24HourClock)
|
|
return showSeconds ? "hh:mm:ss" : "hh:mm";
|
|
if (padHours12Hour)
|
|
return showSeconds ? "hh:mm:ss AP" : "hh:mm AP";
|
|
return showSeconds ? "h:mm:ss AP" : "h:mm AP";
|
|
}
|
|
|
|
function getEffectiveClockDateFormat() {
|
|
return clockDateFormat && clockDateFormat.length > 0 ? clockDateFormat : "ddd d";
|
|
}
|
|
|
|
function getEffectiveLockDateFormat() {
|
|
return lockDateFormat && lockDateFormat.length > 0 ? lockDateFormat : Locale.LongFormat;
|
|
}
|
|
|
|
function initializeListModels() {
|
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
|
if (defaultBar) {
|
|
Lists.init(leftWidgetsModel, centerWidgetsModel, rightWidgetsModel, defaultBar.leftWidgets, defaultBar.centerWidgets, defaultBar.rightWidgets);
|
|
}
|
|
}
|
|
|
|
function updateListModel(listModel, order) {
|
|
Lists.update(listModel, order);
|
|
widgetDataChanged();
|
|
}
|
|
|
|
function hasNamedWorkspaces() {
|
|
if (typeof NiriService === "undefined" || !CompositorService.isNiri)
|
|
return false;
|
|
|
|
for (var i = 0; i < NiriService.allWorkspaces.length; i++) {
|
|
var ws = NiriService.allWorkspaces[i];
|
|
if (ws.name && ws.name.trim() !== "")
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function getNamedWorkspaces() {
|
|
var namedWorkspaces = [];
|
|
if (typeof NiriService === "undefined" || !CompositorService.isNiri)
|
|
return namedWorkspaces;
|
|
|
|
for (const ws of NiriService.allWorkspaces) {
|
|
if (ws.name && ws.name.trim() !== "") {
|
|
namedWorkspaces.push(ws.name);
|
|
}
|
|
}
|
|
return namedWorkspaces;
|
|
}
|
|
|
|
function getPopupYPosition(barHeight) {
|
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
|
const gothOffset = defaultBar?.gothCornersEnabled ? Theme.cornerRadius : 0;
|
|
const spacing = defaultBar?.spacing ?? 4;
|
|
const bottomGap = defaultBar?.bottomGap ?? 0;
|
|
return barHeight + spacing + bottomGap - gothOffset + Theme.popupDistance;
|
|
}
|
|
|
|
function getPopupTriggerPosition(pos, screen, barThickness, widgetWidth, barSpacing, barPosition, barConfig) {
|
|
const relativeX = pos.x;
|
|
const relativeY = pos.y;
|
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
|
const spacing = barSpacing !== undefined ? barSpacing : (defaultBar?.spacing ?? 4);
|
|
const position = barPosition !== undefined ? barPosition : (defaultBar?.position ?? SettingsData.Position.Top);
|
|
const rawBottomGap = barConfig ? (barConfig.bottomGap !== undefined ? barConfig.bottomGap : (defaultBar?.bottomGap ?? 0)) : (defaultBar?.bottomGap ?? 0);
|
|
const bottomGap = Math.max(0, rawBottomGap);
|
|
|
|
const useAutoGaps = (barConfig && barConfig.popupGapsAuto !== undefined) ? barConfig.popupGapsAuto : (defaultBar?.popupGapsAuto ?? true);
|
|
const manualGapValue = (barConfig && barConfig.popupGapsManual !== undefined) ? barConfig.popupGapsManual : (defaultBar?.popupGapsManual ?? 4);
|
|
const popupGap = useAutoGaps ? Math.max(4, spacing) : manualGapValue;
|
|
|
|
switch (position) {
|
|
case SettingsData.Position.Left:
|
|
return {
|
|
"x": barThickness + spacing + popupGap,
|
|
"y": relativeY,
|
|
"width": widgetWidth
|
|
};
|
|
case SettingsData.Position.Right:
|
|
return {
|
|
"x": (screen?.width || 0) - (barThickness + spacing + popupGap),
|
|
"y": relativeY,
|
|
"width": widgetWidth
|
|
};
|
|
case SettingsData.Position.Bottom:
|
|
return {
|
|
"x": relativeX,
|
|
"y": (screen?.height || 0) - (barThickness + spacing + bottomGap + popupGap),
|
|
"width": widgetWidth
|
|
};
|
|
default:
|
|
return {
|
|
"x": relativeX,
|
|
"y": barThickness + spacing + bottomGap + popupGap,
|
|
"width": widgetWidth
|
|
};
|
|
}
|
|
}
|
|
|
|
function getAdjacentBarInfo(screen, barPosition, barConfig) {
|
|
if (!screen || !barConfig) {
|
|
return {
|
|
"topBar": 0,
|
|
"bottomBar": 0,
|
|
"leftBar": 0,
|
|
"rightBar": 0
|
|
};
|
|
}
|
|
|
|
if (barConfig.autoHide) {
|
|
return {
|
|
"topBar": 0,
|
|
"bottomBar": 0,
|
|
"leftBar": 0,
|
|
"rightBar": 0
|
|
};
|
|
}
|
|
|
|
const enabledBars = getEnabledBarConfigs();
|
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
|
const position = barPosition !== undefined ? barPosition : (defaultBar?.position ?? SettingsData.Position.Top);
|
|
let topBar = 0;
|
|
let bottomBar = 0;
|
|
let leftBar = 0;
|
|
let rightBar = 0;
|
|
|
|
for (var i = 0; i < enabledBars.length; i++) {
|
|
const other = enabledBars[i];
|
|
if (other.id === barConfig.id)
|
|
continue;
|
|
if (other.autoHide)
|
|
continue;
|
|
const otherScreens = other.screenPreferences || ["all"];
|
|
const barScreens = barConfig.screenPreferences || ["all"];
|
|
const onSameScreen = otherScreens.includes("all") || barScreens.includes("all") || otherScreens.some(s => isScreenInPreferences(screen, [s]));
|
|
|
|
if (!onSameScreen)
|
|
continue;
|
|
const otherSpacing = other.spacing !== undefined ? other.spacing : (defaultBar?.spacing ?? 4);
|
|
const otherPadding = other.innerPadding !== undefined ? other.innerPadding : (defaultBar?.innerPadding ?? 4);
|
|
const otherThickness = Math.max(26 + otherPadding * 0.6, Theme.barHeight - 4 - (8 - otherPadding)) + otherSpacing;
|
|
|
|
const useAutoGaps = other.popupGapsAuto !== undefined ? other.popupGapsAuto : (defaultBar?.popupGapsAuto ?? true);
|
|
const manualGap = other.popupGapsManual !== undefined ? other.popupGapsManual : (defaultBar?.popupGapsManual ?? 4);
|
|
const popupGap = useAutoGaps ? Math.max(4, otherSpacing) : manualGap;
|
|
|
|
switch (other.position) {
|
|
case SettingsData.Position.Top:
|
|
topBar = Math.max(topBar, otherThickness + popupGap);
|
|
break;
|
|
case SettingsData.Position.Bottom:
|
|
bottomBar = Math.max(bottomBar, otherThickness + popupGap);
|
|
break;
|
|
case SettingsData.Position.Left:
|
|
leftBar = Math.max(leftBar, otherThickness + popupGap);
|
|
break;
|
|
case SettingsData.Position.Right:
|
|
rightBar = Math.max(rightBar, otherThickness + popupGap);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return {
|
|
"topBar": topBar,
|
|
"bottomBar": bottomBar,
|
|
"leftBar": leftBar,
|
|
"rightBar": rightBar
|
|
};
|
|
}
|
|
|
|
function getBarBounds(screen, barThickness, barPosition, barConfig) {
|
|
if (!screen) {
|
|
return {
|
|
"x": 0,
|
|
"y": 0,
|
|
"width": 0,
|
|
"height": 0,
|
|
"wingSize": 0
|
|
};
|
|
}
|
|
|
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
|
const wingRadius = (defaultBar?.gothCornerRadiusOverride ?? false) ? (defaultBar?.gothCornerRadiusValue ?? 12) : Theme.cornerRadius;
|
|
const wingSize = (defaultBar?.gothCornersEnabled ?? false) ? Math.max(0, wingRadius) : 0;
|
|
const screenWidth = screen.width;
|
|
const screenHeight = screen.height;
|
|
const position = barPosition !== undefined ? barPosition : (defaultBar?.position ?? SettingsData.Position.Top);
|
|
const bottomGap = barConfig ? (barConfig.bottomGap !== undefined ? barConfig.bottomGap : (defaultBar?.bottomGap ?? 0)) : (defaultBar?.bottomGap ?? 0);
|
|
|
|
let topOffset = 0;
|
|
let bottomOffset = 0;
|
|
let leftOffset = 0;
|
|
let rightOffset = 0;
|
|
|
|
if (barConfig) {
|
|
const enabledBars = getEnabledBarConfigs();
|
|
for (var i = 0; i < enabledBars.length; i++) {
|
|
const other = enabledBars[i];
|
|
if (other.id === barConfig.id)
|
|
continue;
|
|
const otherScreens = other.screenPreferences || ["all"];
|
|
const barScreens = barConfig.screenPreferences || ["all"];
|
|
const onSameScreen = otherScreens.includes("all") || barScreens.includes("all") || otherScreens.some(s => isScreenInPreferences(screen, [s]));
|
|
|
|
if (!onSameScreen)
|
|
continue;
|
|
const otherSpacing = other.spacing !== undefined ? other.spacing : (defaultBar?.spacing ?? 4);
|
|
const otherPadding = other.innerPadding !== undefined ? other.innerPadding : (defaultBar?.innerPadding ?? 4);
|
|
const otherThickness = Math.max(26 + otherPadding * 0.6, Theme.barHeight - 4 - (8 - otherPadding)) + otherSpacing + wingSize;
|
|
const otherBottomGap = other.bottomGap !== undefined ? other.bottomGap : (defaultBar?.bottomGap ?? 0);
|
|
|
|
switch (other.position) {
|
|
case SettingsData.Position.Top:
|
|
if (position === SettingsData.Position.Top && other.id < barConfig.id) {
|
|
topOffset += otherThickness; // Simple stacking for same pos
|
|
} else if (position === SettingsData.Position.Left || position === SettingsData.Position.Right) {
|
|
topOffset = Math.max(topOffset, otherThickness);
|
|
}
|
|
break;
|
|
case SettingsData.Position.Bottom:
|
|
if (position === SettingsData.Position.Bottom && other.id < barConfig.id) {
|
|
bottomOffset += (otherThickness + otherBottomGap);
|
|
} else if (position === SettingsData.Position.Left || position === SettingsData.Position.Right) {
|
|
bottomOffset = Math.max(bottomOffset, otherThickness + otherBottomGap);
|
|
}
|
|
break;
|
|
case SettingsData.Position.Left:
|
|
if (position === SettingsData.Position.Top || position === SettingsData.Position.Bottom) {
|
|
leftOffset = Math.max(leftOffset, otherThickness);
|
|
} else if (position === SettingsData.Position.Left && other.id < barConfig.id) {
|
|
leftOffset += otherThickness;
|
|
}
|
|
break;
|
|
case SettingsData.Position.Right:
|
|
if (position === SettingsData.Position.Top || position === SettingsData.Position.Bottom) {
|
|
rightOffset = Math.max(rightOffset, otherThickness);
|
|
} else if (position === SettingsData.Position.Right && other.id < barConfig.id) {
|
|
rightOffset += otherThickness;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (position) {
|
|
case SettingsData.Position.Top:
|
|
return {
|
|
"x": leftOffset,
|
|
"y": topOffset + bottomGap,
|
|
"width": screenWidth - leftOffset - rightOffset,
|
|
"height": barThickness + wingSize,
|
|
"wingSize": wingSize
|
|
};
|
|
case SettingsData.Position.Bottom:
|
|
return {
|
|
"x": leftOffset,
|
|
"y": screenHeight - barThickness - wingSize - bottomGap - bottomOffset,
|
|
"width": screenWidth - leftOffset - rightOffset,
|
|
"height": barThickness + wingSize,
|
|
"wingSize": wingSize
|
|
};
|
|
case SettingsData.Position.Left:
|
|
return {
|
|
"x": 0,
|
|
"y": topOffset,
|
|
"width": barThickness + wingSize,
|
|
"height": screenHeight - topOffset - bottomOffset,
|
|
"wingSize": wingSize
|
|
};
|
|
case SettingsData.Position.Right:
|
|
return {
|
|
"x": screenWidth - barThickness - wingSize,
|
|
"y": topOffset,
|
|
"width": barThickness + wingSize,
|
|
"height": screenHeight - topOffset - bottomOffset,
|
|
"wingSize": wingSize
|
|
};
|
|
}
|
|
|
|
return {
|
|
"x": 0,
|
|
"y": 0,
|
|
"width": 0,
|
|
"height": 0,
|
|
"wingSize": 0
|
|
};
|
|
}
|
|
|
|
function updateBarConfigs() {
|
|
barConfigsChanged();
|
|
saveSettings();
|
|
}
|
|
|
|
function getBarConfig(barId) {
|
|
return barConfigs.find(cfg => cfg.id === barId) || null;
|
|
}
|
|
|
|
function addBarConfig(config) {
|
|
const configs = JSON.parse(JSON.stringify(barConfigs));
|
|
configs.push(config);
|
|
barConfigs = configs;
|
|
updateBarConfigs();
|
|
}
|
|
|
|
function updateBarConfig(barId, updates) {
|
|
const configs = JSON.parse(JSON.stringify(barConfigs));
|
|
const index = configs.findIndex(cfg => cfg.id === barId);
|
|
if (index === -1)
|
|
return;
|
|
const positionChanged = updates.position !== undefined && configs[index].position !== updates.position;
|
|
|
|
Object.assign(configs[index], updates);
|
|
barConfigs = configs;
|
|
updateBarConfigs();
|
|
|
|
if (positionChanged) {
|
|
NotificationService.dismissAllPopups();
|
|
}
|
|
}
|
|
|
|
function checkBarCollisions(barId) {
|
|
const bar = getBarConfig(barId);
|
|
if (!bar || !bar.enabled)
|
|
return [];
|
|
|
|
const conflicts = [];
|
|
const enabledBars = getEnabledBarConfigs();
|
|
|
|
for (var i = 0; i < enabledBars.length; i++) {
|
|
const other = enabledBars[i];
|
|
if (other.id === barId)
|
|
continue;
|
|
const samePosition = bar.position === other.position;
|
|
if (!samePosition)
|
|
continue;
|
|
const barScreens = bar.screenPreferences || ["all"];
|
|
const otherScreens = other.screenPreferences || ["all"];
|
|
|
|
const hasAll = barScreens.includes("all") || otherScreens.includes("all");
|
|
if (hasAll) {
|
|
conflicts.push({
|
|
"barId": other.id,
|
|
"barName": other.name,
|
|
"reason": "Same position on all screens"
|
|
});
|
|
continue;
|
|
}
|
|
|
|
const overlapping = barScreens.some(screen => otherScreens.includes(screen));
|
|
if (overlapping) {
|
|
conflicts.push({
|
|
"barId": other.id,
|
|
"barName": other.name,
|
|
"reason": "Same position on overlapping screens"
|
|
});
|
|
}
|
|
}
|
|
|
|
return conflicts;
|
|
}
|
|
|
|
function deleteBarConfig(barId) {
|
|
if (barId === "default")
|
|
return;
|
|
const configs = barConfigs.filter(cfg => cfg.id !== barId);
|
|
barConfigs = configs;
|
|
updateBarConfigs();
|
|
}
|
|
|
|
function getEnabledBarConfigs() {
|
|
return barConfigs.filter(cfg => cfg.enabled);
|
|
}
|
|
|
|
function getScreensSortedByPosition() {
|
|
const screens = [];
|
|
for (var i = 0; i < Quickshell.screens.length; i++) {
|
|
screens.push(Quickshell.screens[i]);
|
|
}
|
|
screens.sort((a, b) => {
|
|
if (a.x !== b.x)
|
|
return a.x - b.x;
|
|
return a.y - b.y;
|
|
});
|
|
return screens;
|
|
}
|
|
|
|
function getScreenModelIndex(screen) {
|
|
if (!screen || !screen.model)
|
|
return -1;
|
|
const sorted = getScreensSortedByPosition();
|
|
let modelCount = 0;
|
|
let screenIndex = -1;
|
|
for (var i = 0; i < sorted.length; i++) {
|
|
if (sorted[i].model === screen.model) {
|
|
if (sorted[i].name === screen.name) {
|
|
screenIndex = modelCount;
|
|
}
|
|
modelCount++;
|
|
}
|
|
}
|
|
if (modelCount <= 1)
|
|
return -1;
|
|
return screenIndex;
|
|
}
|
|
|
|
function getScreenDisplayName(screen) {
|
|
if (!screen)
|
|
return "";
|
|
if (displayNameMode === "model" && screen.model) {
|
|
const modelIndex = getScreenModelIndex(screen);
|
|
if (modelIndex >= 0) {
|
|
return screen.model + "-" + modelIndex;
|
|
}
|
|
return screen.model;
|
|
}
|
|
return screen.name;
|
|
}
|
|
|
|
function isScreenInPreferences(screen, prefs) {
|
|
if (!screen)
|
|
return false;
|
|
|
|
const screenDisplayName = getScreenDisplayName(screen);
|
|
|
|
return prefs.some(pref => {
|
|
if (typeof pref === "string") {
|
|
if (pref === "all" || pref === screen.name)
|
|
return true;
|
|
if (displayNameMode === "model") {
|
|
return pref === screenDisplayName;
|
|
}
|
|
return pref === screen.model;
|
|
}
|
|
|
|
if (displayNameMode === "model") {
|
|
if (pref.model && screen.model) {
|
|
if (pref.modelIndex !== undefined) {
|
|
const screenModelIndex = getScreenModelIndex(screen);
|
|
return pref.model === screen.model && pref.modelIndex === screenModelIndex;
|
|
}
|
|
return pref.model === screen.model;
|
|
}
|
|
return false;
|
|
}
|
|
return pref.name === screen.name;
|
|
});
|
|
}
|
|
|
|
function getFilteredScreens(componentId) {
|
|
var prefs = screenPreferences && screenPreferences[componentId] || ["all"];
|
|
if (prefs.includes("all") || (typeof prefs[0] === "string" && prefs[0] === "all")) {
|
|
return Quickshell.screens;
|
|
}
|
|
var filtered = Quickshell.screens.filter(screen => isScreenInPreferences(screen, prefs));
|
|
if (filtered.length === 0 && showOnLastDisplay && showOnLastDisplay[componentId] && Quickshell.screens.length === 1) {
|
|
return Quickshell.screens;
|
|
}
|
|
return filtered;
|
|
}
|
|
|
|
function sendTestNotifications() {
|
|
NotificationService.dismissAllPopups();
|
|
sendTestNotification(0);
|
|
testNotifTimer1.start();
|
|
testNotifTimer2.start();
|
|
}
|
|
|
|
function sendTestNotification(index) {
|
|
const notifications = [["Notification Position Test", "DMS test notification 1 of 3 ~ Hi there!", "preferences-system"], ["Second Test", "DMS Notification 2 of 3 ~ Check it out!", "applications-graphics"], ["Third Test", "DMS notification 3 of 3 ~ Enjoy!", "face-smile"]];
|
|
|
|
if (index < 0 || index >= notifications.length) {
|
|
return;
|
|
}
|
|
|
|
const notif = notifications[index];
|
|
testNotificationProcess.command = ["notify-send", "-h", "int:transient:1", "-a", "DMS", "-i", notif[2], notif[0], notif[1]];
|
|
testNotificationProcess.running = true;
|
|
}
|
|
|
|
function setMatugenScheme(scheme) {
|
|
var normalized = scheme || "scheme-tonal-spot";
|
|
if (matugenScheme === normalized)
|
|
return;
|
|
set("matugenScheme", normalized);
|
|
if (typeof Theme !== "undefined") {
|
|
Theme.generateSystemThemesFromCurrentTheme();
|
|
}
|
|
}
|
|
|
|
function setRunUserMatugenTemplates(enabled) {
|
|
if (runUserMatugenTemplates === enabled)
|
|
return;
|
|
set("runUserMatugenTemplates", enabled);
|
|
if (typeof Theme !== "undefined") {
|
|
Theme.generateSystemThemesFromCurrentTheme();
|
|
}
|
|
}
|
|
|
|
function setMatugenTargetMonitor(monitorName) {
|
|
if (matugenTargetMonitor === monitorName)
|
|
return;
|
|
set("matugenTargetMonitor", monitorName);
|
|
if (typeof Theme !== "undefined") {
|
|
Theme.generateSystemThemesFromCurrentTheme();
|
|
}
|
|
}
|
|
|
|
function setCornerRadius(radius) {
|
|
set("cornerRadius", radius);
|
|
updateCompositorLayout();
|
|
}
|
|
|
|
function setWeatherLocation(displayName, coordinates) {
|
|
SessionData.setWeatherLocation(displayName, coordinates);
|
|
}
|
|
|
|
function setIconTheme(themeName) {
|
|
iconTheme = themeName;
|
|
updateGtkIconTheme();
|
|
updateQtIconTheme();
|
|
saveSettings();
|
|
if (typeof Theme !== "undefined" && Theme.currentTheme === Theme.dynamic)
|
|
Theme.generateSystemThemesFromCurrentTheme();
|
|
}
|
|
|
|
function setCursorTheme(themeName) {
|
|
const updated = JSON.parse(JSON.stringify(cursorSettings));
|
|
updated.theme = themeName;
|
|
cursorSettings = updated;
|
|
saveSettings();
|
|
updateCompositorCursor();
|
|
}
|
|
|
|
function setCursorSize(size) {
|
|
const updated = JSON.parse(JSON.stringify(cursorSettings));
|
|
updated.size = size;
|
|
cursorSettings = updated;
|
|
saveSettings();
|
|
updateCompositorCursor();
|
|
}
|
|
|
|
// This solution for xwayland cursor themes is from the xwls discussion:
|
|
// https://github.com/Supreeeme/xwayland-satellite/issues/104
|
|
// no idea if this matters on other compositors but we also set XCURSOR stuff in the launcher
|
|
function updateCompositorCursor() {
|
|
updateXResources();
|
|
if (typeof CompositorService === "undefined")
|
|
return;
|
|
if (CompositorService.isNiri && typeof NiriService !== "undefined") {
|
|
NiriService.generateNiriCursorConfig();
|
|
return;
|
|
}
|
|
if (CompositorService.isHyprland && typeof HyprlandService !== "undefined") {
|
|
HyprlandService.generateCursorConfig();
|
|
return;
|
|
}
|
|
if (CompositorService.isDwl && typeof DwlService !== "undefined") {
|
|
DwlService.generateCursorConfig();
|
|
return;
|
|
}
|
|
}
|
|
|
|
function updateXResources() {
|
|
const homeDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.HomeLocation));
|
|
const xresourcesPath = homeDir + "/.Xresources";
|
|
const themeName = cursorSettings.theme === "System Default" ? systemDefaultCursorTheme : cursorSettings.theme;
|
|
const size = cursorSettings.size || 24;
|
|
|
|
if (!themeName)
|
|
return;
|
|
|
|
const script = `
|
|
xresources_file="${xresourcesPath}"
|
|
temp_file="\${xresources_file}.tmp.$$"
|
|
theme_name="${themeName}"
|
|
cursor_size="${size}"
|
|
|
|
if [ -f "$xresources_file" ]; then
|
|
grep -v '^[[:space:]]*Xcursor\\.theme:' "$xresources_file" | grep -v '^[[:space:]]*Xcursor\\.size:' > "$temp_file" 2>/dev/null || true
|
|
else
|
|
touch "$temp_file"
|
|
fi
|
|
|
|
echo "Xcursor.theme: $theme_name" >> "$temp_file"
|
|
echo "Xcursor.size: $cursor_size" >> "$temp_file"
|
|
mv "$temp_file" "$xresources_file"
|
|
xrdb -merge "$xresources_file" 2>/dev/null || true
|
|
`;
|
|
|
|
Quickshell.execDetached(["sh", "-c", script]);
|
|
}
|
|
|
|
function getCursorEnvironment() {
|
|
const isSystemDefault = cursorSettings.theme === "System Default";
|
|
const isDefaultSize = !cursorSettings.size || cursorSettings.size === 24;
|
|
if (isSystemDefault && isDefaultSize)
|
|
return {};
|
|
|
|
const themeName = isSystemDefault ? "" : cursorSettings.theme;
|
|
const size = String(cursorSettings.size || 24);
|
|
const env = {};
|
|
|
|
if (!isDefaultSize) {
|
|
env["XCURSOR_SIZE"] = size;
|
|
env["HYPRCURSOR_SIZE"] = size;
|
|
}
|
|
if (themeName) {
|
|
env["XCURSOR_THEME"] = themeName;
|
|
env["HYPRCURSOR_THEME"] = themeName;
|
|
}
|
|
return env;
|
|
}
|
|
|
|
function setGtkThemingEnabled(enabled) {
|
|
set("gtkThemingEnabled", enabled);
|
|
if (enabled && typeof Theme !== "undefined") {
|
|
Theme.generateSystemThemesFromCurrentTheme();
|
|
}
|
|
}
|
|
|
|
function setQtThemingEnabled(enabled) {
|
|
set("qtThemingEnabled", enabled);
|
|
if (enabled && typeof Theme !== "undefined") {
|
|
Theme.generateSystemThemesFromCurrentTheme();
|
|
}
|
|
}
|
|
|
|
function setShowDock(enabled) {
|
|
showDock = enabled;
|
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
|
const barPos = defaultBar?.position ?? SettingsData.Position.Top;
|
|
if (enabled && dockPosition === barPos) {
|
|
if (barPos === SettingsData.Position.Top) {
|
|
setDockPosition(SettingsData.Position.Bottom);
|
|
return;
|
|
}
|
|
if (barPos === SettingsData.Position.Bottom) {
|
|
setDockPosition(SettingsData.Position.Top);
|
|
return;
|
|
}
|
|
if (barPos === SettingsData.Position.Left) {
|
|
setDockPosition(SettingsData.Position.Right);
|
|
return;
|
|
}
|
|
if (barPos === SettingsData.Position.Right) {
|
|
setDockPosition(SettingsData.Position.Left);
|
|
return;
|
|
}
|
|
}
|
|
saveSettings();
|
|
}
|
|
|
|
function setDockPosition(position) {
|
|
dockPosition = position;
|
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
|
const barPos = defaultBar?.position ?? SettingsData.Position.Top;
|
|
if (position === SettingsData.Position.Bottom && barPos === SettingsData.Position.Bottom && showDock) {
|
|
setDankBarPosition(SettingsData.Position.Top);
|
|
}
|
|
if (position === SettingsData.Position.Top && barPos === SettingsData.Position.Top && showDock) {
|
|
setDankBarPosition(SettingsData.Position.Bottom);
|
|
}
|
|
if (position === SettingsData.Position.Left && barPos === SettingsData.Position.Left && showDock) {
|
|
setDankBarPosition(SettingsData.Position.Right);
|
|
}
|
|
if (position === SettingsData.Position.Right && barPos === SettingsData.Position.Right && showDock) {
|
|
setDankBarPosition(SettingsData.Position.Left);
|
|
}
|
|
saveSettings();
|
|
Qt.callLater(() => forceDockLayoutRefresh());
|
|
}
|
|
|
|
function setDankBarSpacing(spacing) {
|
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
|
if (defaultBar) {
|
|
updateBarConfig(defaultBar.id, {
|
|
"spacing": spacing
|
|
});
|
|
}
|
|
updateCompositorLayout();
|
|
}
|
|
|
|
function setDankBarPosition(position) {
|
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
|
if (!defaultBar)
|
|
return;
|
|
if (position === SettingsData.Position.Bottom && dockPosition === SettingsData.Position.Bottom && showDock) {
|
|
setDockPosition(SettingsData.Position.Top);
|
|
return;
|
|
}
|
|
if (position === SettingsData.Position.Top && dockPosition === SettingsData.Position.Top && showDock) {
|
|
setDockPosition(SettingsData.Position.Bottom);
|
|
return;
|
|
}
|
|
if (position === SettingsData.Position.Left && dockPosition === SettingsData.Position.Left && showDock) {
|
|
setDockPosition(SettingsData.Position.Right);
|
|
return;
|
|
}
|
|
if (position === SettingsData.Position.Right && dockPosition === SettingsData.Position.Right && showDock) {
|
|
setDockPosition(SettingsData.Position.Left);
|
|
return;
|
|
}
|
|
updateBarConfig(defaultBar.id, {
|
|
"position": position
|
|
});
|
|
}
|
|
|
|
function setDankBarLeftWidgets(order) {
|
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
|
if (defaultBar) {
|
|
updateBarConfig(defaultBar.id, {
|
|
"leftWidgets": order
|
|
});
|
|
updateListModel(leftWidgetsModel, order);
|
|
}
|
|
}
|
|
|
|
function setDankBarCenterWidgets(order) {
|
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
|
if (defaultBar) {
|
|
updateBarConfig(defaultBar.id, {
|
|
"centerWidgets": order
|
|
});
|
|
updateListModel(centerWidgetsModel, order);
|
|
}
|
|
}
|
|
|
|
function setDankBarRightWidgets(order) {
|
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
|
if (defaultBar) {
|
|
updateBarConfig(defaultBar.id, {
|
|
"rightWidgets": order
|
|
});
|
|
updateListModel(rightWidgetsModel, order);
|
|
}
|
|
}
|
|
|
|
function resetDankBarWidgetsToDefault() {
|
|
var defaultLeft = ["launcherButton", "workspaceSwitcher", "focusedWindow"];
|
|
var defaultCenter = ["music", "clock", "weather"];
|
|
var defaultRight = ["systemTray", "clipboard", "notificationButton", "battery", "controlCenterButton"];
|
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
|
if (defaultBar) {
|
|
updateBarConfig(defaultBar.id, {
|
|
"leftWidgets": defaultLeft,
|
|
"centerWidgets": defaultCenter,
|
|
"rightWidgets": defaultRight
|
|
});
|
|
}
|
|
updateListModel(leftWidgetsModel, defaultLeft);
|
|
updateListModel(centerWidgetsModel, defaultCenter);
|
|
updateListModel(rightWidgetsModel, defaultRight);
|
|
showLauncherButton = true;
|
|
showWorkspaceSwitcher = true;
|
|
showFocusedWindow = true;
|
|
showWeather = true;
|
|
showMusic = true;
|
|
showClipboard = true;
|
|
showCpuUsage = true;
|
|
showMemUsage = true;
|
|
showCpuTemp = true;
|
|
showGpuTemp = true;
|
|
showSystemTray = true;
|
|
showClock = true;
|
|
showNotificationButton = true;
|
|
showBattery = true;
|
|
showControlCenterButton = true;
|
|
showCapsLockIndicator = true;
|
|
}
|
|
|
|
function setWorkspaceNameIcon(workspaceName, iconData) {
|
|
var iconMap = JSON.parse(JSON.stringify(workspaceNameIcons));
|
|
iconMap[workspaceName] = iconData;
|
|
workspaceNameIcons = iconMap;
|
|
saveSettings();
|
|
workspaceIconsUpdated();
|
|
}
|
|
|
|
function removeWorkspaceNameIcon(workspaceName) {
|
|
var iconMap = JSON.parse(JSON.stringify(workspaceNameIcons));
|
|
delete iconMap[workspaceName];
|
|
workspaceNameIcons = iconMap;
|
|
saveSettings();
|
|
workspaceIconsUpdated();
|
|
}
|
|
|
|
function getWorkspaceNameIcon(workspaceName) {
|
|
return workspaceNameIcons[workspaceName] || null;
|
|
}
|
|
|
|
function addAppIdSubstitution(pattern, replacement, type) {
|
|
var subs = JSON.parse(JSON.stringify(appIdSubstitutions));
|
|
subs.push({
|
|
pattern: pattern,
|
|
replacement: replacement,
|
|
type: type
|
|
});
|
|
appIdSubstitutions = subs;
|
|
saveSettings();
|
|
}
|
|
|
|
function updateAppIdSubstitution(index, pattern, replacement, type) {
|
|
var subs = JSON.parse(JSON.stringify(appIdSubstitutions));
|
|
if (index < 0 || index >= subs.length)
|
|
return;
|
|
subs[index] = {
|
|
pattern: pattern,
|
|
replacement: replacement,
|
|
type: type
|
|
};
|
|
appIdSubstitutions = subs;
|
|
saveSettings();
|
|
}
|
|
|
|
function removeAppIdSubstitution(index) {
|
|
var subs = JSON.parse(JSON.stringify(appIdSubstitutions));
|
|
if (index < 0 || index >= subs.length)
|
|
return;
|
|
subs.splice(index, 1);
|
|
appIdSubstitutions = subs;
|
|
saveSettings();
|
|
}
|
|
|
|
function getDefaultAppIdSubstitutions() {
|
|
return Spec.SPEC.appIdSubstitutions.def;
|
|
}
|
|
|
|
function resetAppIdSubstitutions() {
|
|
appIdSubstitutions = JSON.parse(JSON.stringify(Spec.SPEC.appIdSubstitutions.def));
|
|
saveSettings();
|
|
}
|
|
|
|
function getRegistryThemeVariant(themeId, defaultVariant) {
|
|
var stored = registryThemeVariants[themeId];
|
|
if (typeof stored === "string")
|
|
return stored || defaultVariant || "";
|
|
return defaultVariant || "";
|
|
}
|
|
|
|
function setRegistryThemeVariant(themeId, variantId) {
|
|
var variants = JSON.parse(JSON.stringify(registryThemeVariants));
|
|
variants[themeId] = variantId;
|
|
registryThemeVariants = variants;
|
|
saveSettings();
|
|
if (typeof Theme !== "undefined")
|
|
Theme.reloadCustomThemeVariant();
|
|
}
|
|
|
|
function getRegistryThemeMultiVariant(themeId, defaults) {
|
|
var stored = registryThemeVariants[themeId];
|
|
if (stored && typeof stored === "object")
|
|
return stored;
|
|
return defaults || {};
|
|
}
|
|
|
|
function setRegistryThemeMultiVariant(themeId, flavor, accent) {
|
|
var variants = JSON.parse(JSON.stringify(registryThemeVariants));
|
|
variants[themeId] = {
|
|
flavor: flavor,
|
|
accent: accent
|
|
};
|
|
registryThemeVariants = variants;
|
|
saveSettings();
|
|
if (typeof Theme !== "undefined")
|
|
Theme.reloadCustomThemeVariant();
|
|
}
|
|
|
|
function toggleDankBarVisible() {
|
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
|
if (defaultBar) {
|
|
updateBarConfig(defaultBar.id, {
|
|
"visible": !defaultBar.visible
|
|
});
|
|
}
|
|
}
|
|
|
|
function toggleShowDock() {
|
|
setShowDock(!showDock);
|
|
}
|
|
|
|
function getPluginSetting(pluginId, key, defaultValue) {
|
|
if (!pluginSettings[pluginId]) {
|
|
return defaultValue;
|
|
}
|
|
return pluginSettings[pluginId][key] !== undefined ? pluginSettings[pluginId][key] : defaultValue;
|
|
}
|
|
|
|
function setPluginSetting(pluginId, key, value) {
|
|
const updated = JSON.parse(JSON.stringify(pluginSettings));
|
|
if (!updated[pluginId]) {
|
|
updated[pluginId] = {};
|
|
}
|
|
updated[pluginId][key] = value;
|
|
pluginSettings = updated;
|
|
savePluginSettings();
|
|
}
|
|
|
|
function removePluginSettings(pluginId) {
|
|
if (pluginSettings[pluginId]) {
|
|
delete pluginSettings[pluginId];
|
|
savePluginSettings();
|
|
}
|
|
}
|
|
|
|
function getPluginSettingsForPlugin(pluginId) {
|
|
const settings = pluginSettings[pluginId];
|
|
return settings ? JSON.parse(JSON.stringify(settings)) : {};
|
|
}
|
|
|
|
function getNiriOutputSetting(outputId, key, defaultValue) {
|
|
if (!niriOutputSettings[outputId])
|
|
return defaultValue;
|
|
return niriOutputSettings[outputId][key] !== undefined ? niriOutputSettings[outputId][key] : defaultValue;
|
|
}
|
|
|
|
function setNiriOutputSetting(outputId, key, value) {
|
|
const updated = JSON.parse(JSON.stringify(niriOutputSettings));
|
|
if (!updated[outputId])
|
|
updated[outputId] = {};
|
|
updated[outputId][key] = value;
|
|
niriOutputSettings = updated;
|
|
saveSettings();
|
|
}
|
|
|
|
function getNiriOutputSettings(outputId) {
|
|
const settings = niriOutputSettings[outputId];
|
|
return settings ? JSON.parse(JSON.stringify(settings)) : {};
|
|
}
|
|
|
|
function setNiriOutputSettings(outputId, settings) {
|
|
const updated = JSON.parse(JSON.stringify(niriOutputSettings));
|
|
updated[outputId] = settings;
|
|
niriOutputSettings = updated;
|
|
saveSettings();
|
|
}
|
|
|
|
function removeNiriOutputSettings(outputId) {
|
|
if (!niriOutputSettings[outputId])
|
|
return;
|
|
const updated = JSON.parse(JSON.stringify(niriOutputSettings));
|
|
delete updated[outputId];
|
|
niriOutputSettings = updated;
|
|
saveSettings();
|
|
}
|
|
|
|
function getHyprlandOutputSetting(outputId, key, defaultValue) {
|
|
if (!hyprlandOutputSettings[outputId])
|
|
return defaultValue;
|
|
return hyprlandOutputSettings[outputId][key] !== undefined ? hyprlandOutputSettings[outputId][key] : defaultValue;
|
|
}
|
|
|
|
function setHyprlandOutputSetting(outputId, key, value) {
|
|
const updated = JSON.parse(JSON.stringify(hyprlandOutputSettings));
|
|
if (!updated[outputId])
|
|
updated[outputId] = {};
|
|
updated[outputId][key] = value;
|
|
hyprlandOutputSettings = updated;
|
|
saveSettings();
|
|
}
|
|
|
|
function removeHyprlandOutputSetting(outputId, key) {
|
|
if (!hyprlandOutputSettings[outputId] || !(key in hyprlandOutputSettings[outputId]))
|
|
return;
|
|
const updated = JSON.parse(JSON.stringify(hyprlandOutputSettings));
|
|
delete updated[outputId][key];
|
|
hyprlandOutputSettings = updated;
|
|
saveSettings();
|
|
}
|
|
|
|
function getHyprlandOutputSettings(outputId) {
|
|
const settings = hyprlandOutputSettings[outputId];
|
|
return settings ? JSON.parse(JSON.stringify(settings)) : {};
|
|
}
|
|
|
|
function setHyprlandOutputSettings(outputId, settings) {
|
|
const updated = JSON.parse(JSON.stringify(hyprlandOutputSettings));
|
|
updated[outputId] = settings;
|
|
hyprlandOutputSettings = updated;
|
|
saveSettings();
|
|
}
|
|
|
|
function removeHyprlandOutputSettings(outputId) {
|
|
if (!hyprlandOutputSettings[outputId])
|
|
return;
|
|
const updated = JSON.parse(JSON.stringify(hyprlandOutputSettings));
|
|
delete updated[outputId];
|
|
hyprlandOutputSettings = updated;
|
|
saveSettings();
|
|
}
|
|
|
|
ListModel {
|
|
id: leftWidgetsModel
|
|
}
|
|
|
|
ListModel {
|
|
id: centerWidgetsModel
|
|
}
|
|
|
|
ListModel {
|
|
id: rightWidgetsModel
|
|
}
|
|
|
|
property Process testNotificationProcess
|
|
|
|
testNotificationProcess: Process {
|
|
command: []
|
|
running: false
|
|
}
|
|
|
|
property Timer testNotifTimer1
|
|
|
|
testNotifTimer1: Timer {
|
|
interval: 400
|
|
repeat: false
|
|
onTriggered: sendTestNotification(1)
|
|
}
|
|
|
|
property Timer testNotifTimer2
|
|
|
|
testNotifTimer2: Timer {
|
|
interval: 800
|
|
repeat: false
|
|
onTriggered: sendTestNotification(2)
|
|
}
|
|
|
|
property alias settingsFile: settingsFile
|
|
|
|
FileView {
|
|
id: settingsFile
|
|
|
|
path: isGreeterMode ? "" : StandardPaths.writableLocation(StandardPaths.ConfigLocation) + "/DankMaterialShell/settings.json"
|
|
blockLoading: true
|
|
blockWrites: true
|
|
atomicWrites: true
|
|
watchChanges: !isGreeterMode
|
|
onLoaded: {
|
|
if (isGreeterMode)
|
|
return;
|
|
_loading = true;
|
|
_hasUnsavedChanges = false;
|
|
try {
|
|
const txt = settingsFile.text();
|
|
if (!txt || !txt.trim()) {
|
|
_parseError = true;
|
|
return;
|
|
}
|
|
const obj = JSON.parse(txt);
|
|
_parseError = false;
|
|
Store.parse(root, obj);
|
|
|
|
if (obj.weatherLocation !== undefined)
|
|
_legacyWeatherLocation = obj.weatherLocation;
|
|
if (obj.weatherCoordinates !== undefined)
|
|
_legacyWeatherCoordinates = obj.weatherCoordinates;
|
|
|
|
_loadedSettingsSnapshot = JSON.stringify(Store.toJson(root));
|
|
_hasLoaded = true;
|
|
applyStoredTheme();
|
|
applyStoredIconTheme();
|
|
updateCompositorCursor();
|
|
} catch (e) {
|
|
_parseError = true;
|
|
const msg = e.message;
|
|
console.error("SettingsData: Failed to reload settings.json - file will not be overwritten. Error:", msg);
|
|
Qt.callLater(() => ToastService.showError(I18n.tr("Failed to parse settings.json"), msg));
|
|
} finally {
|
|
_loading = false;
|
|
}
|
|
}
|
|
onLoadFailed: error => {
|
|
if (!isGreeterMode) {
|
|
applyStoredTheme();
|
|
}
|
|
}
|
|
onSaveFailed: error => {
|
|
root._isReadOnly = true;
|
|
root._hasUnsavedChanges = root._checkForUnsavedChanges();
|
|
}
|
|
}
|
|
|
|
FileView {
|
|
id: pluginSettingsFile
|
|
|
|
path: isGreeterMode ? "" : pluginSettingsPath
|
|
blockLoading: true
|
|
blockWrites: true
|
|
atomicWrites: true
|
|
watchChanges: !isGreeterMode
|
|
onLoaded: {
|
|
if (!isGreeterMode) {
|
|
parsePluginSettings(pluginSettingsFile.text());
|
|
}
|
|
}
|
|
onLoadFailed: error => {
|
|
if (!isGreeterMode) {
|
|
pluginSettings = {};
|
|
}
|
|
}
|
|
}
|
|
|
|
property bool pluginSettingsFileExists: false
|
|
|
|
Process {
|
|
id: settingsWritableCheckProcess
|
|
|
|
property string settingsPath: Paths.strip(settingsFile.path)
|
|
|
|
command: ["sh", "-c", "[ ! -f \"" + settingsPath + "\" ] || [ -w \"" + settingsPath + "\" ] && echo 'writable' || echo 'readonly'"]
|
|
running: false
|
|
|
|
stdout: StdioCollector {
|
|
onStreamFinished: {
|
|
const result = text.trim();
|
|
root._onWritableCheckComplete(result === "writable");
|
|
}
|
|
}
|
|
}
|
|
}
|