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
bbedward 1c7201fb04 settings: make settings and file browser normal windows
- add default floating rules for dankinstall
2025-11-23 01:23:06 -05:00

1300 lines
46 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 defaultFontFamily: "Inter Variable"
readonly property string defaultMonoFontFamily: "Fira Code"
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 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 dwlShowAllTags: false
property var workspaceNameIcons: ({})
property bool waveProgressEnabled: 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 clockDateFormat: ""
property string lockDateFormat: ""
property int mediaSize: 1
property string appLauncherViewMode: "list"
property string spotlightModalViewMode: "list"
property bool sortAppsAlphabetically: false
property int appLauncherGridColumns: 4
property bool spotlightCloseNiriOverview: 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()
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 int batteryMonitorTimeout: 0
property int batteryLockTimeout: 0
property int batterySuspendTimeout: 0
property int batterySuspendBehavior: SettingsData.SuspendBehavior.Suspend
property bool lockBeforeSuspend: false
property bool preventIdleForMedia: false
property bool loginctlLockIntegration: true
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 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 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 powerActionConfirm: true
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,
fontScale: 1.0,
autoHide: false,
autoHideDelay: 250,
openOnOverview: false,
visible: true,
popupGapsAuto: true,
popupGapsManual: 4
}]
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 getScreenDisplayName(screen) {
if (!screen) return ""
if (displayNameMode === "model" && screen.model) {
return screen.model
}
return screen.name
}
function isScreenInPreferences(screen, prefs) {
if (!screen) return false
return prefs.some(pref => {
if (typeof pref === "string") {
return pref === "all" || pref === screen.name || pref === screen.model
}
if (displayNameMode === "model") {
return pref.model && screen.model && pref.model === screen.model
}
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
IpcHandler {
function reveal(): string {
root.setShowDock(true)
return "DOCK_SHOW_SUCCESS"
}
function hide(): string {
root.setShowDock(false)
return "DOCK_HIDE_SUCCESS"
}
function toggle(): string {
root.toggleShowDock()
return root.showDock ? "DOCK_SHOW_SUCCESS" : "DOCK_HIDE_SUCCESS"
}
function status(): string {
return root.showDock ? "visible" : "hidden"
}
target: "dock"
}
}