1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -05:00
Files
DankMaterialShell/quickshell/Common/SettingsData.qml
2025-12-05 16:05:11 -05:00

1410 lines
49 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: 2
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 hasTriedDefaultSettings: false
property var pluginSettings: ({})
property alias dankBarLeftWidgetsModel: leftWidgetsModel
property alias dankBarCenterWidgetsModel: centerWidgetsModel
property alias dankBarRightWidgetsModel: rightWidgetsModel
property string currentThemeName: "blue"
property string customThemeFile: ""
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 bool use24HourClock: true
property bool showSeconds: false
property bool useFahrenheit: false
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 controlCenterShowVpnIcon: true
property bool controlCenterShowBrightnessIcon: false
property bool controlCenterShowMicIcon: false
property bool controlCenterShowBatteryIcon: false
property bool controlCenterShowPrinterIcon: false
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 showWorkspacePadding: false
property bool workspaceScrolling: false
property bool showWorkspaceApps: false
property int maxWorkspaceIcons: 3
property bool workspacesPerMonitor: true
property bool showOccupiedWorkspacesOnly: false
property bool dwlShowAllTags: false
property var workspaceNameIcons: ({})
property bool waveProgressEnabled: true
property bool scrollTitleEnabled: true
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 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 bool sortAppsAlphabetically: false
property int appLauncherGridColumns: 4
property bool spotlightCloseNiriOverview: true
property bool niriOverviewOverlayEnabled: true
property string weatherLocation: "New York, NY"
property string weatherCoordinates: "40.7128,-74.0060"
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 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 bool lockBeforeSuspend: false
property bool preventIdleForMedia: false
property bool loginctlLockIntegration: true
property bool fadeToLockEnabled: false
property int fadeToLockGracePeriod: 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 showDock: false
property bool dockAutoHide: 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 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 enableFprint: false
property int maxFprintTries: 3
property bool fprintdAvailable: false
property string lockScreenActiveMonitor: "all"
property string lockScreenInactiveColor: "#000000"
property bool hideBrightnessSlider: false
property int notificationTimeoutLow: 5000
property int notificationTimeoutNormal: 5000
property int notificationTimeoutCritical: 0
property int notificationPopupPosition: SettingsData.Position.Top
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 updaterUseCustomCommand: false
property string updaterCustomCommand: ""
property string updaterTerminalAdditionalParams: ""
property string displayNameMode: "system"
property var screenPreferences: ({})
property var showOnLastDisplay: ({})
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,
openOnOverview: false,
visible: true,
popupGapsAuto: true,
popupGapsManual: 4,
maximizeDetection: true
}
]
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.switchTheme(currentThemeName, false, false);
} else {
Qt.callLater(function () {
if (typeof Theme !== "undefined") {
Theme.switchTheme(currentThemeName, false, false);
}
});
}
}
function regenSystemThemes() {
if (typeof Theme !== "undefined") {
Theme.generateSystemThemesFromCurrentTheme();
}
}
function updateNiriLayout() {
if (typeof NiriService !== "undefined" && typeof CompositorService !== "undefined" && CompositorService.isNiri) {
NiriService.generateNiriLayoutConfig();
}
}
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,
updateNiriLayout: updateNiriLayout,
applyStoredIconTheme: applyStoredIconTheme,
updateBarConfigs: updateBarConfigs
})
function set(key, value) {
Spec.set(root, key, value, saveSettings, _hooks);
}
function loadSettings() {
_loading = true;
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) {
settingsFile.setText(JSON.stringify(migrated, null, 2));
obj = migrated;
}
}
Store.parse(root, obj);
applyStoredTheme();
applyStoredIconTheme();
Processes.detectIcons();
Processes.detectQtTools();
} catch (e) {
console.warn("SettingsData: Failed to load settings:", e.message);
applyStoredTheme();
applyStoredIconTheme();
} finally {
_loading = false;
}
loadPluginSettings();
}
function loadPluginSettings() {
_pluginSettingsLoading = true;
parsePluginSettings(pluginSettingsFile.text());
_pluginSettingsLoading = false;
}
function parsePluginSettings(content) {
_pluginSettingsLoading = true;
try {
if (content && content.trim()) {
pluginSettings = JSON.parse(content);
} else {
pluginSettings = {};
}
} catch (e) {
console.warn("SettingsData: Failed to parse plugin settings:", e.message);
pluginSettings = {};
} finally {
_pluginSettingsLoading = false;
}
}
function saveSettings() {
if (_loading)
return;
settingsFile.setText(JSON.stringify(Store.toJson(root), null, 2));
}
function savePluginSettings() {
if (_pluginSettingsLoading)
return;
pluginSettingsFile.setText(JSON.stringify(pluginSettings, null, 2));
}
function detectAvailableIconThemes() {
Processes.detectIcons();
}
function getEffectiveTimeFormat() {
if (use24HourClock) {
return showSeconds ? "hh:mm:ss" : "hh:mm";
} else {
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(globalPos, screen, barThickness, widgetWidth, barSpacing, barPosition, barConfig) {
const screenX = screen ? screen.x : 0;
const screenY = screen ? screen.y : 0;
const relativeX = globalPos.x - screenX;
const relativeY = globalPos.y - screenY;
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 bottomGap = barConfig ? (barConfig.bottomGap !== undefined ? barConfig.bottomGap : (defaultBar?.bottomGap ?? 0)) : (defaultBar?.bottomGap ?? 0);
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 (let 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 (let 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.clearAllPopups();
}
}
function checkBarCollisions(barId) {
const bar = getBarConfig(barId);
if (!bar || !bar.enabled)
return [];
const conflicts = [];
const enabledBars = getEnabledBarConfigs();
for (let 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 (let 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 (let 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.clearAllPopups();
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);
NiriService.generateNiriLayoutConfig();
}
function setWeatherLocation(displayName, coordinates) {
weatherLocation = displayName;
weatherCoordinates = coordinates;
saveSettings();
}
function setIconTheme(themeName) {
iconTheme = themeName;
updateGtkIconTheme();
updateQtIconTheme();
saveSettings();
if (typeof Theme !== "undefined" && Theme.currentTheme === Theme.dynamic)
Theme.generateSystemThemesFromCurrentTheme();
}
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
});
}
if (typeof NiriService !== "undefined" && CompositorService.isNiri) {
NiriService.generateNiriLayoutConfig();
}
}
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 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)) : {};
}
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) {
try {
const txt = settingsFile.text();
const obj = (txt && txt.trim()) ? JSON.parse(txt) : null;
Store.parse(root, obj);
} catch (e) {
console.warn("SettingsData: Failed to reload settings:", e.message);
}
hasTriedDefaultSettings = false;
}
}
onLoadFailed: error => {
if (!isGreeterMode && !hasTriedDefaultSettings) {
hasTriedDefaultSettings = true;
Processes.checkDefaultSettings();
} else if (!isGreeterMode) {
applyStoredTheme();
}
}
}
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
}