1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-06 05:25:41 -05:00

Compare commits

...

48 Commits

Author SHA1 Message Date
github-actions[bot]
b27f362b44 Update VERSION to v0.3.0 (from DMS) 2025-10-30 18:11:33 +00:00
bbedward
325e3bc19b fix duplicated qt6ct sections 2025-10-30 13:53:47 -04:00
bbedward
9215985335 ci: try and fix changelog filter 2025-10-30 13:45:39 -04:00
Mattias
293179daa6 fix: Enable "Show on Last Display" for Notepad Slideout and System Tray (#590) 2025-10-30 13:38:57 -04:00
github-actions[bot]
4fe79dbe85 i18n: update source strings from codebase 2025-10-30 17:14:49 +00:00
bbedward
55d738e917 about: fix links 2025-10-30 13:13:29 -04:00
github-actions[bot]
986b07f4a9 i18n: update translations 2025-10-30 17:04:57 +00:00
github-actions[bot]
450c2e91ed i18n: update source strings from codebase 2025-10-30 17:04:47 +00:00
bbedward
4d06333624 about page: update for mango and sway 2025-10-30 13:04:10 -04:00
Tulip Blossom
fbe4122404 fix(dms-greeter,rpm): greeter user is supplied by sysusers and having manual user on the spec breaks it (#585)
* fix(dms-greeter,rpm): greeter user is supplied by sysusers and having manual user on the spec breaks it

This makes it so this RPM works fine on fedora 43, the greeter user
should be created and configured by systemd sysusers anyways

Signed-off-by: Tulip Blossom <tulilirockz@outlook.com>

* fix(dms-greeter): use systemd-tmpfiles to set up greeter directories

Signed-off-by: Tulip Blossom <tulilirockz@outlook.com>

* fix(rpm, dms-greeter): require systemd for tmpfiles macro

Signed-off-by: Tulip Blossom <tulilirockz@outlook.com>

---------

Signed-off-by: Tulip Blossom <tulilirockz@outlook.com>
2025-10-30 10:54:37 -04:00
bbedward
baf9b5e6f3 dwl: dont show empty tags 2025-10-30 10:50:35 -04:00
bbedward
c88fc20701 vpn: fix persistence
fixes #587
2025-10-30 09:33:50 -04:00
github-actions[bot]
b1078d6c73 i18n: update translations 2025-10-30 13:22:02 +00:00
bbedward
5033d10246 dwl: remove wlr-randr dependency 2025-10-30 09:21:20 -04:00
bbedward
986993a890 Merge branch 'master' of github.com:bbedward/DankMaterialShell 2025-10-29 23:53:43 -04:00
bbedward
19b13a1e81 dwl: tag changes 2025-10-29 23:45:40 -04:00
bbedward
76637fab33 dwl: hide empty tags by default 2025-10-29 23:45:40 -04:00
github-actions[bot]
0a79d9a187 i18n: update translations 2025-10-30 03:39:57 +00:00
github-actions[bot]
36b3b3c7ae i18n: update source strings from codebase 2025-10-30 03:39:47 +00:00
bbedward
8caeca0c08 dwl: tag changes 2025-10-29 23:39:12 -04:00
bbedward
1c323f54ee dwl: hide empty tags by default 2025-10-29 23:07:15 -04:00
bbedward
7ed0b752a8 hyprland: some targeted improvements 2025-10-29 17:23:38 -04:00
bbedward
0569906f7c network: strip down legacy network service 2025-10-29 17:07:19 -04:00
bbedward
2a7cf187ad keyboard layout: remove polling on hyprland 2025-10-29 16:58:07 -04:00
github-actions[bot]
cc5b98a5d2 i18n: update source strings from codebase 2025-10-29 19:42:59 +00:00
bbedward
1478c92f49 matugen: fix wallpaperengine color generation 2025-10-29 15:41:10 -04:00
github-actions[bot]
e1785a1738 i18n: update translations 2025-10-29 19:10:16 +00:00
github-actions[bot]
44ebd2918c i18n: update source strings from codebase 2025-10-29 19:10:05 +00:00
bbedward
c87fa0de5e sway: add support for sway 2025-10-29 15:08:11 -04:00
bbedward
7b26692c8e dwl: support display scales 2025-10-29 13:45:22 -04:00
bbedward
b294e391e7 settings: don't overflow screen dimensions 2025-10-29 13:34:11 -04:00
bbedward
85f8e362e6 pam: try to avoid racey unlock states 2025-10-29 12:59:35 -04:00
github-actions[bot]
d68a6a1056 i18n: update translations 2025-10-29 16:42:00 +00:00
github-actions[bot]
3dae9c0639 i18n: update source strings from codebase 2025-10-29 16:41:52 +00:00
bbedward
aede6b064a dwl: add dwl/MangoWC support
- Requires dms api v12
- Tags/Workspace support
- MangoWC launcher logo
- dpms off/on support
- logout support
2025-10-29 12:39:31 -04:00
bbedward
76b168020c dash: fix IPC positioning 2025-10-29 10:42:39 -04:00
bbedward
5e36b1454a wallpaper: transition blurred wallpaper layer fixes #579 2025-10-29 09:26:03 -04:00
github-actions[bot]
bd35fbac4d i18n: update source strings from codebase 2025-10-29 13:19:15 +00:00
bbedward
e081ec19cc dankbar: cooldown timer on scrolling workspaces 2025-10-29 09:18:46 -04:00
github-actions[bot]
d870d8bad6 i18n: update translations 2025-10-29 13:12:54 +00:00
bbedward
20fd13c836 dankbar: scroll wheels to cycle apps and workspaces 2025-10-29 09:12:10 -04:00
github-actions[bot]
59f98b151d i18n: update source strings from codebase 2025-10-28 20:40:45 +00:00
bbedward
4ac1990c12 systray: fix icon fallback 2025-10-28 16:40:13 -04:00
bbedward
0a5105cc62 niri: simple blur rule 2025-10-28 12:49:49 -04:00
bbedward
a9f8b835ee notepad: use a mask over content area 2025-10-28 12:08:18 -04:00
bbedward
0109bd5bda Merge branch 'master' of github.com:bbedward/DankMaterialShell 2025-10-28 11:37:50 -04:00
bbedward
01dad64c6d notepad: fix mousearea width
fixes #569
2025-10-28 11:37:24 -04:00
bbedward
ee38f57f6d filebrowser: use NF icons 2025-10-28 11:36:18 -04:00
52 changed files with 1678 additions and 719 deletions

View File

@@ -24,6 +24,8 @@ assignees: ""
- [ ] niri
- [ ] Hyprland
- [ ] dwl (MangoWC)
- [ ] sway
- [ ] Other (specify)
## Distribution

View File

@@ -21,6 +21,8 @@ Is this feature specific to one compositor?
- [ ] All compositors
- [ ] niri
- [ ] Hyprland
- [ ] dwl (MangoWC)
- [ ] sway
## Proposed Solution

View File

@@ -10,6 +10,8 @@ assignees: ""
- [ ] niri
- [ ] Hyprland
- [ ] dwl (MangoWC)
- [ ] sway
- [ ] other
## Distribution

View File

@@ -49,9 +49,9 @@ jobs:
set -e
PREVIOUS_TAG=$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || echo "")
if [ -z "$PREVIOUS_TAG" ]; then
CHANGELOG=$(git log --oneline --pretty=format:"- %s (%h)" --author='^(?!github-actions\[bot\])' | head -50)
CHANGELOG=$(git log --oneline --pretty=format:"%an|%s (%h)" | grep -v "^github-actions\[bot\]|" | sed 's/^[^|]*|/- /' | head -50)
else
CHANGELOG=$(git log --oneline --pretty=format:"- %s (%h)" --author='^(?!github-actions\[bot\])' "${PREVIOUS_TAG}..${TAG}")
CHANGELOG=$(git log --oneline --pretty=format:"%an|%s (%h)" "${PREVIOUS_TAG}..${TAG}" | grep -v "^github-actions\[bot\]|" | sed 's/^[^|]*|/- /')
fi
cat > RELEASE_BODY.md << 'EOF'

View File

@@ -10,17 +10,18 @@ Singleton {
property int defaultDebounceMs: 50
property int defaultTimeoutMs: 10000
property var _procDebouncers: ({}) // id -> { timer, command, callback, waitMs }
property var _procDebouncers: ({})
function runCommand(id, command, callback, debounceMs, timeoutMs) {
const wait = (typeof debounceMs === "number" && debounceMs >= 0) ? debounceMs : defaultDebounceMs
const timeout = (typeof timeoutMs === "number" && timeoutMs > 0) ? timeoutMs : defaultTimeoutMs
let procId = id ? id : Math.random()
const isRandomId = !id
if (!_procDebouncers[procId]) {
const t = Qt.createQmlObject('import QtQuick; Timer { repeat: false }', root)
t.triggered.connect(function() { _launchProc(procId) })
_procDebouncers[procId] = { timer: t, command: command, callback: callback, waitMs: wait, timeoutMs: timeout }
t.triggered.connect(function() { _launchProc(procId, isRandomId) })
_procDebouncers[procId] = { timer: t, command: command, callback: callback, waitMs: wait, timeoutMs: timeout, isRandomId: isRandomId }
} else {
_procDebouncers[procId].command = command
_procDebouncers[procId].callback = callback
@@ -33,7 +34,7 @@ Singleton {
entry.timer.restart()
}
function _launchProc(id) {
function _launchProc(id, isRandomId) {
const entry = _procDebouncers[id]
if (!entry) return
@@ -92,6 +93,15 @@ Singleton {
}
try { proc.destroy() } catch (_) {}
try { timeoutTimer.destroy() } catch (_) {}
if (isRandomId || entry.isRandomId) {
Qt.callLater(function() {
if (_procDebouncers[id]) {
try { _procDebouncers[id].timer.destroy() } catch (_) {}
delete _procDebouncers[id]
}
})
}
}
proc.running = true

View File

@@ -131,6 +131,7 @@ Singleton {
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
@@ -433,6 +434,7 @@ Singleton {
maxWorkspaceIcons = settings.maxWorkspaceIcons !== undefined ? settings.maxWorkspaceIcons : 3
workspaceNameIcons = settings.workspaceNameIcons !== undefined ? settings.workspaceNameIcons : ({})
workspacesPerMonitor = settings.workspacesPerMonitor !== undefined ? settings.workspacesPerMonitor : true
dwlShowAllTags = settings.dwlShowAllTags !== undefined ? settings.dwlShowAllTags : false
waveProgressEnabled = settings.waveProgressEnabled !== undefined ? settings.waveProgressEnabled : true
clockCompactMode = settings.clockCompactMode !== undefined ? settings.clockCompactMode : false
focusedWindowCompactMode = settings.focusedWindowCompactMode !== undefined ? settings.focusedWindowCompactMode : false
@@ -470,6 +472,7 @@ Singleton {
spotlightModalViewMode = settings.spotlightModalViewMode !== undefined ? settings.spotlightModalViewMode : "list"
sortAppsAlphabetically = settings.sortAppsAlphabetically !== undefined ? settings.sortAppsAlphabetically : false
networkPreference = settings.networkPreference !== undefined ? settings.networkPreference : "auto"
vpnLastConnected = settings.vpnLastConnected !== undefined ? settings.vpnLastConnected : ""
iconTheme = settings.iconTheme !== undefined ? settings.iconTheme : "System Default"
if (settings.useOSLogo !== undefined) {
launcherLogoMode = settings.useOSLogo ? "os" : "apps"
@@ -647,6 +650,7 @@ Singleton {
"showWorkspaceApps": showWorkspaceApps,
"maxWorkspaceIcons": maxWorkspaceIcons,
"workspacesPerMonitor": workspacesPerMonitor,
"dwlShowAllTags": dwlShowAllTags,
"workspaceNameIcons": workspaceNameIcons,
"waveProgressEnabled": waveProgressEnabled,
"clockCompactMode": clockCompactMode,
@@ -665,6 +669,7 @@ Singleton {
"spotlightModalViewMode": spotlightModalViewMode,
"sortAppsAlphabetically": sortAppsAlphabetically,
"networkPreference": networkPreference,
"vpnLastConnected": vpnLastConnected,
"iconTheme": iconTheme,
"launcherLogoMode": launcherLogoMode,
"launcherLogoCustomPath": launcherLogoCustomPath,
@@ -774,7 +779,7 @@ Singleton {
}
function cleanupUnusedKeys() {
const validKeys = ["currentThemeName", "customThemeFile", "matugenScheme", "runUserMatugenTemplates", "matugenTargetMonitor", "dankBarTransparency", "dankBarWidgetTransparency", "popupTransparency", "dockTransparency", "use24HourClock", "showSeconds", "useFahrenheit", "nightModeEnabled", "weatherLocation", "weatherCoordinates", "useAutoLocation", "weatherEnabled", "showLauncherButton", "showWorkspaceSwitcher", "showFocusedWindow", "showWeather", "showMusic", "showClipboard", "showCpuUsage", "showMemUsage", "showCpuTemp", "showGpuTemp", "selectedGpuIndex", "enabledGpuPciIds", "showSystemTray", "showClock", "showNotificationButton", "showBattery", "showControlCenterButton", "controlCenterShowNetworkIcon", "controlCenterShowBluetoothIcon", "controlCenterShowAudioIcon", "controlCenterWidgets", "showWorkspaceIndex", "workspaceScrolling", "showWorkspacePadding", "showWorkspaceApps", "maxWorkspaceIcons", "workspacesPerMonitor", "workspaceNameIcons", "waveProgressEnabled", "clockCompactMode", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "runningAppsCurrentWorkspace", "runningAppsGroupByApp", "clockDateFormat", "lockDateFormat", "mediaSize", "dankBarLeftWidgets", "dankBarCenterWidgets", "dankBarRightWidgets", "appLauncherViewMode", "spotlightModalViewMode", "sortAppsAlphabetically", "networkPreference", "iconTheme", "launcherLogoMode", "launcherLogoCustomPath", "launcherLogoColorOverride", "launcherLogoColorInvertOnMode", "launcherLogoBrightness", "launcherLogoContrast", "launcherLogoSizeOffset", "fontFamily", "monoFontFamily", "fontWeight", "fontScale", "dankBarFontScale", "notepadUseMonospace", "notepadFontFamily", "notepadFontSize", "notepadShowLineNumbers", "notepadTransparencyOverride", "notepadLastCustomTransparency", "soundsEnabled", "useSystemSoundTheme", "soundNewNotification", "soundVolumeChanged", "soundPluggedIn", "gtkThemingEnabled", "qtThemingEnabled", "syncModeWithPortal", "showDock", "dockAutoHide", "dockGroupByApp", "dockOpenOnOverview", "dockPosition", "dockSpacing", "dockBottomGap", "dockIconSize", "dockIndicatorStyle", "cornerRadius", "notificationOverlayEnabled", "dankBarAutoHide", "dankBarOpenOnOverview", "dankBarVisible", "dankBarSpacing", "dankBarBottomGap", "dankBarInnerPadding", "dankBarSquareCorners", "dankBarNoBackground", "dankBarGothCornersEnabled", "dankBarBorderEnabled", "dankBarBorderColor", "dankBarBorderOpacity", "dankBarBorderThickness", "popupGapsAuto", "popupGapsManual", "dankBarPosition", "lockScreenShowPowerActions", "enableFprint", "maxFprintTries", "hideBrightnessSlider", "widgetBackgroundColor", "surfaceBase", "wallpaperFillMode", "blurredWallpaperLayer", "blurWallpaperOnOverview", "notificationTimeoutLow", "notificationTimeoutNormal", "notificationTimeoutCritical", "notificationPopupPosition", "osdAlwaysShowValue", "powerActionConfirm", "customPowerActionLock", "customPowerActionLogout", "customPowerActionSuspend", "customPowerActionHibernate", "customPowerActionReboot", "customPowerActionPowerOff", "updaterUseCustomCommand", "updaterCustomCommand", "updaterTerminalAdditionalParams", "screenPreferences", "showOnLastDisplay", "animationSpeed", "customAnimationDuration", "acMonitorTimeout", "acLockTimeout", "acSuspendTimeout", "acHibernateTimeout", "batteryMonitorTimeout", "batteryLockTimeout", "batterySuspendTimeout", "batteryHibernateTimeout", "lockBeforeSuspend", "loginctlLockIntegration", "launchPrefix", "brightnessDevicePins", "configVersion"]
const validKeys = ["currentThemeName", "customThemeFile", "matugenScheme", "runUserMatugenTemplates", "matugenTargetMonitor", "dankBarTransparency", "dankBarWidgetTransparency", "popupTransparency", "dockTransparency", "use24HourClock", "showSeconds", "useFahrenheit", "nightModeEnabled", "weatherLocation", "weatherCoordinates", "useAutoLocation", "weatherEnabled", "showLauncherButton", "showWorkspaceSwitcher", "showFocusedWindow", "showWeather", "showMusic", "showClipboard", "showCpuUsage", "showMemUsage", "showCpuTemp", "showGpuTemp", "selectedGpuIndex", "enabledGpuPciIds", "showSystemTray", "showClock", "showNotificationButton", "showBattery", "showControlCenterButton", "controlCenterShowNetworkIcon", "controlCenterShowBluetoothIcon", "controlCenterShowAudioIcon", "controlCenterWidgets", "showWorkspaceIndex", "workspaceScrolling", "showWorkspacePadding", "showWorkspaceApps", "maxWorkspaceIcons", "workspacesPerMonitor", "dwlShowAllTags", "workspaceNameIcons", "waveProgressEnabled", "clockCompactMode", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "runningAppsCurrentWorkspace", "runningAppsGroupByApp", "clockDateFormat", "lockDateFormat", "mediaSize", "dankBarLeftWidgets", "dankBarCenterWidgets", "dankBarRightWidgets", "appLauncherViewMode", "spotlightModalViewMode", "sortAppsAlphabetically", "networkPreference", "vpnLastConnected", "iconTheme", "launcherLogoMode", "launcherLogoCustomPath", "launcherLogoColorOverride", "launcherLogoColorInvertOnMode", "launcherLogoBrightness", "launcherLogoContrast", "launcherLogoSizeOffset", "fontFamily", "monoFontFamily", "fontWeight", "fontScale", "dankBarFontScale", "notepadUseMonospace", "notepadFontFamily", "notepadFontSize", "notepadShowLineNumbers", "notepadTransparencyOverride", "notepadLastCustomTransparency", "soundsEnabled", "useSystemSoundTheme", "soundNewNotification", "soundVolumeChanged", "soundPluggedIn", "gtkThemingEnabled", "qtThemingEnabled", "syncModeWithPortal", "showDock", "dockAutoHide", "dockGroupByApp", "dockOpenOnOverview", "dockPosition", "dockSpacing", "dockBottomGap", "dockIconSize", "dockIndicatorStyle", "cornerRadius", "notificationOverlayEnabled", "dankBarAutoHide", "dankBarOpenOnOverview", "dankBarVisible", "dankBarSpacing", "dankBarBottomGap", "dankBarInnerPadding", "dankBarSquareCorners", "dankBarNoBackground", "dankBarGothCornersEnabled", "dankBarBorderEnabled", "dankBarBorderColor", "dankBarBorderOpacity", "dankBarBorderThickness", "popupGapsAuto", "popupGapsManual", "dankBarPosition", "lockScreenShowPowerActions", "enableFprint", "maxFprintTries", "hideBrightnessSlider", "widgetBackgroundColor", "surfaceBase", "wallpaperFillMode", "blurredWallpaperLayer", "blurWallpaperOnOverview", "notificationTimeoutLow", "notificationTimeoutNormal", "notificationTimeoutCritical", "notificationPopupPosition", "osdAlwaysShowValue", "powerActionConfirm", "customPowerActionLock", "customPowerActionLogout", "customPowerActionSuspend", "customPowerActionHibernate", "customPowerActionReboot", "customPowerActionPowerOff", "updaterUseCustomCommand", "updaterCustomCommand", "updaterTerminalAdditionalParams", "screenPreferences", "showOnLastDisplay", "animationSpeed", "customAnimationDuration", "acMonitorTimeout", "acLockTimeout", "acSuspendTimeout", "acHibernateTimeout", "batteryMonitorTimeout", "batteryLockTimeout", "batterySuspendTimeout", "batteryHibernateTimeout", "lockBeforeSuspend", "loginctlLockIntegration", "launchPrefix", "brightnessDevicePins", "configVersion"]
try {
const content = settingsFile.text()
@@ -965,7 +970,7 @@ Singleton {
}
return {
"x": relativeX,
"y": barThickness + dankBarSpacing + dankBarBottomGap + Theme.popupDistance,
"y": barThickness + dankBarSpacing + Theme.popupDistance,
"width": widgetWidth
}
}
@@ -1270,6 +1275,11 @@ Singleton {
saveSettings()
}
function setDwlShowAllTags(enabled) {
dwlShowAllTags = enabled
saveSettings()
}
function setWorkspaceNameIcon(workspaceName, iconData) {
var iconMap = JSON.parse(JSON.stringify(workspaceNameIcons))
iconMap[workspaceName] = iconData

View File

@@ -111,12 +111,12 @@ Singleton {
console.info("Theme: Matugen now available, regenerating colors for dynamic theme")
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode)
const iconTheme = (typeof SettingsData !== "undefined" && SettingsData.iconTheme) ? SettingsData.iconTheme : "System Default"
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
const selectedMatugenType = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
if (rawWallpaperPath.startsWith("#")) {
setDesiredTheme("hex", rawWallpaperPath, isLight, iconTheme, selectedMatugenType)
const effectivePath = rawWallpaperPath.startsWith("we:") ? (stateDir + "/we_screenshots/" + rawWallpaperPath.substring(3) + ".jpg") : rawWallpaperPath
if (effectivePath.startsWith("#")) {
setDesiredTheme("hex", effectivePath, isLight, iconTheme, selectedMatugenType)
} else {
setDesiredTheme("image", rawWallpaperPath, isLight, iconTheme, selectedMatugenType)
setDesiredTheme("image", effectivePath, isLight, iconTheme, selectedMatugenType)
}
return
}
@@ -126,12 +126,12 @@ Singleton {
if (currentTheme === dynamic) {
if (rawWallpaperPath) {
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
const selectedMatugenType = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
if (rawWallpaperPath.startsWith("#")) {
setDesiredTheme("hex", rawWallpaperPath, isLight, iconTheme, selectedMatugenType)
const effectivePath = rawWallpaperPath.startsWith("we:") ? (stateDir + "/we_screenshots/" + rawWallpaperPath.substring(3) + ".jpg") : rawWallpaperPath
if (effectivePath.startsWith("#")) {
setDesiredTheme("hex", effectivePath, isLight, iconTheme, selectedMatugenType)
} else {
setDesiredTheme("image", rawWallpaperPath, isLight, iconTheme, selectedMatugenType)
setDesiredTheme("image", effectivePath, isLight, iconTheme, selectedMatugenType)
}
}
} else {
@@ -148,7 +148,6 @@ Singleton {
}
if (primaryColor) {
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
setDesiredTheme("hex", primaryColor, isLight, iconTheme, matugenType)
}
}
@@ -812,11 +811,12 @@ Singleton {
const desiredPath = stateDir + "/matugen.desired.json"
Quickshell.execDetached(["sh", "-c", `mkdir -p '${stateDir}' && cat > '${desiredPath}' << 'EOF'\n${json}\nEOF`])
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
workerRunning = true
const syncModeWithPortal = (typeof SettingsData !== "undefined" && SettingsData.syncModeWithPortal) ? "true" : "false"
if (rawWallpaperPath.startsWith("we:")) {
console.log("Theme: Starting matugen worker (WE wallpaper)")
systemThemeGenerator.command = ["sh", "-c", `sleep 1 && ${shellDir}/scripts/matugen-worker.sh '${stateDir}' '${shellDir}' '${configDir}' '${syncModeWithPortal}' --run`]
console.log("Theme: Starting matugen worker (WE wallpaper, waiting for screenshot)")
systemThemeGenerator.command = ["sh", "-c", `sleep 3 && ${shellDir}/scripts/matugen-worker.sh '${stateDir}' '${shellDir}' '${configDir}' '${syncModeWithPortal}' --run`]
} else {
console.log("Theme: Starting matugen worker")
systemThemeGenerator.command = [shellDir + "/scripts/matugen-worker.sh", stateDir, shellDir, configDir, syncModeWithPortal, "--run"]
@@ -837,10 +837,11 @@ Singleton {
return
}
const selectedMatugenType = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
if (rawWallpaperPath.startsWith("#")) {
setDesiredTheme("hex", rawWallpaperPath, isLight, iconTheme, selectedMatugenType)
const effectivePath = rawWallpaperPath.startsWith("we:") ? (stateDir + "/we_screenshots/" + rawWallpaperPath.substring(3) + ".jpg") : rawWallpaperPath
if (effectivePath.startsWith("#")) {
setDesiredTheme("hex", effectivePath, isLight, iconTheme, selectedMatugenType)
} else {
setDesiredTheme("image", rawWallpaperPath, isLight, iconTheme, selectedMatugenType)
setDesiredTheme("image", effectivePath, isLight, iconTheme, selectedMatugenType)
}
} else {
let primaryColor

View File

@@ -54,7 +54,7 @@ Item {
Loader {
id: blurredWallpaperBackgroundLoader
active: SettingsData.blurredWallpaperLayer
active: SettingsData.blurredWallpaperLayer && CompositorService.isNiri
asynchronous: false
sourceComponent: BlurredWallpaperBackground {}

View File

@@ -135,24 +135,22 @@ Item {
}
function toggle(tab: string): string {
root.dankDashPopoutLoader.active = true
if (root.dankDashPopoutLoader.item) {
if (root.dankDashPopoutLoader.item.dashVisible) {
root.dankDashPopoutLoader.item.dashVisible = false
} else {
if (root.dankBarLoader.item && root.dankBarLoader.item.triggerWallpaperBrowserOnFocusedScreen()) {
if (root.dankDashPopoutLoader.item) {
switch (tab.toLowerCase()) {
case "media":
root.dankDashPopoutLoader.item.currentTabIndex = 1
break
case "wallpaper":
root.dankDashPopoutLoader.item.currentTabIndex = 2
break
case "weather":
root.dankDashPopoutLoader.item.currentTabIndex = SettingsData.weatherEnabled ? 2 : 0
root.dankDashPopoutLoader.item.currentTabIndex = SettingsData.weatherEnabled ? 3 : 0
break
default:
root.dankDashPopoutLoader.item.currentTabIndex = 0
break
}
root.dankDashPopoutLoader.item.setTriggerPosition(Screen.width / 2, Theme.barHeight + Theme.spacingS, 100, "center", Screen)
root.dankDashPopoutLoader.item.dashVisible = true
}
return "DASH_TOGGLE_SUCCESS"
}

View File

@@ -17,17 +17,7 @@ PanelWindow {
property real height: 300
readonly property real screenWidth: screen ? screen.width : 1920
readonly property real screenHeight: screen ? screen.height : 1080
readonly property real dpr: {
if (CompositorService.isNiri && screen) {
const niriScale = NiriService.displayScales[screen.name]
if (niriScale !== undefined) return niriScale
}
if (CompositorService.isHyprland && screen) {
const hyprlandMonitor = Hyprland.monitors.values.find(m => m.name === screen.name)
if (hyprlandMonitor?.scale !== undefined) return hyprlandMonitor.scale
}
return (screen?.devicePixelRatio) || 1
}
readonly property real dpr: CompositorService.getScreenScale(screen)
property bool showBackground: true
property real backgroundOpacity: 0.5
property string positioning: "center"

View File

@@ -20,78 +20,68 @@ StyledRect {
signal itemClicked(int index, string path, string name, bool isDir)
signal itemSelected(int index, string path, string name, bool isDir)
function getFileExtension(fileName) {
const parts = fileName.split('.')
if (parts.length > 1) {
return parts[parts.length - 1].toLowerCase()
}
return ""
}
function determineFileType(fileName) {
const ext = getFileExtension(fileName)
const imageExts = ["png", "jpg", "jpeg", "gif", "bmp", "webp", "svg", "ico"]
if (imageExts.includes(ext)) {
return "image"
}
const videoExts = ["mp4", "mkv", "avi", "mov", "webm", "flv", "wmv", "m4v"]
if (videoExts.includes(ext)) {
return "video"
}
const audioExts = ["mp3", "wav", "flac", "ogg", "m4a", "aac", "wma"]
if (audioExts.includes(ext)) {
return "audio"
}
const codeExts = ["js", "ts", "jsx", "tsx", "py", "go", "rs", "c", "cpp", "h", "java", "kt", "swift", "rb", "php", "html", "css", "scss", "json", "xml", "yaml", "yml", "toml", "sh", "bash", "zsh", "fish", "qml", "vue", "svelte"]
if (codeExts.includes(ext)) {
return "code"
}
const docExts = ["txt", "md", "pdf", "doc", "docx", "odt", "rtf"]
if (docExts.includes(ext)) {
return "document"
}
const archiveExts = ["zip", "tar", "gz", "bz2", "xz", "7z", "rar"]
if (archiveExts.includes(ext)) {
return "archive"
}
if (!ext || fileName.indexOf('.') === -1) {
return "binary"
}
return "file"
}
function isImageFile(fileName) {
if (!fileName) {
return false
}
const ext = fileName.toLowerCase().split('.').pop()
return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'].includes(ext)
return determineFileType(fileName) === "image"
}
function getFileIcon(fileName, isDir) {
if (isDir) {
return "folder"
function getIconForFile(fileName) {
const lowerName = fileName.toLowerCase()
if (lowerName.startsWith("dockerfile")) {
return "docker"
}
if (!fileName) {
return "description"
}
const ext = fileName.toLowerCase().split('.').pop()
const iconMap = {
"mp3": 'music_note',
"wav": 'music_note',
"flac": 'music_note',
"ogg": 'music_note',
"aac": 'music_note',
"mp4": 'movie',
"mkv": 'movie',
"avi": 'movie',
"mov": 'movie',
"webm": 'movie',
"flv": 'movie',
"wmv": 'movie',
"jpg": 'image',
"jpeg": 'image',
"png": 'image',
"gif": 'image',
"bmp": 'image',
"webp": 'image',
"svg": 'image',
"pdf": 'picture_as_pdf',
"zip": 'folder_zip',
"rar": 'folder_zip',
"7z": 'folder_zip',
"tar": 'folder_zip',
"gz": 'folder_zip',
"bz2": 'folder_zip',
"xz": 'folder_zip',
"txt": 'description',
"md": 'description',
"doc": 'description',
"docx": 'description',
"odt": 'description',
"rtf": 'description',
"sh": 'terminal',
"py": 'code',
"js": 'code',
"ts": 'code',
"cpp": 'code',
"c": 'code',
"h": 'code',
"java": 'code',
"go": 'code',
"rs": 'code',
"php": 'code',
"rb": 'code',
"qml": 'code',
"html": 'code',
"css": 'code',
"json": 'data_object',
"xml": 'data_object',
"yaml": 'data_object',
"yml": 'data_object',
"toml": 'data_object'
}
return iconMap[ext] || 'description'
const ext = fileName.split('.').pop()
return ext || ""
}
width: weMode ? 245 : iconSizes[iconSizeIndex] + 16
@@ -179,9 +169,9 @@ StyledRect {
}
}
DankIcon {
DankNFIcon {
anchors.centerIn: parent
name: getFileIcon(delegateRoot.fileName, delegateRoot.fileIsDir)
name: delegateRoot.fileIsDir ? "folder" : getIconForFile(delegateRoot.fileName)
size: iconSizes[iconSizeIndex] * 0.45
color: delegateRoot.fileIsDir ? Theme.primary : Theme.surfaceText
visible: (!delegateRoot.fileIsDir && !isImageFile(delegateRoot.fileName)) || (delegateRoot.fileIsDir && !weMode)

View File

@@ -19,78 +19,68 @@ StyledRect {
signal itemClicked(int index, string path, string name, bool isDir)
signal itemSelected(int index, string path, string name, bool isDir)
function getFileExtension(fileName) {
const parts = fileName.split('.')
if (parts.length > 1) {
return parts[parts.length - 1].toLowerCase()
}
return ""
}
function determineFileType(fileName) {
const ext = getFileExtension(fileName)
const imageExts = ["png", "jpg", "jpeg", "gif", "bmp", "webp", "svg", "ico"]
if (imageExts.includes(ext)) {
return "image"
}
const videoExts = ["mp4", "mkv", "avi", "mov", "webm", "flv", "wmv", "m4v"]
if (videoExts.includes(ext)) {
return "video"
}
const audioExts = ["mp3", "wav", "flac", "ogg", "m4a", "aac", "wma"]
if (audioExts.includes(ext)) {
return "audio"
}
const codeExts = ["js", "ts", "jsx", "tsx", "py", "go", "rs", "c", "cpp", "h", "java", "kt", "swift", "rb", "php", "html", "css", "scss", "json", "xml", "yaml", "yml", "toml", "sh", "bash", "zsh", "fish", "qml", "vue", "svelte"]
if (codeExts.includes(ext)) {
return "code"
}
const docExts = ["txt", "md", "pdf", "doc", "docx", "odt", "rtf"]
if (docExts.includes(ext)) {
return "document"
}
const archiveExts = ["zip", "tar", "gz", "bz2", "xz", "7z", "rar"]
if (archiveExts.includes(ext)) {
return "archive"
}
if (!ext || fileName.indexOf('.') === -1) {
return "binary"
}
return "file"
}
function isImageFile(fileName) {
if (!fileName) {
return false
}
const ext = fileName.toLowerCase().split('.').pop()
return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'].includes(ext)
return determineFileType(fileName) === "image"
}
function getFileIcon(fileName, isDir) {
if (isDir) {
return "folder"
function getIconForFile(fileName) {
const lowerName = fileName.toLowerCase()
if (lowerName.startsWith("dockerfile")) {
return "docker"
}
if (!fileName) {
return "description"
}
const ext = fileName.toLowerCase().split('.').pop()
const iconMap = {
"mp3": 'music_note',
"wav": 'music_note',
"flac": 'music_note',
"ogg": 'music_note',
"aac": 'music_note',
"mp4": 'movie',
"mkv": 'movie',
"avi": 'movie',
"mov": 'movie',
"webm": 'movie',
"flv": 'movie',
"wmv": 'movie',
"jpg": 'image',
"jpeg": 'image',
"png": 'image',
"gif": 'image',
"bmp": 'image',
"webp": 'image',
"svg": 'image',
"pdf": 'picture_as_pdf',
"zip": 'folder_zip',
"rar": 'folder_zip',
"7z": 'folder_zip',
"tar": 'folder_zip',
"gz": 'folder_zip',
"bz2": 'folder_zip',
"xz": 'folder_zip',
"txt": 'description',
"md": 'description',
"doc": 'description',
"docx": 'description',
"odt": 'description',
"rtf": 'description',
"sh": 'terminal',
"py": 'code',
"js": 'code',
"ts": 'code',
"cpp": 'code',
"c": 'code',
"h": 'code',
"java": 'code',
"go": 'code',
"rs": 'code',
"php": 'code',
"rb": 'code',
"qml": 'code',
"html": 'code',
"css": 'code',
"json": 'data_object',
"xml": 'data_object',
"yaml": 'data_object',
"yml": 'data_object',
"toml": 'data_object'
}
return iconMap[ext] || 'description'
const ext = fileName.split('.').pop()
return ext || ""
}
function formatFileSize(size) {
@@ -168,9 +158,9 @@ StyledRect {
}
}
DankIcon {
DankNFIcon {
anchors.centerIn: parent
name: getFileIcon(listDelegateRoot.fileName, listDelegateRoot.fileIsDir)
name: listDelegateRoot.fileIsDir ? "folder" : getIconForFile(listDelegateRoot.fileName)
size: Theme.iconSize - 2
color: listDelegateRoot.fileIsDir ? Theme.primary : Theme.surfaceText
visible: listDelegateRoot.fileIsDir || !isImageFile(listDelegateRoot.fileName)

View File

@@ -34,8 +34,8 @@ DankModal {
}
objectName: "settingsModal"
width: 800
height: 800
width: Math.min(800, screenWidth * 0.9)
height: Math.min(800, screenHeight * 0.85)
visible: false
onBackgroundClicked: () => {
return hide();

View File

@@ -54,17 +54,25 @@ Variants {
}
function getFillMode(modeName) {
switch(modeName) {
case "Stretch": return Image.Stretch
case "Fit":
case "PreserveAspectFit": return Image.PreserveAspectFit
case "Fill":
case "PreserveAspectCrop": return Image.PreserveAspectCrop
case "Tile": return Image.Tile
case "TileVertically": return Image.TileVertically
case "TileHorizontally": return Image.TileHorizontally
case "Pad": return Image.Pad
default: return Image.PreserveAspectCrop
switch (modeName) {
case "Stretch":
return Image.Stretch
case "Fit":
case "PreserveAspectFit":
return Image.PreserveAspectFit
case "Fill":
case "PreserveAspectCrop":
return Image.PreserveAspectCrop
case "Tile":
return Image.Tile
case "TileVertically":
return Image.TileVertically
case "TileHorizontally":
return Image.TileHorizontally
case "Pad":
return Image.Pad
default:
return Image.PreserveAspectCrop
}
}
@@ -76,33 +84,77 @@ Variants {
Component.onCompleted: {
if (source) {
const formattedSource = source.startsWith("file://") ? source : "file://" + source
wallpaperImage.source = formattedSource
setWallpaperImmediate(formattedSource)
}
isInitialized = true
}
Component.onDestruction: {
weProc.stop()
}
property bool isInitialized: false
property real transitionProgress: 0
readonly property bool transitioning: transitionAnimation.running
onSourceChanged: {
const isWE = source.startsWith("we:")
const isColor = source.startsWith("#")
if (isWE) {
wallpaperImage.source = ""
setWallpaperImmediate("")
weProc.start(source.substring(3))
} else {
weProc.stop()
if (!source) {
wallpaperImage.source = ""
setWallpaperImmediate("")
} else if (isColor) {
wallpaperImage.source = ""
setWallpaperImmediate("")
} else {
wallpaperImage.source = source.startsWith("file://") ? source : "file://" + source
if (!isInitialized || !currentWallpaper.source) {
setWallpaperImmediate(source.startsWith("file://") ? source : "file://" + source)
isInitialized = true
} else {
changeWallpaper(source.startsWith("file://") ? source : "file://" + source)
}
}
}
}
function setWallpaperImmediate(newSource) {
transitionAnimation.stop()
root.transitionProgress = 0.0
currentWallpaper.source = newSource
nextWallpaper.source = ""
currentWallpaper.opacity = 1
nextWallpaper.opacity = 0
}
function changeWallpaper(newPath) {
if (newPath === currentWallpaper.source)
return
if (!newPath || newPath.startsWith("#"))
return
if (root.transitioning) {
transitionAnimation.stop()
root.transitionProgress = 0
currentWallpaper.source = nextWallpaper.source
nextWallpaper.source = ""
}
if (!currentWallpaper.source) {
setWallpaperImmediate(newPath)
return
}
nextWallpaper.source = newPath
if (nextWallpaper.status === Image.Ready) {
transitionAnimation.start()
}
}
Loader {
anchors.fill: parent
active: !root.source || root.isColorSource
@@ -114,21 +166,78 @@ Variants {
}
Image {
id: wallpaperImage
id: currentWallpaper
anchors.fill: parent
visible: false
opacity: 1
asynchronous: true
smooth: true
cache: true
fillMode: root.getFillMode(SettingsData.wallpaperFillMode)
}
MultiEffect {
Image {
id: nextWallpaper
anchors.fill: parent
source: wallpaperImage
blurEnabled: true
blur: 0.8
blurMax: 75
visible: false
opacity: 0
asynchronous: true
smooth: true
cache: true
fillMode: root.getFillMode(SettingsData.wallpaperFillMode)
onStatusChanged: {
if (status !== Image.Ready)
return
if (!root.transitioning) {
transitionAnimation.start()
}
}
}
Item {
id: blurredLayer
anchors.fill: parent
MultiEffect {
anchors.fill: parent
source: currentWallpaper
blurEnabled: true
blur: 0.8
blurMax: 75
opacity: 1 - root.transitionProgress
}
MultiEffect {
anchors.fill: parent
source: nextWallpaper
blurEnabled: true
blur: 0.8
blurMax: 75
opacity: root.transitionProgress
}
}
NumberAnimation {
id: transitionAnimation
target: root
property: "transitionProgress"
from: 0.0
to: 1.0
duration: 1000
easing.type: Easing.InOutCubic
onFinished: {
Qt.callLater(() => {
if (nextWallpaper.source && nextWallpaper.status === Image.Ready && !nextWallpaper.source.toString().startsWith("#")) {
currentWallpaper.source = nextWallpaper.source
}
nextWallpaper.source = ""
currentWallpaper.opacity = 1
nextWallpaper.opacity = 0
root.transitionProgress = 0.0
})
}
}
}
}

View File

@@ -18,17 +18,7 @@ Item {
anchors.topMargin: -(SettingsData.dankBarGothCornersEnabled && !axis.isVertical && axis.edge === "bottom" ? barWindow._wingR : 0)
anchors.bottomMargin: -(SettingsData.dankBarGothCornersEnabled && !axis.isVertical && axis.edge === "top" ? barWindow._wingR : 0)
readonly property real dpr: {
if (CompositorService.isNiri && barWindow.screen) {
const niriScale = NiriService.displayScales[barWindow.screen.name]
if (niriScale !== undefined) return niriScale
}
if (CompositorService.isHyprland && barWindow.screen) {
const hyprlandMonitor = Hyprland.monitors.values.find(m => m.name === barWindow.screen.name)
if (hyprlandMonitor?.scale !== undefined) return hyprlandMonitor.scale
}
return barWindow.screen?.devicePixelRatio || 1
}
readonly property real dpr: CompositorService.getScreenScale(barWindow.screen)
function requestRepaint() {
debounceTimer.restart()

View File

@@ -4,6 +4,7 @@ import QtQuick.Effects
import QtQuick.Shapes
import Quickshell
import Quickshell.Hyprland
import Quickshell.I3
import Quickshell.Io
import Quickshell.Services.Mpris
import Quickshell.Services.Notifications
@@ -31,6 +32,9 @@ Item {
focusedScreenName = Hyprland.focusedWorkspace.monitor.name
} else if (CompositorService.isNiri && NiriService.currentOutput) {
focusedScreenName = NiriService.currentOutput
} else if (CompositorService.isSway) {
const focusedWs = I3.workspaces?.values?.find(ws => ws.focused === true)
focusedScreenName = focusedWs?.monitor?.name || ""
}
if (!focusedScreenName && barVariants.instances.length > 0) {
@@ -55,6 +59,9 @@ Item {
focusedScreenName = Hyprland.focusedWorkspace.monitor.name
} else if (CompositorService.isNiri && NiriService.currentOutput) {
focusedScreenName = NiriService.currentOutput
} else if (CompositorService.isSway) {
const focusedWs = I3.workspaces?.values?.find(ws => ws.focused === true)
focusedScreenName = focusedWs?.monitor?.name || ""
}
if (!focusedScreenName && barVariants.instances.length > 0) {
@@ -110,9 +117,9 @@ Item {
return
}
if (clockButtonRef && dankDashPopoutLoader.item.setTriggerPosition) {
const globalPos = clockButtonRef.mapToGlobal(0, 0)
const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, clockButtonRef.width)
if (clockButtonRef && clockButtonRef.visualContent && dankDashPopoutLoader.item.setTriggerPosition) {
const globalPos = clockButtonRef.visualContent.mapToGlobal(0, 0)
const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, clockButtonRef.visualWidth)
const section = clockButtonRef.section || "center"
dankDashPopoutLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, section, barWindow.screen)
} else {
@@ -173,19 +180,7 @@ Item {
readonly property color _surfaceContainer: Theme.surfaceContainer
readonly property real _backgroundAlpha: topBarCore?.backgroundTransparency ?? SettingsData.dankBarTransparency
readonly property color _bgColor: Theme.withAlpha(_surfaceContainer, _backgroundAlpha)
readonly property real _dpr: {
if (CompositorService.isNiri && barWindow.screen) {
const niriScale = NiriService.displayScales[barWindow.screen.name]
if (niriScale !== undefined)
return niriScale
}
if (CompositorService.isHyprland && barWindow.screen) {
const hyprlandMonitor = Hyprland.monitors.values.find(m => m.name === barWindow.screen.name)
if (hyprlandMonitor?.scale !== undefined)
return hyprlandMonitor.scale
}
return (barWindow.screen?.devicePixelRatio) || 1
}
readonly property real _dpr: CompositorService.getScreenScale(barWindow.screen)
property string screenName: modelData.name
readonly property int notificationCount: NotificationService.notifications.length
@@ -551,6 +546,163 @@ Item {
componentMapRevision++
}
readonly property var sortedToplevels: {
return CompositorService.filterCurrentWorkspace(CompositorService.sortedToplevels, barWindow.screenName);
}
function getRealWorkspaces() {
if (CompositorService.isNiri) {
if (!barWindow.screenName || !SettingsData.workspacesPerMonitor) {
return NiriService.getCurrentOutputWorkspaceNumbers()
}
const workspaces = NiriService.allWorkspaces.filter(ws => ws.output === barWindow.screenName).map(ws => ws.idx + 1)
return workspaces.length > 0 ? workspaces : [1, 2]
} else if (CompositorService.isHyprland) {
const workspaces = Hyprland.workspaces?.values || []
if (!barWindow.screenName || !SettingsData.workspacesPerMonitor) {
const sorted = workspaces.slice().sort((a, b) => a.id - b.id)
const filtered = sorted.filter(ws => ws.id > -1)
return filtered.length > 0 ? filtered : [{"id": 1, "name": "1"}]
}
const monitorWorkspaces = workspaces.filter(ws => {
return ws.lastIpcObject && ws.lastIpcObject.monitor === barWindow.screenName && ws.id > -1
})
if (monitorWorkspaces.length === 0) {
return [{"id": 1, "name": "1"}]
}
return monitorWorkspaces.sort((a, b) => a.id - b.id)
} else if (CompositorService.isDwl) {
if (!DwlService.dwlAvailable) {
return [0]
}
if (SettingsData.dwlShowAllTags) {
return Array.from({length: DwlService.tagCount}, (_, i) => i)
}
return DwlService.getVisibleTags(barWindow.screenName)
} else if (CompositorService.isSway) {
const workspaces = I3.workspaces?.values || []
if (workspaces.length === 0) return [{"num": 1}]
if (!barWindow.screenName || !SettingsData.workspacesPerMonitor) {
return workspaces.slice().sort((a, b) => a.num - b.num)
}
const monitorWorkspaces = workspaces.filter(ws => ws.monitor?.name === barWindow.screenName)
return monitorWorkspaces.length > 0 ? monitorWorkspaces.sort((a, b) => a.num - b.num) : [{"num": 1}]
}
return [1]
}
function getCurrentWorkspace() {
if (CompositorService.isNiri) {
if (!barWindow.screenName || !SettingsData.workspacesPerMonitor) {
return NiriService.getCurrentWorkspaceNumber()
}
const activeWs = NiriService.allWorkspaces.find(ws => ws.output === barWindow.screenName && ws.is_active)
return activeWs ? activeWs.idx + 1 : 1
} else if (CompositorService.isHyprland) {
const monitors = Hyprland.monitors?.values || []
const currentMonitor = monitors.find(monitor => monitor.name === barWindow.screenName)
return currentMonitor?.activeWorkspace?.id ?? 1
} else if (CompositorService.isDwl) {
if (!DwlService.dwlAvailable) return 0
const outputState = DwlService.getOutputState(barWindow.screenName)
if (!outputState || !outputState.tags) return 0
const activeTags = DwlService.getActiveTags(barWindow.screenName)
return activeTags.length > 0 ? activeTags[0] : 0
} else if (CompositorService.isSway) {
if (!barWindow.screenName || !SettingsData.workspacesPerMonitor) {
const focusedWs = I3.workspaces?.values?.find(ws => ws.focused === true)
return focusedWs ? focusedWs.num : 1
}
const focusedWs = I3.workspaces?.values?.find(ws => ws.monitor?.name === barWindow.screenName && ws.focused === true)
return focusedWs ? focusedWs.num : 1
}
return 1
}
function switchWorkspace(direction) {
const realWorkspaces = getRealWorkspaces()
if (realWorkspaces.length < 2) {
return
}
if (CompositorService.isNiri) {
const currentWs = getCurrentWorkspace()
const currentIndex = realWorkspaces.findIndex(ws => ws === currentWs)
const validIndex = currentIndex === -1 ? 0 : currentIndex
const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0)
if (nextIndex !== validIndex) {
NiriService.switchToWorkspace(realWorkspaces[nextIndex] - 1)
}
} else if (CompositorService.isHyprland) {
const currentWs = getCurrentWorkspace()
const currentIndex = realWorkspaces.findIndex(ws => ws.id === currentWs)
const validIndex = currentIndex === -1 ? 0 : currentIndex
const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0)
if (nextIndex !== validIndex) {
Hyprland.dispatch(`workspace ${realWorkspaces[nextIndex].id}`)
}
} else if (CompositorService.isDwl) {
const currentTag = getCurrentWorkspace()
const currentIndex = realWorkspaces.findIndex(tag => tag === currentTag)
const validIndex = currentIndex === -1 ? 0 : currentIndex
const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0)
if (nextIndex !== validIndex) {
DwlService.switchToTag(barWindow.screenName, realWorkspaces[nextIndex])
}
} else if (CompositorService.isSway) {
const currentWs = getCurrentWorkspace()
const currentIndex = realWorkspaces.findIndex(ws => ws.num === currentWs)
const validIndex = currentIndex === -1 ? 0 : currentIndex
const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0)
if (nextIndex !== validIndex) {
try { I3.dispatch(`workspace number ${realWorkspaces[nextIndex].num}`) } catch(_){}
}
}
}
function switchApp(deltaY) {
const windows = sortedToplevels;
if (windows.length < 2) {
return;
}
let currentIndex = -1;
for (let i = 0; i < windows.length; i++) {
if (windows[i].activated) {
currentIndex = i;
break;
}
}
let nextIndex;
if (deltaY < 0) {
if (currentIndex === -1) {
nextIndex = 0;
} else {
nextIndex = currentIndex + 1;
}
} else {
if (currentIndex === -1) {
nextIndex = windows.length - 1;
} else {
nextIndex = currentIndex - 1;
}
}
const nextWindow = windows[nextIndex];
if (nextWindow) {
nextWindow.activate();
}
}
readonly property int availableWidth: width
readonly property int launcherButtonWidth: 40
readonly property int workspaceSwitcherWidth: 120
@@ -683,6 +835,60 @@ Item {
id: stackContainer
anchors.fill: parent
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
propagateComposedEvents: true
z: -1
property real scrollAccumulator: 0
property real touchpadThreshold: 500
property bool actionInProgress: false
Timer {
id: cooldownTimer
interval: 100
onTriggered: parent.actionInProgress = false
}
onWheel: wheel => {
if (actionInProgress) {
wheel.accepted = false
return
}
const deltaY = wheel.angleDelta.y
const deltaX = wheel.angleDelta.x
if (CompositorService.isNiri && Math.abs(deltaX) > Math.abs(deltaY)) {
topBarContent.switchApp(deltaX)
wheel.accepted = false
return
}
const isMouseWheel = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0
const direction = deltaY < 0 ? 1 : -1
if (isMouseWheel) {
topBarContent.switchWorkspace(direction)
actionInProgress = true
cooldownTimer.restart()
} else {
scrollAccumulator += deltaY
if (Math.abs(scrollAccumulator) >= touchpadThreshold) {
const touchDirection = scrollAccumulator < 0 ? 1 : -1
topBarContent.switchWorkspace(touchDirection)
scrollAccumulator = 0
actionInProgress = true
cooldownTimer.restart()
}
}
wheel.accepted = false
}
}
Item {
id: horizontalStack
anchors.fill: parent

View File

@@ -12,7 +12,7 @@ BasePill {
id: root
property bool compactMode: SettingsData.keyboardLayoutNameCompactMode
property string currentLayout: ""
property string currentLayout: CompositorService.isNiri ? NiriService.getCurrentKeyboardLayoutName() : ""
property string hyprlandKeyboard: ""
content: Component {
@@ -83,24 +83,14 @@ BasePill {
}
}
Timer {
id: updateTimer
interval: 1000
running: true
repeat: true
onTriggered: {
Component.onCompleted: {
if (CompositorService.isHyprland) {
updateLayout()
}
}
Component.onCompleted: {
updateLayout()
}
function updateLayout() {
if (CompositorService.isNiri) {
root.currentLayout = NiriService.getCurrentKeyboardLayoutName()
} else if (CompositorService.isHyprland) {
if (CompositorService.isHyprland) {
Proc.runCommand(null, ["hyprctl", "-j", "devices"], (output, exitCode) => {
if (exitCode !== 0) {
root.currentLayout = "Unknown"

View File

@@ -37,7 +37,7 @@ BasePill {
}
IconImage {
visible: SettingsData.launcherLogoMode === "compositor"
visible: SettingsData.launcherLogoMode === "compositor" && (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl || CompositorService.isSway)
anchors.centerIn: parent
width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
@@ -48,6 +48,10 @@ BasePill {
return "file://" + Theme.shellDir + "/assets/niri.svg"
} else if (CompositorService.isHyprland) {
return "file://" + Theme.shellDir + "/assets/hyprland.svg"
} else if (CompositorService.isDwl) {
return "file://" + Theme.shellDir + "/assets/mango.png"
} else if (CompositorService.isSway) {
return "file://" + Theme.shellDir + "/assets/sway.svg"
}
return ""
}

View File

@@ -116,6 +116,7 @@ Item {
color: trayItemArea.containsMouse ? Theme.primaryHover : "transparent"
IconImage {
id: iconImg
anchors.centerIn: parent
width: Theme.barIconSize(root.barThickness)
height: Theme.barIconSize(root.barThickness)
@@ -123,6 +124,21 @@ Item {
asynchronous: true
smooth: true
mipmap: true
visible: status === Image.Ready
}
Text {
anchors.centerIn: parent
visible: !iconImg.visible
text: {
const itemId = trayItem?.id || ""
if (!itemId) {
return "?"
}
return itemId.charAt(0).toUpperCase()
}
font.pixelSize: 10
color: Theme.surfaceText
}
}
@@ -203,6 +219,7 @@ Item {
color: trayItemArea.containsMouse ? Theme.primaryHover : "transparent"
IconImage {
id: iconImg
anchors.centerIn: parent
width: Theme.barIconSize(root.barThickness)
height: Theme.barIconSize(root.barThickness)
@@ -210,6 +227,21 @@ Item {
asynchronous: true
smooth: true
mipmap: true
visible: status === Image.Ready
}
Text {
anchors.centerIn: parent
visible: !iconImg.visible
text: {
const itemId = trayItem?.id || ""
if (!itemId) {
return "?"
}
return itemId.charAt(0).toUpperCase()
}
font.pixelSize: 10
color: Theme.surfaceText
}
}

View File

@@ -3,6 +3,7 @@ import QtQuick.Controls
import Quickshell
import Quickshell.Widgets
import Quickshell.Hyprland
import Quickshell.I3
import qs.Common
import qs.Services
import qs.Widgets
@@ -28,14 +29,26 @@ Item {
_desktopEntriesUpdateTrigger++
}
}
property int currentWorkspace: {
if (CompositorService.isNiri) {
return getNiriActiveWorkspace()
} else if (CompositorService.isHyprland) {
return getHyprlandActiveWorkspace()
} else if (CompositorService.isDwl) {
const activeTags = getDwlActiveTags()
return activeTags.length > 0 ? activeTags[0] : 0
} else if (CompositorService.isSway) {
return getSwayActiveWorkspace()
}
return 1
}
property var dwlActiveTags: {
if (CompositorService.isDwl) {
return getDwlActiveTags()
}
return []
}
property var workspaceList: {
if (CompositorService.isNiri) {
const baseList = getNiriWorkspaces()
@@ -43,13 +56,42 @@ Item {
}
if (CompositorService.isHyprland) {
const baseList = getHyprlandWorkspaces()
// Filter out special workspaces
const filteredList = baseList.filter(ws => ws.id > -1)
return SettingsData.showWorkspacePadding ? padWorkspaces(filteredList) : filteredList
}
if (CompositorService.isDwl) {
const baseList = getDwlTags()
return SettingsData.showWorkspacePadding ? padWorkspaces(baseList) : baseList
}
if (CompositorService.isSway) {
const baseList = getSwayWorkspaces()
return SettingsData.showWorkspacePadding ? padWorkspaces(baseList) : baseList
}
return [1]
}
function getSwayWorkspaces() {
const workspaces = I3.workspaces?.values || []
if (workspaces.length === 0) return [{"num": 1}]
if (!root.screenName || !SettingsData.workspacesPerMonitor) {
return workspaces.slice().sort((a, b) => a.num - b.num)
}
const monitorWorkspaces = workspaces.filter(ws => ws.monitor?.name === root.screenName)
return monitorWorkspaces.length > 0 ? monitorWorkspaces.sort((a, b) => a.num - b.num) : [{"num": 1}]
}
function getSwayActiveWorkspace() {
if (!root.screenName || !SettingsData.workspacesPerMonitor) {
const focusedWs = I3.workspaces?.values?.find(ws => ws.focused === true)
return focusedWs ? focusedWs.num : 1
}
const focusedWs = I3.workspaces?.values?.find(ws => ws.monitor?.name === root.screenName && ws.focused === true)
return focusedWs ? focusedWs.num : 1
}
function getWorkspaceIcons(ws) {
_desktopEntriesUpdateTrigger
if (!SettingsData.showWorkspaceApps || !ws) {
@@ -69,15 +111,35 @@ Item {
targetWorkspaceId = workspace.id
} else if (CompositorService.isHyprland) {
targetWorkspaceId = ws.id !== undefined ? ws.id : ws
} else if (CompositorService.isDwl) {
if (typeof ws !== "object" || ws.tag === undefined) {
return []
}
targetWorkspaceId = ws.tag
} else if (CompositorService.isSway) {
targetWorkspaceId = ws.num !== undefined ? ws.num : ws
} else {
return []
}
const wins = CompositorService.isNiri ? (NiriService.windows || []) : CompositorService.sortedToplevels
const byApp = {}
const isActiveWs = CompositorService.isNiri ? NiriService.allWorkspaces.some(ws => ws.id === targetWorkspaceId && ws.is_active) : targetWorkspaceId === root.currentWorkspace
let isActiveWs = false
if (CompositorService.isNiri) {
isActiveWs = NiriService.allWorkspaces.some(ws => ws.id === targetWorkspaceId && ws.is_active)
} else if (CompositorService.isSway) {
const focusedWs = I3.workspaces?.values?.find(ws => ws.focused === true)
isActiveWs = focusedWs ? (focusedWs.num === targetWorkspaceId) : false
} else if (CompositorService.isDwl) {
const output = DwlService.getOutputState(root.screenName)
if (output && output.tags) {
const tag = output.tags.find(t => t.tag === targetWorkspaceId)
isActiveWs = tag ? (tag.state === 1) : false
}
} else {
isActiveWs = targetWorkspaceId === root.currentWorkspace
}
wins.forEach((w, i) => {
if (!w) {
@@ -87,8 +149,9 @@ Item {
let winWs = null
if (CompositorService.isNiri) {
winWs = w.workspace_id
} else if (CompositorService.isSway) {
winWs = w.workspace?.num
} else {
// For Hyprland, we need to find the corresponding Hyprland toplevel to get workspace
const hyprlandToplevels = Array.from(Hyprland.toplevels?.values || [])
const hyprToplevel = hyprlandToplevels.find(ht => ht.wayland === w)
winWs = hyprToplevel?.workspace?.id
@@ -110,14 +173,14 @@ Item {
"type": "icon",
"icon": icon,
"isSteamApp": isSteamApp,
"active": !!(w.activated || (CompositorService.isNiri && w.is_focused)),
"active": !!((w.activated || w.is_focused) || (CompositorService.isNiri && w.is_focused)),
"count": 1,
"windowId": w.address || w.id,
"fallbackText": w.appId || w.class || w.title || ""
}
} else {
byApp[key].count++
if (w.activated || (CompositorService.isNiri && w.is_focused)) {
if ((w.activated || w.is_focused) || (CompositorService.isNiri && w.is_focused)) {
byApp[key].active = true
}
}
@@ -128,10 +191,16 @@ Item {
function padWorkspaces(list) {
const padded = list.slice()
const placeholder = CompositorService.isHyprland ? {
"id": -1,
"name": ""
} : -1
let placeholder
if (CompositorService.isHyprland) {
placeholder = {"id": -1, "name": ""}
} else if (CompositorService.isDwl) {
placeholder = {"tag": -1}
} else if (CompositorService.isSway) {
placeholder = {"num": -1}
} else {
placeholder = -1
}
while (padded.length < 3) {
padded.push(placeholder)
}
@@ -199,7 +268,6 @@ Item {
return Hyprland.focusedWorkspace ? Hyprland.focusedWorkspace.id : 1
}
// Find the monitor object for this screen
const monitors = Hyprland.monitors?.values || []
const currentMonitor = monitors.find(monitor => monitor.name === root.screenName)
@@ -207,10 +275,44 @@ Item {
return 1
}
// Use the monitor's active workspace ID (like original config)
return currentMonitor.activeWorkspace?.id ?? 1
}
function getDwlTags() {
if (!DwlService.dwlAvailable) {
return [{"tag": 0}, {"tag": 1}]
}
const output = DwlService.getOutputState(root.screenName)
if (!output || !output.tags || output.tags.length === 0) {
return [{"tag": 0}]
}
if (SettingsData.dwlShowAllTags) {
return output.tags.map(tag => ({"tag": tag.tag, "state": tag.state, "clients": tag.clients, "focused": tag.focused}))
}
const visibleTagIndices = DwlService.getVisibleTags(root.screenName)
return visibleTagIndices.map(tagIndex => {
const tagData = output.tags.find(t => t.tag === tagIndex)
return {
"tag": tagIndex,
"state": tagData?.state ?? 0,
"clients": tagData?.clients ?? 0,
"focused": tagData?.focused ?? false
}
})
}
function getDwlActiveTags() {
if (!DwlService.dwlAvailable) {
return [0]
}
const activeTags = DwlService.getActiveTags(root.screenName)
return activeTags.length > 0 ? activeTags : [0]
}
readonly property real padding: Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
readonly property real visualWidth: isVertical ? widgetHeight : (workspaceRow.implicitWidth + padding * 2)
readonly property real visualHeight: isVertical ? (workspaceRow.implicitHeight + padding * 2) : widgetHeight
@@ -219,6 +321,10 @@ Item {
return root.workspaceList.filter(ws => {
if (CompositorService.isHyprland) {
return ws && ws.id !== -1
} else if (CompositorService.isDwl) {
return ws && ws.tag !== -1
} else if (CompositorService.isSway) {
return ws && ws.num !== -1
}
return ws !== -1
})
@@ -255,12 +361,42 @@ Item {
}
Hyprland.dispatch(`workspace ${realWorkspaces[nextIndex].id}`)
} else if (CompositorService.isDwl) {
const realWorkspaces = getRealWorkspaces()
if (realWorkspaces.length < 2) {
return
}
const currentIndex = realWorkspaces.findIndex(ws => ws.tag === root.currentWorkspace)
const validIndex = currentIndex === -1 ? 0 : currentIndex
const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0)
if (nextIndex === validIndex) {
return
}
DwlService.switchToTag(root.screenName, realWorkspaces[nextIndex].tag)
} else if (CompositorService.isSway) {
const realWorkspaces = getRealWorkspaces()
if (realWorkspaces.length < 2) {
return
}
const currentIndex = realWorkspaces.findIndex(ws => ws.num === root.currentWorkspace)
const validIndex = currentIndex === -1 ? 0 : currentIndex
const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0)
if (nextIndex === validIndex) {
return
}
try { I3.dispatch(`workspace number ${realWorkspaces[nextIndex].num}`) } catch(_){}
}
}
width: isVertical ? barThickness : visualWidth
height: isVertical ? visualHeight : barThickness
visible: CompositorService.isNiri || CompositorService.isHyprland
visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl || CompositorService.isSway
Rectangle {
id: visualBackground
@@ -280,104 +416,11 @@ Item {
anchors.fill: parent
acceptedButtons: Qt.RightButton
property real scrollAccumulator: 0
property real touchpadThreshold: 500
onClicked: mouse => {
if (mouse.button === Qt.RightButton && CompositorService.isHyprland && root.hyprlandOverviewLoader?.item) {
root.hyprlandOverviewLoader.item.overviewOpen = !root.hyprlandOverviewLoader.item.overviewOpen
}
}
onWheel: wheel => {
const deltaY = wheel.angleDelta.y
const isMouseWheel = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0
const direction = deltaY < 0 ? 1 : -1
if (isMouseWheel) {
if (!SettingsData.workspaceScrolling || !CompositorService.isNiri) {
switchWorkspace(direction)
}
else {
const windows = root.sortedToplevels;
if (windows.length < 2) {
return;
}
let currentIndex = -1;
for (let i = 0; i < windows.length; i++) {
if (windows[i].activated) {
currentIndex = i;
break;
}
}
let nextIndex;
if (deltaY < 0) {
if (currentIndex === -1) {
nextIndex = 0;
} else {
nextIndex = currentIndex +1;
}
} else {
if (currentIndex === -1) {
nextIndex = windows.length -1;
} else {
nextIndex = currentIndex - 1
}
}
const nextWindow = windows[nextIndex];
if (nextWindow) {
nextWindow.activate();
}
}
} else {
scrollAccumulator += deltaY
if (Math.abs(scrollAccumulator) >= touchpadThreshold) {
const touchDirection = scrollAccumulator < 0 ? 1 : -1
if (!SettingsData.workspaceScrolling || !CompositorService.isNiri) {
switchWorkspace(touchDirection)
}
else {
const windows = root.sortedToplevels;
if (windows.length < 2) {
return;
}
let currentIndex = -1;
for (let i = 0; i < windows.length; i++) {
if (windows[i].activated) {
currentIndex = i;
break;
}
}
let nextIndex;
if (deltaY < 0) {
if (currentIndex === -1) {
nextIndex = 0;
} else {
nextIndex = currentIndex +1;
}
} else {
if (currentIndex === -1) {
nextIndex = windows.length -1;
} else {
nextIndex = currentIndex - 1
}
}
const nextWindow = windows[nextIndex];
if (nextWindow) {
nextWindow.activate();
}
}
scrollAccumulator = 0
}
}
wheel.accepted = true
}
}
Flow {
@@ -396,12 +439,20 @@ Item {
property bool isActive: {
if (CompositorService.isHyprland) {
return modelData && modelData.id === root.currentWorkspace
} else if (CompositorService.isDwl) {
return modelData && root.dwlActiveTags.includes(modelData.tag)
} else if (CompositorService.isSway) {
return modelData && modelData.num === root.currentWorkspace
}
return modelData === root.currentWorkspace
}
property bool isPlaceholder: {
if (CompositorService.isHyprland) {
return modelData && modelData.id === -1
} else if (CompositorService.isDwl) {
return modelData && modelData.tag === -1
} else if (CompositorService.isSway) {
return modelData && modelData.num === -1
}
return modelData === -1
}
@@ -412,8 +463,11 @@ Item {
property bool isUrgent: {
if (CompositorService.isHyprland) {
return modelData?.urgent ?? false
}
if (CompositorService.isNiri) {
} else if (CompositorService.isNiri) {
return loadedIsUrgent
} else if (CompositorService.isDwl) {
return modelData?.state === 2
} else if (CompositorService.isSway) {
return loadedIsUrgent
}
return false
@@ -456,15 +510,29 @@ Item {
hoverEnabled: !isPlaceholder
cursorShape: isPlaceholder ? Qt.ArrowCursor : Qt.PointingHandCursor
enabled: !isPlaceholder
onClicked: {
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: mouse => {
if (isPlaceholder) {
return
}
const isRightClick = mouse.button === Qt.RightButton
if (CompositorService.isNiri) {
NiriService.switchToWorkspace(modelData - 1)
} else if (CompositorService.isHyprland && modelData?.id) {
Hyprland.dispatch(`workspace ${modelData.id}`)
} else if (CompositorService.isDwl && modelData?.tag !== undefined) {
console.log("DWL click - tag:", modelData.tag, "rightClick:", isRightClick)
if (isRightClick) {
console.log("Calling toggleTag")
DwlService.toggleTag(root.screenName, modelData.tag)
} else {
console.log("Calling switchToTag")
DwlService.switchToTag(root.screenName, modelData.tag)
}
} else if (CompositorService.isSway && modelData?.num) {
try { I3.dispatch(`workspace number ${modelData.num}`) } catch(_){}
}
}
}
@@ -487,9 +555,13 @@ Item {
wsData = NiriService.allWorkspaces.find(ws => ws.idx + 1 === modelData && ws.output === root.screenName) || null;
} else if (CompositorService.isHyprland) {
wsData = modelData;
} else if (CompositorService.isDwl) {
wsData = modelData;
} else if (CompositorService.isSway) {
wsData = modelData;
}
delegateRoot.loadedWorkspaceData = wsData;
delegateRoot.loadedIsUrgent = wsData?.is_urgent ?? false;
delegateRoot.loadedIsUrgent = wsData?.urgent ?? false;
var icData = null;
if (wsData?.name) {
@@ -499,7 +571,11 @@ Item {
delegateRoot.loadedHasIcon = icData !== null;
if (SettingsData.showWorkspaceApps) {
delegateRoot.loadedIcons = root.getWorkspaceIcons(CompositorService.isHyprland ? modelData : (modelData === -1 ? null : modelData));
if (CompositorService.isDwl || CompositorService.isSway) {
delegateRoot.loadedIcons = root.getWorkspaceIcons(modelData);
} else {
delegateRoot.loadedIcons = root.getWorkspaceIcons(CompositorService.isHyprland ? modelData : (modelData === -1 ? null : modelData));
}
} else {
delegateRoot.loadedIcons = [];
}
@@ -521,8 +597,14 @@ Item {
radius: Theme.cornerRadius
color: isActive ? Theme.primary : isUrgent ? Theme.error : isPlaceholder ? Theme.surfaceTextLight : isHovered ? Theme.outlineButton : Theme.surfaceTextAlpha
border.width: isUrgent && !isActive ? 2 : 0
border.color: isUrgent && !isActive ? Theme.error : Theme.withAlpha(Theme.error, 0)
border.width: {
if (isUrgent && !isActive) return 2
return 0
}
border.color: {
if (isUrgent && !isActive) return Theme.error
return Theme.withAlpha(Theme.error, 0)
}
Behavior on width {
enabled: (!SettingsData.showWorkspaceApps || SettingsData.maxWorkspaceIcons <= 3)
@@ -554,6 +636,13 @@ Item {
}
}
Behavior on border.color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
}
Loader {
id: appIconsLoader
anchors.fill: parent
@@ -744,11 +833,29 @@ Item {
StyledText {
anchors.centerIn: parent
text: {
const isPlaceholder = CompositorService.isHyprland ? (modelData?.id === -1) : (modelData === -1)
let isPlaceholder
if (CompositorService.isHyprland) {
isPlaceholder = modelData?.id === -1
} else if (CompositorService.isDwl) {
isPlaceholder = modelData?.tag === -1
} else if (CompositorService.isSway) {
isPlaceholder = modelData?.num === -1
} else {
isPlaceholder = modelData === -1
}
if (isPlaceholder) {
return index + 1
}
return CompositorService.isHyprland ? (modelData?.id || "") : (modelData - 1);
if (CompositorService.isHyprland) {
return modelData?.id || ""
} else if (CompositorService.isDwl) {
return (modelData?.tag !== undefined) ? (modelData.tag + 1) : ""
} else if (CompositorService.isSway) {
return modelData?.num || ""
}
return modelData - 1
}
color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium
font.pixelSize: Theme.barTextSize(barThickness)
@@ -776,6 +883,16 @@ Item {
function onShowWorkspaceAppsChanged() { delegateRoot.updateAllData() }
function onWorkspaceNameIconsChanged() { delegateRoot.updateAllData() }
}
Connections {
target: DwlService
enabled: CompositorService.isDwl
function onStateChanged() { delegateRoot.updateAllData() }
}
Connections {
target: I3.workspaces
enabled: CompositorService.isSway
function onValuesChanged() { delegateRoot.updateAllData() }
}
}
}
}

View File

@@ -11,6 +11,7 @@ Scope {
id: root
property bool lockSecured: false
property bool unlockInProgress: false
readonly property alias passwd: passwd
readonly property alias fprint: fprint
@@ -50,7 +51,11 @@ Scope {
onCompleted: res => {
if (res === PamResult.Success) {
root.unlockRequested();
if (!root.unlockInProgress) {
root.unlockInProgress = true;
fprint.abort();
root.unlockRequested();
}
return;
}
@@ -92,7 +97,11 @@ Scope {
return;
if (res === PamResult.Success) {
root.unlockRequested();
if (!root.unlockInProgress) {
root.unlockInProgress = true;
passwd.abort();
root.unlockRequested();
}
return;
}
@@ -162,8 +171,11 @@ Scope {
root.state = "";
root.fprintState = "";
root.lockMessage = "";
root.unlockInProgress = false;
} else {
fprint.abort();
passwd.abort();
root.unlockInProgress = false;
}
}

View File

@@ -9,6 +9,59 @@ Item {
id: aboutTab
property bool isHyprland: CompositorService.isHyprland
property bool isNiri: CompositorService.isNiri
property bool isSway: CompositorService.isSway
property bool isDwl: CompositorService.isDwl
property string compositorName: {
if (isHyprland) return "hyprland"
if (isSway) return "sway"
if (isDwl) return "mangowc"
return "niri"
}
property string compositorLogo: {
if (isHyprland) return "/assets/hyprland.svg"
if (isSway) return "/assets/sway.svg"
if (isDwl) return "/assets/mango.png"
return "/assets/niri.svg"
}
property string compositorUrl: {
if (isHyprland) return "https://hypr.land"
if (isSway) return "https://swaywm.org"
if (isDwl) return "https://github.com/DreamMaoMao/mangowc"
return "https://github.com/YaLTeR/niri"
}
property string compositorTooltip: {
if (isHyprland) return "Hyprland Website"
if (isSway) return "Sway Website"
if (isDwl) return "mangowc GitHub"
return "niri GitHub"
}
property string dmsDiscordUrl: "https://discord.gg/vT8Sfjy7sx"
property string dmsDiscordTooltip: "niri/dms Discord"
property string compositorDiscordUrl: {
if (isHyprland) return "https://discord.com/invite/hQ9XvMUjjr"
if (isDwl) return "https://discord.gg/CPjbDxesh5"
return ""
}
property string compositorDiscordTooltip: {
if (isHyprland) return "Hyprland Discord Server"
if (isDwl) return "mangowc Discord Server"
return ""
}
property string redditUrl: "https://reddit.com/r/niri"
property string redditTooltip: "r/niri Subreddit"
property bool showMatrix: isNiri && !isHyprland && !isSway && !isDwl
property bool showCompositorDiscord: isHyprland || isDwl
property bool showReddit: isNiri && !isHyprland && !isSway && !isDwl
DankFlickable {
anchors.fill: parent
@@ -69,14 +122,19 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
height: 24
width: {
if (isHyprland) {
return compositorButton.width + discordButton.width + Theme.spacingM + redditButton.width + Theme.spacingM
} else {
return compositorButton.width + matrixButton.width + 4 + discordButton.width + Theme.spacingM + redditButton.width + Theme.spacingM
let baseWidth = compositorButton.width + dmsDiscordButton.width + Theme.spacingM
if (showMatrix) {
baseWidth += matrixButton.width + 4
}
if (showCompositorDiscord) {
baseWidth += compositorDiscordButton.width + Theme.spacingM
}
if (showReddit) {
baseWidth += redditButton.width + Theme.spacingM
}
return baseWidth
}
// Compositor logo (Niri or Hyprland)
Item {
id: compositorButton
width: 24
@@ -86,14 +144,14 @@ Item {
x: 0
property bool hovered: false
property string tooltipText: isHyprland ? "Hyprland Website" : "niri GitHub"
property string tooltipText: compositorTooltip
Image {
anchors.fill: parent
source: Qt.resolvedUrl(".").toString().replace(
"file://", "").replace(
"/Modules/Settings/",
"") + (isHyprland ? "/assets/hyprland.svg" : "/assets/niri.svg")
"") + compositorLogo
sourceSize: Qt.size(24, 24)
smooth: true
fillMode: Image.PreserveAspectFit
@@ -105,18 +163,16 @@ Item {
hoverEnabled: true
onEntered: parent.hovered = true
onExited: parent.hovered = false
onClicked: Qt.openUrlExternally(
isHyprland ? "https://hypr.land" : "https://github.com/YaLTeR/niri")
onClicked: Qt.openUrlExternally(compositorUrl)
}
}
// Matrix button (only for Niri)
Item {
id: matrixButton
width: 30
height: 24
x: compositorButton.x + compositorButton.width + 4
visible: !isHyprland
visible: showMatrix
property bool hovered: false
property string tooltipText: "niri Matrix Chat"
@@ -149,16 +205,15 @@ Item {
}
}
// Discord button
Item {
id: discordButton
id: dmsDiscordButton
width: 20
height: 20
x: isHyprland ? compositorButton.x + compositorButton.width + Theme.spacingM : matrixButton.x + matrixButton.width + Theme.spacingM
x: showMatrix ? matrixButton.x + matrixButton.width + Theme.spacingM : compositorButton.x + compositorButton.width + Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
property bool hovered: false
property string tooltipText: isHyprland ? "Hyprland Discord Server" : "niri Discord Server"
property string tooltipText: dmsDiscordTooltip
Image {
anchors.fill: parent
@@ -177,21 +232,52 @@ Item {
hoverEnabled: true
onEntered: parent.hovered = true
onExited: parent.hovered = false
onClicked: Qt.openUrlExternally(
isHyprland ? "https://discord.com/invite/hQ9XvMUjjr" : "https://discord.gg/vT8Sfjy7sx")
onClicked: Qt.openUrlExternally(dmsDiscordUrl)
}
}
Item {
id: compositorDiscordButton
width: 20
height: 20
x: dmsDiscordButton.x + dmsDiscordButton.width + Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
visible: showCompositorDiscord
property bool hovered: false
property string tooltipText: compositorDiscordTooltip
Image {
anchors.fill: parent
source: Qt.resolvedUrl(".").toString().replace(
"file://", "").replace(
"/Modules/Settings/",
"") + "/assets/discord.svg"
sourceSize: Qt.size(20, 20)
smooth: true
fillMode: Image.PreserveAspectFit
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onEntered: parent.hovered = true
onExited: parent.hovered = false
onClicked: Qt.openUrlExternally(compositorDiscordUrl)
}
}
// Reddit button
Item {
id: redditButton
width: 20
height: 20
x: discordButton.x + discordButton.width + Theme.spacingM
x: showCompositorDiscord ? compositorDiscordButton.x + compositorDiscordButton.width + Theme.spacingM : dmsDiscordButton.x + dmsDiscordButton.width + Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
visible: showReddit
property bool hovered: false
property string tooltipText: isHyprland ? "r/hyprland Subreddit" : "r/niri Subreddit"
property string tooltipText: redditTooltip
Image {
anchors.fill: parent
@@ -210,8 +296,7 @@ Item {
hoverEnabled: true
onEntered: parent.hovered = true
onExited: parent.hovered = false
onClicked: Qt.openUrlExternally(
isHyprland ? "https://reddit.com/r/hyprland" : "https://reddit.com/r/niri")
onClicked: Qt.openUrlExternally(redditUrl)
}
}
}
@@ -506,8 +591,9 @@ Item {
property var hoveredButton: {
if (compositorButton.hovered) return compositorButton
if (matrixButton.visible && matrixButton.hovered) return matrixButton
if (discordButton.hovered) return discordButton
if (redditButton.hovered) return redditButton
if (dmsDiscordButton.hovered) return dmsDiscordButton
if (compositorDiscordButton.visible && compositorDiscordButton.hovered) return compositorDiscordButton
if (redditButton.visible && redditButton.hovered) return redditButton
return null
}

View File

@@ -697,7 +697,7 @@ Item {
text: I18n.tr("Show on Last Display")
description: I18n.tr("Always show when there's only one connected display")
checked: displaysTab.getShowOnLastDisplay(parent.componentId)
visible: !displaysTab.getScreenPreferences(parent.componentId).includes("all") && ["dankBar", "dock", "notifications", "osd", "toast"].includes(parent.componentId)
visible: !displaysTab.getScreenPreferences(parent.componentId).includes("all") && ["dankBar", "dock", "notifications", "osd", "toast", "notepad", "systemTray"].includes(parent.componentId)
onToggled: (checked) => {
displaysTab.setShowOnLastDisplay(parent.componentId, checked);
}

View File

@@ -87,9 +87,16 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
model: {
const modes = [I18n.tr("Apps Icon"), I18n.tr("OS Logo")]
if (CompositorService.isNiri || CompositorService.isHyprland) {
const compositorName = CompositorService.isNiri ? "niri" : "Hyprland"
modes.push(compositorName)
if (CompositorService.isNiri) {
modes.push("niri")
} else if (CompositorService.isHyprland) {
modes.push("Hyprland")
} else if (CompositorService.isDwl) {
modes.push("mango")
} else if (CompositorService.isSway) {
modes.push("Sway")
} else {
modes.push(I18n.tr("Compositor"))
}
modes.push(I18n.tr("Custom"))
return modes
@@ -97,12 +104,8 @@ Item {
currentIndex: {
if (SettingsData.launcherLogoMode === "apps") return 0
if (SettingsData.launcherLogoMode === "os") return 1
if (SettingsData.launcherLogoMode === "compositor") {
return (CompositorService.isNiri || CompositorService.isHyprland) ? 2 : -1
}
if (SettingsData.launcherLogoMode === "custom") {
return (CompositorService.isNiri || CompositorService.isHyprland) ? 3 : 2
}
if (SettingsData.launcherLogoMode === "compositor") return 2
if (SettingsData.launcherLogoMode === "custom") return 3
return 0
}
onSelectionChanged: (index, selected) => {
@@ -111,13 +114,9 @@ Item {
SettingsData.setLauncherLogoMode("apps")
} else if (index === 1) {
SettingsData.setLauncherLogoMode("os")
} else if (CompositorService.isNiri || CompositorService.isHyprland) {
if (index === 2) {
SettingsData.setLauncherLogoMode("compositor")
} else if (index === 3) {
SettingsData.setLauncherLogoMode("custom")
}
} else if (index === 2) {
SettingsData.setLauncherLogoMode("compositor")
} else if (index === 3) {
SettingsData.setLauncherLogoMode("custom")
}
}

View File

@@ -65,17 +65,6 @@ Item {
checked)
}
}
DankToggle {
width: parent.width
text: I18n.tr("Window Scrolling")
description: I18n.tr("Scroll through windows, rather than workspaces")
checked: SettingsData.workspaceScrolling
visible: CompositorService.isNiri
onToggled: checked => {
return SettingsData.setWorkspaceScrolling(checked)
}
}
DankToggle {
width: parent.width
text: I18n.tr("Workspace Padding")
@@ -149,6 +138,17 @@ Item {
return SettingsData.setWorkspacesPerMonitor(checked);
}
}
DankToggle {
width: parent.width
text: I18n.tr("Show All Tags")
description: I18n.tr("Show all 9 tags instead of only occupied tags (DWL only)")
checked: SettingsData.dwlShowAllTags
visible: CompositorService.isDwl
onToggled: checked => {
return SettingsData.setDwlShowAllTags(checked);
}
}
}
}

View File

@@ -12,7 +12,7 @@
</div>
A modern Wayland desktop shell built with [Quickshell](https://quickshell.org/) and [Go](https://go.dev/). Optimized for the [niri](https://github.com/YaLTeR/niri) and [Hyprland](https://hyprland.org/) compositors.
A modern Wayland desktop shell built with [Quickshell](https://quickshell.org/) and [Go](https://go.dev/). Optimized for the [niri](https://github.com/YaLTeR/niri), [Hyprland](https://hyprland.org/), [sway](https://swaywm.org/), and [dwl/mangowc](https://github.com/DreamMaoMao/mangowc) compositors.
Features notifications, app launcher, wallpaper customization, and fully customizable with [plugins](https://github.com/AvengeMedia/dms-plugin-registry).
@@ -134,7 +134,7 @@ curl -fsSL https://install.danklinux.com | sh
### Compositor Setup
DankMaterialShell particularly aims at supporting the **niri** and **Hyprland** compositors, but it does support more wayland compositors with a diminished feature set (no monitor off, workspace switcher, overview integration, etc.):
DankMaterialShell particularly aims at supporting the **niri**, **Hyprland**, **sway**, and **dwl/MangoWC** compositors, but it does support more wayland compositors with a diminished feature set (no monitor off, workspace switcher, overview integration, etc.):
**Niri**:
```bash
@@ -164,6 +164,10 @@ sudo dnf copr enable solopasha/hyprland && sudo dnf install hyprland
For detailed Hyprland installation instructions, see the [Hyprland wiki](https://wiki.hypr.land/Getting-Started/Installation/).
**sway/dwl (MangoWC)**:
TODO - not documented.
### Dank Shell Installation
*feel free to contribute steps for other distributions*
@@ -438,15 +442,9 @@ layer-rule {
If using "Blur Layer" option, you may want the blurred layer to appear on overview only, that can be done with some layer rules:
```kdl
layer-rule {
match namespace="dms:blurwallpaper"
opacity 0.0
}
layer-rule {
match namespace="dms:blurwallpaper"
place-within-backdrop true
opacity 1.0
}
```

View File

@@ -12,10 +12,13 @@ Singleton {
property bool isHyprland: false
property bool isNiri: false
property bool isDwl: false
property bool isSway: false
property string compositor: "unknown"
readonly property string hyprlandSignature: Quickshell.env("HYPRLAND_INSTANCE_SIGNATURE")
readonly property string niriSocket: Quickshell.env("NIRI_SOCKET")
readonly property string swaySocket: Quickshell.env("SWAYSOCK")
property bool useNiriSorting: isNiri && NiriService
property var sortedToplevels: sortedToplevelsCache
@@ -26,6 +29,30 @@ Singleton {
property bool _hasRefreshedOnce: false
property var _coordCache: ({})
property int _refreshCount: 0
property real _refreshWindowStart: 0
readonly property int _maxRefreshesPerSecond: 3
function getScreenScale(screen) {
if (!screen) return 1
if (isNiri && screen) {
const niriScale = NiriService.displayScales[screen.name]
if (niriScale !== undefined) return niriScale
}
if (isHyprland && screen) {
const hyprlandMonitor = Hyprland.monitors.values.find(m => m.name === screen.name)
if (hyprlandMonitor?.scale !== undefined) return hyprlandMonitor.scale
}
if (isDwl && screen) {
const dwlScale = DwlService.getOutputScale(screen.name)
if (dwlScale !== undefined && dwlScale > 0) return dwlScale
}
return screen?.devicePixelRatio || 1
}
Timer {
id: refreshTimer
@@ -53,6 +80,19 @@ Singleton {
function scheduleRefresh() {
if (!isHyprland) return
if (_refreshScheduled) return
const now = Date.now()
if (now - _refreshWindowStart > 1000) {
_refreshCount = 0
_refreshWindowStart = now
}
if (_refreshCount >= _maxRefreshesPerSecond) {
console.warn("CompositorService: Refresh rate limit exceeded, skipping refresh")
return
}
_refreshCount++
_refreshScheduled = true
refreshTimer.restart()
}
@@ -87,6 +127,15 @@ Singleton {
Qt.callLater(() => NiriService.generateNiriLayoutConfig())
}
Connections {
target: DwlService
function onStateChanged() {
if (isDwl && !isHyprland && !isNiri) {
scheduleSort()
}
}
}
function computeSortedToplevels() {
if (!ToplevelManager.toplevels || !ToplevelManager.toplevels.values)
return []
@@ -125,6 +174,18 @@ Singleton {
} catch(e) { return fb }
}
let currentAddresses = new Set()
for (let i = 0; i < items.length; i++) {
const addr = items[i]?.address
if (addr) currentAddresses.add(addr)
}
for (let cachedAddr in _coordCache) {
if (!currentAddresses.has(cachedAddr)) {
delete _coordCache[cachedAddr]
}
}
let snap = []
let missingAnyPosition = false
let hasNewWindow = false
@@ -331,6 +392,8 @@ Singleton {
if (hyprlandSignature && hyprlandSignature.length > 0) {
isHyprland = true
isNiri = false
isDwl = false
isSway = false
compositor = "hyprland"
console.info("CompositorService: Detected Hyprland")
try {
@@ -344,34 +407,103 @@ Singleton {
if (exitCode === 0) {
isNiri = true
isHyprland = false
isDwl = false
isSway = false
compositor = "niri"
console.info("CompositorService: Detected Niri with socket:", niriSocket)
NiriService.generateNiriBinds()
NiriService.generateNiriBlurrule()
} else {
isHyprland = false
isNiri = true
compositor = "niri"
console.warn("CompositorService: Niri socket check failed, defaulting to Niri anyway")
}
}, 0)
return
}
if (swaySocket && swaySocket.length > 0) {
Proc.runCommand("swaySocketCheck", ["test", "-S", swaySocket], (output, exitCode) => {
if (exitCode === 0) {
isNiri = false
isHyprland = false
isDwl = false
isSway = true
compositor = "sway"
console.info("CompositorService: Detected Sway with socket:", swaySocket)
}
}, 0)
return
}
if (DMSService.dmsAvailable) {
Qt.callLater(checkForDwl)
} else {
isHyprland = false
isNiri = false
isDwl = false
isSway = false
compositor = "unknown"
console.warn("CompositorService: No compositor detected")
}
}
Connections {
target: DMSService
function onCapabilitiesReceived() {
if (!isHyprland && !isNiri && !isDwl) {
checkForDwl()
}
}
}
function checkForDwl() {
if (DMSService.apiVersion >= 12 && DMSService.capabilities.includes("dwl")) {
isHyprland = false
isNiri = false
isDwl = true
compositor = "dwl"
console.info("CompositorService: Detected DWL via DMS capability")
}
}
function powerOffMonitors() {
if (isNiri) return NiriService.powerOffMonitors()
if (isHyprland) return Hyprland.dispatch("dpms off")
if (isDwl) return _dwlPowerOffMonitors()
if (isSway) { try { I3.dispatch("output * dpms off") } catch(_){} return }
console.warn("CompositorService: Cannot power off monitors, unknown compositor")
}
function powerOnMonitors() {
if (isNiri) return NiriService.powerOnMonitors()
if (isHyprland) return Hyprland.dispatch("dpms on")
if (isDwl) return _dwlPowerOnMonitors()
if (isSway) { try { I3.dispatch("output * dpms on") } catch(_){} return }
console.warn("CompositorService: Cannot power on monitors, unknown compositor")
}
function _dwlPowerOffMonitors() {
if (!Quickshell.screens || Quickshell.screens.length === 0) {
console.warn("CompositorService: No screens available for DWL power off")
return
}
for (let i = 0; i < Quickshell.screens.length; i++) {
const screen = Quickshell.screens[i]
if (screen && screen.name) {
Quickshell.execDetached(["mmsg", "-d", "disable_monitor," + screen.name])
}
}
}
function _dwlPowerOnMonitors() {
if (!Quickshell.screens || Quickshell.screens.length === 0) {
console.warn("CompositorService: No screens available for DWL power on")
return
}
for (let i = 0; i < Quickshell.screens.length; i++) {
const screen = Quickshell.screens[i]
if (screen && screen.name) {
Quickshell.execDetached(["mmsg", "-d", "enable_monitor," + screen.name])
}
}
}
}

View File

@@ -42,6 +42,7 @@ Singleton {
signal capabilitiesReceived()
signal credentialsRequest(var data)
signal bluetoothPairingRequest(var data)
signal dwlStateUpdate(var data)
Component.onCompleted: {
if (socketPath && socketPath.length > 0) {
@@ -266,6 +267,8 @@ Singleton {
}
} else if (service === "bluetooth.pairing") {
bluetoothPairingRequest(data)
} else if (service === "dwl") {
dwlStateUpdate(data)
}
}

259
Services/DwlService.qml Normal file
View File

@@ -0,0 +1,259 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
Singleton {
id: root
property bool dwlAvailable: false
property var outputs: ({})
property var tagCount: 9
property var layouts: []
property string activeOutput: ""
property var outputScales: ({})
signal stateChanged()
Connections {
target: DMSService
function onCapabilitiesReceived() {
checkCapabilities()
}
function onConnectionStateChanged() {
if (DMSService.isConnected) {
checkCapabilities()
} else {
dwlAvailable = false
}
}
function onDwlStateUpdate(data) {
if (dwlAvailable) {
handleStateUpdate(data)
}
}
}
Component.onCompleted: {
if (DMSService.dmsAvailable) {
checkCapabilities()
}
if (dwlAvailable) {
refreshOutputScales()
}
}
function checkCapabilities() {
if (!DMSService.capabilities || !Array.isArray(DMSService.capabilities)) {
dwlAvailable = false
return
}
const hasDwl = DMSService.capabilities.includes("dwl")
if (hasDwl && !dwlAvailable) {
dwlAvailable = true
console.info("DwlService: DWL capability detected")
requestState()
refreshOutputScales()
} else if (!hasDwl) {
dwlAvailable = false
}
}
function requestState() {
if (!DMSService.isConnected || !dwlAvailable) {
return
}
DMSService.sendRequest("dwl.getState", null, response => {
if (response.result) {
handleStateUpdate(response.result)
}
})
}
function handleStateUpdate(state) {
outputs = state.outputs || {}
tagCount = state.tagCount || 9
layouts = state.layouts || []
activeOutput = state.activeOutput || ""
stateChanged()
}
function setTags(outputName, tagmask, toggleTagset) {
if (!DMSService.isConnected || !dwlAvailable) {
return
}
DMSService.sendRequest("dwl.setTags", {
"output": outputName,
"tagmask": tagmask,
"toggleTagset": toggleTagset
}, response => {
if (response.error) {
console.warn("DwlService: setTags error:", response.error)
}
})
}
function setClientTags(outputName, andTags, xorTags) {
if (!DMSService.isConnected || !dwlAvailable) {
return
}
DMSService.sendRequest("dwl.setClientTags", {
"output": outputName,
"andTags": andTags,
"xorTags": xorTags
}, response => {
if (response.error) {
console.warn("DwlService: setClientTags error:", response.error)
}
})
}
function setLayout(outputName, index) {
if (!DMSService.isConnected || !dwlAvailable) {
return
}
DMSService.sendRequest("dwl.setLayout", {
"output": outputName,
"index": index
}, response => {
if (response.error) {
console.warn("DwlService: setLayout error:", response.error)
}
})
}
function getOutputState(outputName) {
if (!outputs || !outputs[outputName]) {
return null
}
return outputs[outputName]
}
function getActiveTags(outputName) {
const output = getOutputState(outputName)
if (!output || !output.tags) {
return []
}
return output.tags.filter(tag => tag.state === 1).map(tag => tag.tag)
}
function getTagsWithClients(outputName) {
const output = getOutputState(outputName)
if (!output || !output.tags) {
return []
}
return output.tags.filter(tag => tag.clients > 0).map(tag => tag.tag)
}
function getUrgentTags(outputName) {
const output = getOutputState(outputName)
if (!output || !output.tags) {
return []
}
return output.tags.filter(tag => tag.state === 2).map(tag => tag.tag)
}
function switchToTag(outputName, tagIndex) {
const tagmask = 1 << tagIndex
setTags(outputName, tagmask, 0)
}
function toggleTag(outputName, tagIndex) {
const output = getOutputState(outputName)
if (!output || !output.tags) {
console.log("toggleTag: no output or tags for", outputName)
return
}
let currentMask = 0
output.tags.forEach(tag => {
if (tag.state === 1) {
currentMask |= (1 << tag.tag)
}
})
const clickedMask = 1 << tagIndex
const newMask = currentMask ^ clickedMask
console.log("toggleTag:", outputName, "tag:", tagIndex, "currentMask:", currentMask.toString(2), "clickedMask:", clickedMask.toString(2), "newMask:", newMask.toString(2))
if (newMask === 0) {
console.log("toggleTag: newMask is 0, switching to tag", tagIndex)
setTags(outputName, 1 << tagIndex, 0)
} else {
console.log("toggleTag: setting combined mask", newMask)
setTags(outputName, newMask, 0)
}
}
function quit() {
Quickshell.execDetached(["mmsg", "-d", "quit"])
}
Process {
id: scaleQueryProcess
command: ["mmsg", "-A"]
running: false
stdout: StdioCollector {
onStreamFinished: {
try {
const newScales = {}
const lines = text.trim().split('\n')
for (const line of lines) {
const parts = line.trim().split(/\s+/)
if (parts.length >= 3 && parts[1] === "scale_factor") {
const outputName = parts[0]
const scale = parseFloat(parts[2])
if (!isNaN(scale)) {
newScales[outputName] = scale
}
}
}
outputScales = newScales
} catch (e) {
console.warn("DwlService: Failed to parse mmsg output:", e)
}
}
}
onExited: exitCode => {
if (exitCode !== 0) {
console.warn("DwlService: mmsg failed with exit code:", exitCode)
}
}
}
function refreshOutputScales() {
if (!dwlAvailable) return
scaleQueryProcess.running = true
}
function getOutputScale(outputName) {
return outputScales[outputName]
}
function getVisibleTags(outputName) {
const output = getOutputState(outputName)
if (!output || !output.tags) {
return [0]
}
const visibleTags = new Set([0])
output.tags.forEach(tag => {
if (tag.state === 1 || tag.clients > 0) {
visibleTags.add(tag.tag)
}
})
return Array.from(visibleTags).sort((a, b) => a - b)
}
}

View File

@@ -19,7 +19,7 @@ Singleton {
Process {
id: getKeybinds
running: true
running: false
command: [root.scriptPath, "--path", root.hyprConfigPath]
stdout: SplitParser {
@@ -31,9 +31,22 @@ Singleton {
}
}
}
onExited: (code) => {
if (code !== 0) {
console.warn("[HyprKeybindsService] Process exited with code:", code)
}
}
}
Component.onCompleted: {
getKeybinds.running = true
}
function reload() {
getKeybinds.running = true
getKeybinds.running = false
Qt.callLater(function() {
getKeybinds.running = true
})
}
}

View File

@@ -10,7 +10,6 @@ import qs.Common
Singleton {
id: root
property int refCount: 0
property bool isActive: false
property string networkStatus: "disconnected"
property string primaryConnection: ""
@@ -56,8 +55,6 @@ Singleton {
property string connectionError: ""
property bool isScanning: false
property bool autoScan: false
property bool wifiAvailable: true
property bool wifiToggling: false
property bool changingPreference: false
@@ -66,7 +63,6 @@ Singleton {
property string connectionStatus: ""
property string lastConnectionError: ""
property bool passwordDialogShouldReopen: false
property bool autoRefreshEnabled: false
property string wifiPassword: ""
property string forgetSSID: ""
@@ -109,114 +105,20 @@ Singleton {
root.userPreference = SettingsData.networkPreference
}
Component.onDestruction: {
nmStateMonitor.running = false
}
function activate() {
if (!isActive) {
isActive = true
console.info("LegacyNetworkService: Activating...")
initializeDBusMonitors()
doRefreshNetworkState()
}
}
function addRef() {
refCount++
if (refCount === 1) {
startAutoScan()
}
}
function removeRef() {
refCount = Math.max(0, refCount - 1)
if (refCount === 0) {
stopAutoScan()
}
}
function initializeDBusMonitors() {
nmStateMonitor.running = true
doRefreshNetworkState()
}
Process {
id: nmStateMonitor
command: lowPriorityCmd.concat(["gdbus", "monitor", "--system", "--dest", "org.freedesktop.NetworkManager"])
running: false
property var lastRefreshTime: 0
property int minRefreshInterval: 1000
stdout: SplitParser {
splitMarker: "\n"
onRead: line => {
const now = Date.now()
if (line.includes("PropertiesChanged") && line.includes("org.freedesktop.NetworkManager.AccessPoint")) {
if (line.includes("'Strength'") && root.activeAccessPointPath && line.includes(root.activeAccessPointPath)) {
parseSignalStrengthFromDbus(line)
}
return
}
if (line.includes("StateChanged") ||
line.includes("PrimaryConnectionChanged") ||
line.includes("WirelessEnabled") ||
(line.includes("ActiveConnection") && line.includes("State"))) {
if (now - nmStateMonitor.lastRefreshTime > nmStateMonitor.minRefreshInterval) {
nmStateMonitor.lastRefreshTime = now
refreshNetworkState()
}
}
}
}
onExited: exitCode => {
if (exitCode !== 0 && !restartTimer.running) {
console.warn("NetworkManager monitor failed, restarting in 5s")
restartTimer.start()
}
}
}
Timer {
id: restartTimer
interval: 5000
running: false
onTriggered: nmStateMonitor.running = true
}
Timer {
id: refreshDebounceTimer
interval: 100
running: false
onTriggered: doRefreshNetworkState()
}
function refreshNetworkState() {
refreshDebounceTimer.restart()
}
function parseSignalStrengthFromDbus(line) {
const strengthMatch = line.match(/'Strength': <byte (0x[0-9a-fA-F]+)>/)
if (strengthMatch) {
const hexValue = strengthMatch[1]
const strength = parseInt(hexValue, 16)
if (strength >= 0 && strength <= 100) {
root.wifiSignalStrength = strength
}
}
}
function doRefreshNetworkState() {
updatePrimaryConnection()
updateDeviceStates()
updateActiveConnections()
updateWifiState()
if (root.refCount > 0 && root.wifiEnabled) {
scanWifiNetworks()
}
}
function updatePrimaryConnection() {
@@ -333,9 +235,6 @@ Singleton {
getEthernetIP.running = true
} else {
root.ethernetIP = ""
if (root.networkStatus === "ethernet") {
updatePrimaryConnection()
}
}
}
}
@@ -907,18 +806,6 @@ Singleton {
}
}
function startAutoScan() {
root.autoScan = true
root.autoRefreshEnabled = true
if (root.wifiEnabled) {
scanWifi()
}
}
function stopAutoScan() {
root.autoScan = false
root.autoRefreshEnabled = false
}
function fetchNetworkInfo(ssid) {
root.networkInfoSSID = ssid

View File

@@ -6,6 +6,7 @@ import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import Quickshell.I3
import Quickshell.Wayland
import qs.Common
@@ -184,7 +185,16 @@ Singleton {
return
}
// Hyprland fallback
if (CompositorService.isDwl) {
DwlService.quit()
return
}
if (CompositorService.isSway) {
try { I3.dispatch("exit") } catch(_){}
return
}
Hyprland.dispatch("exit")
} else {
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionLogout])

View File

@@ -1,10 +1,4 @@
layer-rule {
match namespace="dms:blurwallpaper"
opacity 0.0
}
layer-rule {
match namespace="dms:blurwallpaper"
place-within-backdrop true
opacity 1.0
}

View File

@@ -1 +1 @@
v0.2.5
v0.3.0

View File

@@ -14,6 +14,10 @@ Item {
// This is for file browser, particularly - might want another map later for app IDs
readonly property var iconMap: ({
// --- special types ---
"folder": "\u{F024B}",
"file": "\u{F0214}",
// --- special filenames (no extension) ---
"docker": "\u{F0868}",
"makefile": "\u{F09EE}",
@@ -22,6 +26,7 @@ Item {
// --- programming languages ---
"rs": "\u{F1617}",
"dart": "\u{e798}",
"go": "\u{F07D3}",
"py": "\u{F0320}",
"js": "\u{F031E}",
@@ -72,6 +77,7 @@ Item {
"rtf": "\u{F09EE}",
"ppt": "\u{F09EE}",
"pptx": "\u{F09EE}",
"log": "\u{F09EE}",
"xls": "\u{F021C}",
"xlsx": "\u{F021C}",
@@ -104,7 +110,7 @@ Item {
})
readonly property string text: iconMap[name] || ""
readonly property string text: iconMap[name] || iconMap["file"] || ""
function getIconForFile(fileName) {
const lowerName = fileName.toLowerCase()

View File

@@ -75,17 +75,7 @@ PanelWindow {
readonly property real screenWidth: root.screen.width
readonly property real screenHeight: root.screen.height
readonly property real dpr: {
if (CompositorService.isNiri && root.screen) {
const niriScale = NiriService.displayScales[root.screen.name]
if (niriScale !== undefined) return niriScale
}
if (CompositorService.isHyprland && root.screen) {
const hyprlandMonitor = Hyprland.monitors.values.find(m => m.name === root.screen.name)
if (hyprlandMonitor?.scale !== undefined) return hyprlandMonitor.scale
}
return root.screen?.devicePixelRatio || 1
}
readonly property real dpr: CompositorService.getScreenScale(root.screen)
readonly property real alignedWidth: Theme.px(popupWidth, dpr)
readonly property real alignedHeight: Theme.px(popupHeight, dpr)

View File

@@ -57,6 +57,15 @@ PanelWindow {
WlrLayershell.exclusiveZone: 0
WlrLayershell.keyboardFocus: isVisible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
mask: Region {
item: Rectangle {
x: root.width - contentRect.width
y: 0
width: contentRect.width
height: root.height
}
}
StyledRect {
id: contentRect
layer.enabled: true

BIN
assets/mango.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

47
assets/sway.svg Normal file
View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="1589.1 -0.1 410.9 383.1" enable-background="new 1589.1 -0.1 410.9 383.1" xml:space="preserve">
<polygon fill="#FF4040" points="1794.4,383 1589.1,304.6 1589.1,273.2 1794.4,291.5 1999.9,273.2 1999.9,304.6 "/>
<path fill="#AFA137" d="M1794.4,351.4l-205.3-78.2c0,0,13.6-5.3,16.2-6.2c12.2-4.5,25-8.3,36.8-13.4c24.5-10.9,51.4-17.8,78.2-18.2
c15.4-0.2,29.8,4.5,44.8,2.2c17.4-2.9,34.1-9.4,51.6-12.9c31.6-6,62.3,2.2,91.5,13.8c30,11.8,59.2,22.3,89.3,33.8
c0.9,0.4,1.6,0.5,2.5,0.9L1794.4,351.4z"/>
<g>
<g>
<path fill="#C6ADAC" d="M1656.3,277.2c-2.5-57,55.2-87.7,97.8-111.1c37.2-20.3,83.7-46.8,74.1-96.4c-2-10.3-17.8-6-15.8,4.4
c4.4,22.7-8.5,39.9-25.8,53c-20.3,15.4-43.7,26.3-65.9,38.8c-40.8,23.2-83.1,59.9-81,111.3
C1640.5,287.7,1656.8,287.7,1656.3,277.2L1656.3,277.2z"/>
</g>
</g>
<g>
<g>
<path fill="#C6ADAC" d="M1728,183.7c56.6,11.8,116.4-5.1,158.3-45c7.6-7.3-4-18.9-11.6-11.6c-37.4,35.8-91.7,51.4-142.3,40.8
C1722,165.9,1717.6,181.5,1728,183.7L1728,183.7z"/>
</g>
</g>
<g>
<path fill="#68751C" d="M1727.8,64.8c14.9,3.6,40.1-13.6,51.4-19.1c14.5-7.1,29.6-12.5,44.3-19.1c5.6-2.5,24-8.3,24.1-16.5
c0.4-10.2-19.1-10.7-25.4-10c-12.9,1.3-25.4,5.3-37.8,8.9c-12.7,3.8-25,8.9-36.8,15.2c-8.9,4.7-21.1,13.1-26.3,21.8
c-3.3,5.3-3.1,13.1,2,16.7C1724.5,63.7,1726.2,64.4,1727.8,64.8z"/>
<path fill="#68751C" d="M1819.1,37.2c2.5-1.5,5.1-2.7,7.6-4c21.1-10,45.4-18.2,69.3-20c20.9-1.6,43.9-5.4,64.6,0.2
c10.7,2.9,22.5,13.3,16.9,23.2c-4.2,7.4-21.2,17.4-28.7,22.9c-27.4,19.4-65.7,28-100.7,29.8c-25.4,1.3-67.3-13.3-44.1-40.7
C1808,44.1,1813.3,40.5,1819.1,37.2z"/>
<path fill="#68751C" d="M1958.9,60.1c5.8,0.2,12.5,1.6,15.2,6.9c2.2,4.4,0.5,9.6-2.5,13.3c-3.3,3.6-7.6,5.6-12.2,7.4
c-22.5,9.4-46.6,15.1-71,16.7c-6.7,0.4-13.8,0.5-20.3-1.6s-12.3-7.1-14.2-13.6c-4-15.1,29-20,37.9-21.6s17.8-2.7,26.7-3.1
C1932,63.5,1945.1,59.5,1958.9,60.1z"/>
<path fill="#68751C" d="M1955.6,158.8c-8.9-22-62.4,2.2-74.6,10c-4.5,2.9-9.3,7.3-8.3,12.5c0.9,5.6,7.6,8.2,13.4,8.9
C1900.2,192,1966.1,184.8,1955.6,158.8z"/>
<path fill="#68751C" d="M1787.5,87.7c-2.2,14.5-21.8,30.5-34.9,35c-12.7,4.5-33.9-4.9-26.1-20.3c6.9-13.4,41-37.4,55.9-27.6
C1786.6,77.5,1788.2,83,1787.5,87.7z"/>
<path fill="#68751C" d="M1825.1,159.9c20.9,2.5,42.3-0.2,61.9-7.6c4.4-1.6,8.9-3.8,10.7-8.2c1.3-3.3,0.4-7.3-2.2-9.6
c-2.5-2.4-6.5-3.1-9.6-1.6c-5.8-6.4-16-5.8-24.5-4.2c-13.4,2.7-26.7,7.6-38.8,14c-4.4,2.4-9.3,6.4-8,11.1
C1815.8,157.9,1820.9,159.4,1825.1,159.9z"/>
<path fill="#68751C" d="M1849.4,143c27.6,6.2,57.9-1.8,81.5-17.1c3.1-2,6.5-5.1,6-8.9c-0.7-4.5-6.5-6-11.3-5.8
c-26.9,0.4-54.1,7.1-77.5,20.7c-2.7,1.5-5.6,4-4.9,6.9C1844,141.4,1846.9,142.5,1849.4,143z"/>
<g>
<path fill="#68751C" d="M1711.1,106.4c2.5-3.1,4.7-6.9,3.6-10.5c-5.1-20.1-32.5,6-39,12c-8.3,7.6-8,14.3,5.6,14.3
C1692.6,122,1704.4,114.5,1711.1,106.4z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -16,6 +16,8 @@ Source0: {{{ git_dir_pack }}}
BuildRequires: git-core
BuildRequires: rpkg
# For the _tmpfilesdir macro.
BuildRequires: systemd-rpm-macros
Requires: greetd
Requires: (quickshell-git or quickshell)
@@ -150,7 +152,7 @@ chmod 755 %{buildroot}%{_bindir}/dms-greeter-sync
install -Dm644 Modules/Greetd/README.md %{buildroot}%{_docdir}/dms-greeter/README.md
# Create cache directory for greeter data
install -dm750 %{buildroot}%{_localstatedir}/cache/dms-greeter
install -Dpm0644 ./systemd/tmpfiles-dms-greeter.conf %{buildroot}%{_tmpfilesdir}/dms-greeter.conf
# Create greeter home directory
install -dm755 %{buildroot}%{_sharedstatedir}/greeter
@@ -181,8 +183,7 @@ fi
%{_bindir}/dms-greeter
%{_bindir}/dms-greeter-sync
%{_datadir}/quickshell/dms-greeter/
%dir %attr(0750,greeter,greeter) %{_localstatedir}/cache/dms-greeter
%dir %attr(0755,greeter,greeter) %{_sharedstatedir}/greeter
%{_tmpfilesdir}/%{name}.conf
%pre
# Create greeter user/group if they don't exist (greetd expects this)

View File

@@ -47,7 +47,7 @@ Suggests: qt6ct
%description
DankMaterialShell (DMS) is a modern Wayland desktop shell built with Quickshell
and optimized for the niri and hyprland compositors. Features notifications,
and optimized for the niri, hyprland, sway, and dwl (MangoWC) compositors. Features notifications,
app launcher, wallpaper customization, and fully customizable with plugins.
Includes auto-theming for GTK/Qt apps with matugen, 20+ customizable widgets,

View File

@@ -508,12 +508,12 @@ Dashboard popup control with tab selection for overview, media, and weather info
**Functions:**
- `open [tab]` - Show dashboard popup with optional tab selection
- Parameters: `tab` - Optional tab to open: "" (default), "overview", "media", or "weather"
- Parameters: `tab` - Tab to open: "", "overview", "media", or "weather"
- Returns: Success/failure message
- `close` - Hide dashboard popup
- Returns: Success/failure message
- `toggle [tab]` - Toggle dashboard popup visibility with optional tab selection
- Parameters: `tab` - Optional tab to open when showing: "" (default), "overview", "media", or "weather"
- Parameters: `tab` - Tab to open when showing: "", "overview", "media", or "weather"
- Returns: Success/failure message
### Target: `dankdash`

View File

@@ -21,11 +21,11 @@ apply_qt_colors() {
local config_file="$1"
if [ -f "$config_file" ]; then
if grep -q '^\\[Appearance\\]' "$config_file"; then
if grep -q '^\[Appearance\]' "$config_file"; then
if grep -q '^custom_palette=' "$config_file"; then
sed -i 's/^custom_palette=.*/custom_palette=true/' "$config_file"
else
sed -i '/^\\[Appearance\\]/a custom_palette=true' "$config_file"
sed -i '/^\[Appearance\]/a custom_palette=true' "$config_file"
fi
if grep -q '^color_scheme_path=' "$config_file"; then

View File

@@ -0,0 +1,3 @@
# Path Mode User Group Age Argument
d /var/cache/dms-greeter 0750 greeter greeter -
d /var/lib/greeter 0755 greeter greeter -

View File

@@ -14,13 +14,13 @@
{
"term": "- Stateless System Monitoring",
"context": "- Stateless System Monitoring",
"reference": "Modules/Settings/AboutTab.qml:432",
"reference": "Modules/Settings/AboutTab.qml:517",
"comment": ""
},
{
"term": "- Support Us With a Star ⭐",
"context": "- Support Us With a Star ⭐",
"reference": "Modules/Settings/AboutTab.qml:397",
"reference": "Modules/Settings/AboutTab.qml:482",
"comment": ""
},
{
@@ -62,7 +62,7 @@
{
"term": "About",
"context": "About",
"reference": "Modules/Settings/AboutTab.qml:251, Modals/Settings/SettingsSidebar.qml:44",
"reference": "Modules/Settings/AboutTab.qml:336, Modals/Settings/SettingsSidebar.qml:44",
"comment": ""
},
{
@@ -146,7 +146,7 @@
{
"term": "Always show a minimum of 3 workspaces, even if fewer are available",
"context": "Always show a minimum of 3 workspaces, even if fewer are available",
"reference": "Modules/Settings/WidgetTweaksTab.qml:82",
"reference": "Modules/Settings/WidgetTweaksTab.qml:71",
"comment": ""
},
{
@@ -212,7 +212,7 @@
{
"term": "Apps are ordered by usage frequency, then last used, then alphabetically.",
"context": "Apps are ordered by usage frequency, then last used, then alphabetically.",
"reference": "Modules/Settings/LauncherTab.qml:608",
"reference": "Modules/Settings/LauncherTab.qml:607",
"comment": ""
},
{
@@ -404,13 +404,13 @@
{
"term": "Back",
"context": "Back",
"reference": "Modules/DankBar/Widgets/SystemTrayBar.qml:479",
"reference": "Modules/DankBar/Widgets/SystemTrayBar.qml:511",
"comment": ""
},
{
"term": "Balanced palette with focused accents (default).",
"context": "Balanced palette with focused accents (default).",
"reference": "Common/Theme.qml:216",
"reference": "Common/Theme.qml:215",
"comment": ""
},
{
@@ -512,7 +512,7 @@
{
"term": "Brightness",
"context": "Brightness",
"reference": "Modules/Settings/LauncherTab.qml:324",
"reference": "Modules/Settings/LauncherTab.qml:323",
"comment": ""
},
{
@@ -584,7 +584,7 @@
{
"term": "Choose Launcher Logo Color",
"context": "Choose Launcher Logo Color",
"reference": "Modules/Settings/LauncherTab.qml:251",
"reference": "Modules/Settings/LauncherTab.qml:250",
"comment": ""
},
{
@@ -662,7 +662,7 @@
{
"term": "Color Override",
"context": "Color Override",
"reference": "Modules/Settings/LauncherTab.qml:187",
"reference": "Modules/Settings/LauncherTab.qml:186",
"comment": ""
},
{
@@ -680,7 +680,7 @@
{
"term": "Colorful mix of bright contrasting accents.",
"context": "Colorful mix of bright contrasting accents.",
"reference": "Common/Theme.qml:240",
"reference": "Common/Theme.qml:239",
"comment": ""
},
{
@@ -731,6 +731,12 @@
"reference": "Modules/Settings/WidgetsTabSection.qml:521",
"comment": ""
},
{
"term": "Compositor",
"context": "Compositor",
"reference": "Modules/Settings/LauncherTab.qml:99",
"comment": ""
},
{
"term": "Configuration activated",
"context": "Configuration activated",
@@ -788,7 +794,7 @@
{
"term": "Contrast",
"context": "Contrast",
"reference": "Modules/Settings/LauncherTab.qml:353",
"reference": "Modules/Settings/LauncherTab.qml:352",
"comment": ""
},
{
@@ -878,7 +884,7 @@
{
"term": "Custom",
"context": "Custom",
"reference": "Modules/Settings/LauncherTab.qml:94, Modules/Settings/LauncherTab.qml:200",
"reference": "Modules/Settings/LauncherTab.qml:101, Modules/Settings/LauncherTab.qml:199",
"comment": ""
},
{
@@ -926,7 +932,7 @@
{
"term": "DMS out of date",
"context": "DMS out of date",
"reference": "Services/DMSService.qml:229",
"reference": "Services/DMSService.qml:230",
"comment": ""
},
{
@@ -980,7 +986,7 @@
{
"term": "Default",
"context": "Default",
"reference": "Modules/Settings/LauncherTab.qml:200",
"reference": "Modules/Settings/LauncherTab.qml:199",
"comment": ""
},
{
@@ -998,7 +1004,7 @@
{
"term": "Derives colors that closely match the underlying image.",
"context": "Derives colors that closely match the underlying image.",
"reference": "Common/Theme.qml:228",
"reference": "Common/Theme.qml:227",
"comment": ""
},
{
@@ -1070,7 +1076,7 @@
{
"term": "Display application icons in workspace indicators",
"context": "Display application icons in workspace indicators",
"reference": "Modules/Settings/WidgetTweaksTab.qml:93",
"reference": "Modules/Settings/WidgetTweaksTab.qml:82",
"comment": ""
},
{
@@ -1100,7 +1106,7 @@
{
"term": "Diverse palette spanning the full spectrum.",
"context": "Diverse palette spanning the full spectrum.",
"reference": "Common/Theme.qml:252",
"reference": "Common/Theme.qml:251",
"comment": ""
},
{
@@ -1136,7 +1142,7 @@
{
"term": "Donate on Ko-fi",
"context": "Donate on Ko-fi",
"reference": "Modules/Settings/AboutTab.qml:486",
"reference": "Modules/Settings/AboutTab.qml:571",
"comment": ""
},
{
@@ -1508,7 +1514,7 @@
{
"term": "Github:",
"context": "Github:",
"reference": "Modules/Settings/AboutTab.qml:370",
"reference": "Modules/Settings/AboutTab.qml:455",
"comment": ""
},
{
@@ -1580,13 +1586,13 @@
{
"term": "High-contrast palette for strong visual distinction.",
"context": "High-contrast palette for strong visual distinction.",
"reference": "Common/Theme.qml:224",
"reference": "Common/Theme.qml:223",
"comment": ""
},
{
"term": "High-fidelity palette that preserves source hues.",
"context": "High-fidelity palette that preserves source hues.",
"reference": "Common/Theme.qml:236",
"reference": "Common/Theme.qml:235",
"comment": ""
},
{
@@ -1700,7 +1706,7 @@
{
"term": "Invert on mode change",
"context": "Invert on mode change",
"reference": "Modules/Settings/LauncherTab.qml:382",
"reference": "Modules/Settings/LauncherTab.qml:381",
"comment": ""
},
{
@@ -1718,31 +1724,31 @@
{
"term": "Last launched %1",
"context": "Last launched %1",
"reference": "Modules/Settings/LauncherTab.qml:708",
"reference": "Modules/Settings/LauncherTab.qml:707",
"comment": ""
},
{
"term": "Last launched %1 day%2 ago",
"context": "Last launched %1 day%2 ago",
"reference": "Modules/Settings/LauncherTab.qml:704",
"reference": "Modules/Settings/LauncherTab.qml:703",
"comment": ""
},
{
"term": "Last launched %1 hour%2 ago",
"context": "Last launched %1 hour%2 ago",
"reference": "Modules/Settings/LauncherTab.qml:699",
"reference": "Modules/Settings/LauncherTab.qml:698",
"comment": ""
},
{
"term": "Last launched %1 minute%2 ago",
"context": "Last launched %1 minute%2 ago",
"reference": "Modules/Settings/LauncherTab.qml:694",
"reference": "Modules/Settings/LauncherTab.qml:693",
"comment": ""
},
{
"term": "Last launched just now",
"context": "Last launched just now",
"reference": "Modules/Settings/LauncherTab.qml:691",
"reference": "Modules/Settings/LauncherTab.qml:690",
"comment": ""
},
{
@@ -1760,7 +1766,7 @@
{
"term": "Launch Prefix",
"context": "Launch Prefix",
"reference": "Modules/Settings/LauncherTab.qml:433",
"reference": "Modules/Settings/LauncherTab.qml:432",
"comment": ""
},
{
@@ -1808,7 +1814,7 @@
{
"term": "Lively palette with saturated accents.",
"context": "Lively palette with saturated accents.",
"reference": "Common/Theme.qml:220",
"reference": "Common/Theme.qml:219",
"comment": ""
},
{
@@ -1916,7 +1922,7 @@
{
"term": "Max apps to show",
"context": "Max apps to show",
"reference": "Modules/Settings/WidgetTweaksTab.qml:114",
"reference": "Modules/Settings/WidgetTweaksTab.qml:103",
"comment": ""
},
{
@@ -1964,7 +1970,7 @@
{
"term": "Minimal palette built around a single hue.",
"context": "Minimal palette built around a single hue.",
"reference": "Common/Theme.qml:244",
"reference": "Common/Theme.qml:243",
"comment": ""
},
{
@@ -2006,7 +2012,7 @@
{
"term": "Muted palette with subdued, calming tones.",
"context": "Muted palette with subdued, calming tones.",
"reference": "Common/Theme.qml:248",
"reference": "Common/Theme.qml:247",
"comment": ""
},
{
@@ -2354,7 +2360,7 @@
{
"term": "Per-Monitor Workspaces",
"context": "Per-Monitor Workspaces",
"reference": "Modules/Settings/WidgetTweaksTab.qml:145",
"reference": "Modules/Settings/WidgetTweaksTab.qml:134",
"comment": ""
},
{
@@ -2450,7 +2456,7 @@
{
"term": "Plugins:",
"context": "Plugins:",
"reference": "Modules/Settings/AboutTab.qml:347",
"reference": "Modules/Settings/AboutTab.qml:432",
"comment": ""
},
{
@@ -2516,7 +2522,7 @@
{
"term": "Primary",
"context": "Primary",
"reference": "Modules/Settings/LauncherTab.qml:200",
"reference": "Modules/Settings/LauncherTab.qml:199",
"comment": ""
},
{
@@ -2588,7 +2594,7 @@
{
"term": "Recently Used Apps",
"context": "Recently Used Apps",
"reference": "Modules/Settings/LauncherTab.qml:578",
"reference": "Modules/Settings/LauncherTab.qml:577",
"comment": ""
},
{
@@ -2624,7 +2630,7 @@
{
"term": "Resources",
"context": "Resources",
"reference": "Modules/Settings/AboutTab.qml:309",
"reference": "Modules/Settings/AboutTab.qml:394",
"comment": ""
},
{
@@ -2705,12 +2711,6 @@
"reference": "Services/AppSearchService.qml:175",
"comment": ""
},
{
"term": "Scroll through windows, rather than workspaces",
"context": "Scroll through windows, rather than workspaces",
"reference": "Modules/Settings/WidgetTweaksTab.qml:71",
"comment": ""
},
{
"term": "Search file contents",
"context": "Search file contents",
@@ -2774,7 +2774,7 @@
{
"term": "Select an image file...",
"context": "Select an image file...",
"reference": "Modules/Settings/LauncherTab.qml:152",
"reference": "Modules/Settings/LauncherTab.qml:151",
"comment": ""
},
{
@@ -2855,6 +2855,12 @@
"reference": "Modals/Clipboard/ClipboardKeyboardHints.qml:9",
"comment": ""
},
{
"term": "Show All Tags",
"context": "Show All Tags",
"reference": "Modules/Settings/WidgetTweaksTab.qml:144",
"comment": ""
},
{
"term": "Show Confirmation on Power Actions",
"context": "Show Confirmation on Power Actions",
@@ -2882,7 +2888,13 @@
{
"term": "Show Workspace Apps",
"context": "Show Workspace Apps",
"reference": "Modules/Settings/WidgetTweaksTab.qml:92",
"reference": "Modules/Settings/WidgetTweaksTab.qml:81",
"comment": ""
},
{
"term": "Show all 9 tags instead of only occupied tags (DWL only)",
"context": "Show all 9 tags instead of only occupied tags (DWL only)",
"reference": "Modules/Settings/WidgetTweaksTab.qml:145",
"comment": ""
},
{
@@ -2918,7 +2930,7 @@
{
"term": "Show only workspaces belonging to each specific monitor.",
"context": "Show only workspaces belonging to each specific monitor.",
"reference": "Modules/Settings/WidgetTweaksTab.qml:146",
"reference": "Modules/Settings/WidgetTweaksTab.qml:135",
"comment": ""
},
{
@@ -2978,13 +2990,13 @@
{
"term": "Size Offset",
"context": "Size Offset",
"reference": "Modules/Settings/LauncherTab.qml:273",
"reference": "Modules/Settings/LauncherTab.qml:272",
"comment": ""
},
{
"term": "Sort Alphabetically",
"context": "Sort Alphabetically",
"reference": "Modules/Settings/LauncherTab.qml:488",
"reference": "Modules/Settings/LauncherTab.qml:487",
"comment": ""
},
{
@@ -3038,13 +3050,13 @@
{
"term": "Support Development",
"context": "Support Development",
"reference": "Modules/Settings/AboutTab.qml:471",
"reference": "Modules/Settings/AboutTab.qml:556",
"comment": ""
},
{
"term": "Surface",
"context": "Surface",
"reference": "Modules/Settings/LauncherTab.qml:200",
"reference": "Modules/Settings/LauncherTab.qml:199",
"comment": ""
},
{
@@ -3110,7 +3122,7 @@
{
"term": "System Monitoring:",
"context": "System Monitoring:",
"reference": "Modules/Settings/AboutTab.qml:405",
"reference": "Modules/Settings/AboutTab.qml:490",
"comment": ""
},
{
@@ -3254,7 +3266,7 @@
{
"term": "To update, run the following command:",
"context": "To update, run the following command:",
"reference": "Services/DMSService.qml:230",
"reference": "Services/DMSService.qml:231",
"comment": ""
},
{
@@ -3482,7 +3494,7 @@
{
"term": "Vibrant palette with playful saturation.",
"context": "Vibrant palette with playful saturation.",
"reference": "Common/Theme.qml:232",
"reference": "Common/Theme.qml:231",
"comment": ""
},
{
@@ -3554,13 +3566,13 @@
{
"term": "Website:",
"context": "Website:",
"reference": "Modules/Settings/AboutTab.qml:324",
"reference": "Modules/Settings/AboutTab.qml:409",
"comment": ""
},
{
"term": "When enabled, apps are sorted alphabetically. When disabled, apps are sorted by usage frequency.",
"context": "When enabled, apps are sorted alphabetically. When disabled, apps are sorted by usage frequency.",
"reference": "Modules/Settings/LauncherTab.qml:517",
"reference": "Modules/Settings/LauncherTab.qml:516",
"comment": ""
},
{
@@ -3605,12 +3617,6 @@
"reference": "Modules/DankDash/WeatherTab.qml:354, Modules/Settings/TimeWeatherTab.qml:1181",
"comment": ""
},
{
"term": "Window Scrolling",
"context": "Window Scrolling",
"reference": "Modules/Settings/WidgetTweaksTab.qml:70",
"comment": ""
},
{
"term": "Workspace",
"context": "Workspace",
@@ -3626,7 +3632,7 @@
{
"term": "Workspace Padding",
"context": "Workspace Padding",
"reference": "Modules/Settings/WidgetTweaksTab.qml:81",
"reference": "Modules/Settings/WidgetTweaksTab.qml:70",
"comment": ""
},
{

View File

@@ -368,6 +368,9 @@
"Compact Mode": {
"Compact Mode": "Modo Compatto"
},
"Compositor": {
"Compositor": ""
},
"Compositor:": {
"Compositor:": "Compositor:"
},
@@ -1454,6 +1457,9 @@
"Shift+Del: Clear All • Esc: Close": {
"Shift+Del: Clear All • Esc: Close": "Shift+Del: Pulisce Tutto • Esc: Chiude"
},
"Show All Tags": {
"Show All Tags": ""
},
"Show Confirmation on Power Actions": {
"Show Confirmation on Power Actions": "Chiedi Conferma per Azione Engertiche"
},
@@ -1469,6 +1475,9 @@
"Show Workspace Apps": {
"Show Workspace Apps": "Mostra Apps Workspace"
},
"Show all 9 tags instead of only occupied tags (DWL only)": {
"Show all 9 tags instead of only occupied tags (DWL only)": ""
},
"Show on Last Display": {
"Show on Last Display": "Mostra sull'ultimo display"
},

View File

@@ -368,6 +368,9 @@
"Compact Mode": {
"Compact Mode": "コンパクトモード"
},
"Compositor": {
"Compositor": "コンポジター"
},
"Compositor:": {
"Compositor:": "コンポジター:"
},
@@ -1454,6 +1457,9 @@
"Shift+Del: Clear All • Esc: Close": {
"Shift+Del: Clear All • Esc: Close": "Shift+Del: すべてクリア • Esc: 閉じる"
},
"Show All Tags": {
"Show All Tags": "すべてのタグを表示"
},
"Show Confirmation on Power Actions": {
"Show Confirmation on Power Actions": "電源アクションの確認を表示"
},
@@ -1469,6 +1475,9 @@
"Show Workspace Apps": {
"Show Workspace Apps": "ワークスペースアプリを表示"
},
"Show all 9 tags instead of only occupied tags (DWL only)": {
"Show all 9 tags instead of only occupied tags (DWL only)": "占有タグのみではなく、9 つのタグをすべて表示 (DWL のみ)"
},
"Show on Last Display": {
"Show on Last Display": "最後のディスプレイに表示"
},

View File

@@ -368,6 +368,9 @@
"Compact Mode": {
"Compact Mode": "Modo Compacto"
},
"Compositor": {
"Compositor": ""
},
"Compositor:": {
"Compositor:": "Compositor:"
},
@@ -1454,6 +1457,9 @@
"Shift+Del: Clear All • Esc: Close": {
"Shift+Del: Clear All • Esc: Close": "Shift+Del: Apagar tudo • Esc: Fechar"
},
"Show All Tags": {
"Show All Tags": ""
},
"Show Confirmation on Power Actions": {
"Show Confirmation on Power Actions": "Mostrar Confirmação em Ações de Energia"
},
@@ -1469,6 +1475,9 @@
"Show Workspace Apps": {
"Show Workspace Apps": "Mostrar Aplicativos da Área de Trabalho Virtual"
},
"Show all 9 tags instead of only occupied tags (DWL only)": {
"Show all 9 tags instead of only occupied tags (DWL only)": ""
},
"Show on Last Display": {
"Show on Last Display": ""
},

View File

@@ -368,6 +368,9 @@
"Compact Mode": {
"Compact Mode": "Kompakt Mod"
},
"Compositor": {
"Compositor": "Kompozitör"
},
"Compositor:": {
"Compositor:": "Kompozitör:"
},
@@ -612,7 +615,7 @@
"Enable WiFi": "WiFi Etkinleştir"
},
"Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.": {
"Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.": "Compositor tarafından hedeflenebilir bulanıklık katmanını etkinleştir (isim alanı: dms:blurwallpaper). Manuel Niri yapılandırması gerektirir."
"Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.": "Kompozitör tarafından hedeflenebilir bulanıklık katmanını etkinleştir (isim alanı: dms:blurwallpaper). Manuel Niri yapılandırması gerektirir."
},
"Enable fingerprint authentication": {
"Enable fingerprint authentication": "Parmak izi kimlik doğrulamasını etkinleştir"
@@ -1454,6 +1457,9 @@
"Shift+Del: Clear All • Esc: Close": {
"Shift+Del: Clear All • Esc: Close": "Shift+Del: Tümünü Temizle • Esc: Kapat"
},
"Show All Tags": {
"Show All Tags": ""
},
"Show Confirmation on Power Actions": {
"Show Confirmation on Power Actions": "Güç Eylemlerinde Onay Göster"
},
@@ -1469,6 +1475,9 @@
"Show Workspace Apps": {
"Show Workspace Apps": "Çalışma Alanı Uygulamalarını Göster"
},
"Show all 9 tags instead of only occupied tags (DWL only)": {
"Show all 9 tags instead of only occupied tags (DWL only)": ""
},
"Show on Last Display": {
"Show on Last Display": "Son Ekranda Göster"
},

View File

@@ -368,6 +368,9 @@
"Compact Mode": {
"Compact Mode": "紧凑模式"
},
"Compositor": {
"Compositor": "合成器"
},
"Compositor:": {
"Compositor:": "合成器:"
},
@@ -1454,6 +1457,9 @@
"Shift+Del: Clear All • Esc: Close": {
"Shift+Del: Clear All • Esc: Close": "Shift+Del: 清空 • Esc: 关闭"
},
"Show All Tags": {
"Show All Tags": "显示所有标签"
},
"Show Confirmation on Power Actions": {
"Show Confirmation on Power Actions": "显示电源操作确认弹窗"
},
@@ -1469,6 +1475,9 @@
"Show Workspace Apps": {
"Show Workspace Apps": "显示工作区内应用"
},
"Show all 9 tags instead of only occupied tags (DWL only)": {
"Show all 9 tags instead of only occupied tags (DWL only)": "显示所有 9 个标签,而非仅占用的标签(仅限 DWL"
},
"Show on Last Display": {
"Show on Last Display": "上一个显示器"
},

View File

@@ -9,22 +9,22 @@
"- Stateless System Monitoring": "- 無狀態系統監控"
},
"- Support Us With a Star ⭐": {
"- Support Us With a Star ⭐": "- 用星星 ⭐ 支持我們"
"- Support Us With a Star ⭐": "- 給我們點個 Star支持我們"
},
"1 event": {
"1 event": "1 個活動"
},
"24-Hour Format": {
"24-Hour Format": "24小時制"
"24-Hour Format": "24 小時制"
},
"24-hour format": {
"24-hour format": "24小時制"
"24-hour format": "24 小時制"
},
"3rd party": {
"3rd party": "第三方"
},
"7-Day Forecast": {
"7-Day Forecast": "七天氣預報"
"7-Day Forecast": "七天氣預報"
},
"A file with this name already exists. Do you want to overwrite it?": {
"A file with this name already exists. Do you want to overwrite it?": "檔案名稱已存在。是否要覆蓋它?"
@@ -69,7 +69,7 @@
"Alt+←/Backspace: Back • F1/I: File Info • F10: Help • Esc: Close": "Alt+←/Backspace: 返回 • F1/I: 檔案資訊 • F10: 幫助 • Esc: 關閉"
},
"Always Show OSD Percentage": {
"Always Show OSD Percentage": "始終顯示 OSD 百分比"
"Always Show OSD Percentage": "OSD 始終顯示百分比"
},
"Always show a minimum of 3 workspaces, even if fewer are available": {
"Always show a minimum of 3 workspaces, even if fewer are available": "始終顯示至少 3 個工作區,即使可用的工作區較少"
@@ -93,16 +93,16 @@
"Apply": "套用"
},
"Apply GTK Colors": {
"Apply GTK Colors": "套用 GTK 色"
"Apply GTK Colors": "套用 GTK 色"
},
"Apply Qt Colors": {
"Apply Qt Colors": "套用 Qt 色"
"Apply Qt Colors": "套用 Qt 色"
},
"Apply warm color temperature to reduce eye strain. Use automation settings below to control when it activates.": {
"Apply warm color temperature to reduce eye strain. Use automation settings below to control when it activates.": "應用暖色溫以減輕眼睛疲勞。使用下方的自動化設定來控制其啟動時間。"
},
"Apps Icon": {
"Apps Icon": "App 圖示"
"Apps Icon": "啟動器圖示"
},
"Apps are ordered by usage frequency, then last used, then alphabetically.": {
"Apps are ordered by usage frequency, then last used, then alphabetically.": "應用程式按使用頻率、最後使用時間、字母順序排列。"
@@ -168,22 +168,22 @@
"Automatic Control": "自動控制"
},
"Automatic Cycling": {
"Automatic Cycling": "自動循環"
"Automatic Cycling": "桌布自動輪替"
},
"Automatically calculate popup distance from bar edge.": {
"Automatically calculate popup distance from bar edge.": "自動計算彈出框與資訊欄的距離。"
},
"Automatically cycle through wallpapers in the same folder": {
"Automatically cycle through wallpapers in the same folder": "自動循環更換同一資料夾中的桌布"
"Automatically cycle through wallpapers in the same folder": "自動輪替更換同一資料夾中的桌布"
},
"Automatically detect location based on IP address": {
"Automatically detect location based on IP address": ""
"Automatically detect location based on IP address": "根據IP位址自動偵測位置"
},
"Automatically determine your location using your IP address": {
"Automatically determine your location using your IP address": "使用您的 IP 位址自動確定您的位置"
},
"Automatically extract colors from wallpaper": {
"Automatically extract colors from wallpaper": "自動從壁紙中提取顏色"
"Automatically extract colors from wallpaper": "自動從桌布中提取顏色"
},
"Automatically hide the top bar to expand screen real estate": {
"Automatically hide the top bar to expand screen real estate": "自動隱藏頂部欄以擴大螢幕空間"
@@ -207,7 +207,7 @@
"Back": "返回"
},
"Balanced palette with focused accents (default).": {
"Balanced palette with focused accents (default).": ""
"Balanced palette with focused accents (default).": "顏色平衡且帶有重點點綴的調色板 (預設)。"
},
"Battery": {
"Battery": "電池"
@@ -216,7 +216,7 @@
"Battery level and power management": "電量與電源管理"
},
"Battery not detected - only AC power settings available": {
"Battery not detected - only AC power settings available": "未偵測到電池 - 僅 AC 電源設定可用"
"Battery not detected - only AC power settings available": "未偵測到電池 - 僅提供交流電源設定"
},
"Bind lock screen to dbus signals from loginctl. Disable if using an external lock screen": {
"Bind lock screen to dbus signals from loginctl. Disable if using an external lock screen": "將鎖定畫面綁定到 loginctl 的 dbus 訊號。如果使用外部鎖屏,請停用"
@@ -234,7 +234,7 @@
"Blur on Overview": "模糊概覽"
},
"Blur wallpaper when niri overview is open": {
"Blur wallpaper when niri overview is open": "當 niri 概覽打開時模糊壁紙"
"Blur wallpaper when niri overview is open": "當 niri 概覽打開時模糊桌布"
},
"Border": {
"Border": "邊框"
@@ -255,7 +255,7 @@
"Bottom Section": "下方區塊"
},
"Bottom dock for pinned and running applications": {
"Bottom dock for pinned and running applications": ""
"Bottom dock for pinned and running applications": "底部 Dock 用於固定和正在運行的應用程式"
},
"Brightness": {
"Brightness": "亮度"
@@ -333,16 +333,16 @@
"Close": "關閉"
},
"Color Override": {
"Color Override": "色覆蓋"
"Color Override": "色覆蓋"
},
"Color Picker": {
"Color Picker": "色選擇器"
"Color Picker": "色選擇器"
},
"Color temperature for night mode": {
"Color temperature for night mode": "夜晚模式色溫"
},
"Colorful mix of bright contrasting accents.": {
"Colorful mix of bright contrasting accents.": "明亮對比的色彩組合。"
"Colorful mix of bright contrasting accents.": "明亮對比點綴的繽紛組合。"
},
"Command or script to run instead of the standard hibernate procedure": {
"Command or script to run instead of the standard hibernate procedure": "代替標準休眠的指令或腳本"
@@ -368,6 +368,9 @@
"Compact Mode": {
"Compact Mode": "緊湊模式"
},
"Compositor": {
"Compositor": "合成器"
},
"Compositor:": {
"Compositor:": "合成器:"
},
@@ -489,7 +492,7 @@
"Dank Bar Widget Transparency": "Dank Bar 部件透明度"
},
"Dank Suite:": {
"Dank Suite:": ""
"Dank Suite:": "Dank 套件:"
},
"DankBar Font Scale": {
"DankBar Font Scale": "Dank Bar 字體縮放"
@@ -510,7 +513,7 @@
"Del: Clear • Shift+Del: Clear All • 1-9: Actions • F10: Help • Esc: Close": "Del: 清除 • Shift+Del: 清除所有 • 1-9: 動作 • F10: 幫助 • Esc: 關閉"
},
"Derives colors that closely match the underlying image.": {
"Derives colors that closely match the underlying image.": ""
"Derives colors that closely match the underlying image.": "提取與底層圖像高度匹配的顏色。"
},
"Desktop background images": {
"Desktop background images": "桌面背景圖片"
@@ -537,7 +540,7 @@
"Disk Usage": "硬碟使用率"
},
"Dismiss": {
"Dismiss": ""
"Dismiss": "忽略"
},
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": {
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": "顯示一個帶有固定和正在運行的應用程式的 Dock這些應用程式可以放置在螢幕的頂部、底部、左側或右側邊緣"
@@ -561,7 +564,7 @@
"Displays the active keyboard layout and allows switching": "顯示目前鍵盤布局且允許切換"
},
"Diverse palette spanning the full spectrum.": {
"Diverse palette spanning the full spectrum.": ""
"Diverse palette spanning the full spectrum.": "涵蓋整個光譜的多元調色板。"
},
"Do Not Disturb": {
"Do Not Disturb": "請勿打擾"
@@ -582,7 +585,7 @@
"Donate on Ko-fi": "在 Ko-fi 上捐款"
},
"Drag widgets to reorder within sections. Use the eye icon to hide/show widgets (maintains spacing), or X to remove them completely.": {
"Drag widgets to reorder within sections. Use the eye icon to hide/show widgets (maintains spacing), or X to remove them completely.": "拖曳部件即可在版塊內重新排序。使用眼睛圖示隱藏/顯示部件保持間距,或使用 X 將其完全移除。"
"Drag widgets to reorder within sections. Use the eye icon to hide/show widgets (maintains spacing), or X to remove them completely.": "拖曳部件即可在版塊內重新排序。使用眼睛圖示隱藏/顯示部件 (會保持間距),或使用 X 將其完全移除。"
},
"Duration": {
"Duration": "持續時間"
@@ -618,7 +621,7 @@
"Enable fingerprint authentication": "啟用指紋驗證"
},
"Enable loginctl lock integration": {
"Enable loginctl lock integration": "啟用 loginctl lock 集成"
"Enable loginctl lock integration": "啟用 loginctl 鎖定整合"
},
"End": {
"End": "結束"
@@ -654,7 +657,7 @@
"Enter password for ": "輸入密碼 "
},
"Exclusive Zone Offset": {
"Exclusive Zone Offset": "專屬區域偏移"
"Exclusive Zone Offset": "獨佔區域偏移"
},
"Execute templates from ~/.config/matugen/config.toml": {
"Execute templates from ~/.config/matugen/config.toml": "從 ~/.config/matugen/config.toml 執行模板"
@@ -696,7 +699,7 @@
"Failed to start connection to ": "無法啟動連線到 "
},
"Feels Like": {
"Feels Like": "感覺像"
"Feels Like": "體感溫度"
},
"File Already Exists": {
"File Already Exists": "檔案已經存在"
@@ -714,19 +717,19 @@
"Focused Window": "視窗對焦"
},
"Font Family": {
"Font Family": "字"
"Font Family": "字"
},
"Font Scale": {
"Font Scale": "字比例"
"Font Scale": "字比例"
},
"Font Settings": {
"Font Settings": "字設定"
"Font Settings": "字設定"
},
"Font Size": {
"Font Size": "字大小"
"Font Size": "字大小"
},
"Font Weight": {
"Font Weight": "字型寬度"
"Font Weight": "字體粗細"
},
"Force Kill Process": {
"Force Kill Process": "強制結束程序"
@@ -741,7 +744,7 @@
"Forgot network ": "忘記網路 "
},
"Format Legend": {
"Format Legend": ""
"Format Legend": "格式說明"
},
"Framework:": {
"Framework:": "框架:"
@@ -765,7 +768,7 @@
"Gamma Control": "Gamma 控制"
},
"Gamma control not available. Requires DMS API v6+.": {
"Gamma control not available. Requires DMS API v6+.": ""
"Gamma control not available. Requires DMS API v6+.": "Gamma 控制不可用。需要 DMS API v6+。"
},
"Geoclue service not running - cannot auto-detect location": {
"Geoclue service not running - cannot auto-detect location": "Geoclue 服務未運作 - 無法自動偵測位置"
@@ -777,7 +780,7 @@
"Good": "好"
},
"Goth Corners": {
"Goth Corners": ""
"Goth Corners": "圓角介面融合"
},
"Graphics": {
"Graphics": "圖形"
@@ -786,13 +789,13 @@
"Group by App": "App 分組"
},
"Group multiple windows of the same app together with a window count indicator": {
"Group multiple windows of the same app together with a window count indicator": ""
"Group multiple windows of the same app together with a window count indicator": "將同一應用程式的多個視窗匯集在一起,並附帶視窗數量指示器"
},
"Health": {
"Health": "健康狀態"
},
"Height to Edge Gap (Exclusive Zone)": {
"Height to Edge Gap (Exclusive Zone)": ""
"Height to Edge Gap (Exclusive Zone)": "邊緣間隙高度 (獨佔區域)"
},
"Hex:": {
"Hex:": "色碼:"
@@ -816,7 +819,7 @@
"Hour": "小時"
},
"How often to change wallpaper": {
"How often to change wallpaper": "多久更換一次壁紙"
"How often to change wallpaper": "多久更換一次桌布"
},
"Humidity": {
"Humidity": "濕度"
@@ -831,7 +834,7 @@
"Icon Theme": "圖示主題"
},
"Idle Inhibitor": {
"Idle Inhibitor": ""
"Idle Inhibitor": "空閒抑制器"
},
"Idle Settings": {
"Idle Settings": "閒置設定"
@@ -849,7 +852,7 @@
"Incorrect password": "密碼錯誤"
},
"Indicator Style": {
"Indicator Style": ""
"Indicator Style": "指示樣式"
},
"Individual Batteries": {
"Individual Batteries": "獨立電池"
@@ -918,7 +921,7 @@
"Left Section": "左方區塊"
},
"Light Mode": {
"Light Mode": "白天模式"
"Light Mode": "淺色主題"
},
"Lines: %1": {
"Lines: %1": "行數:"
@@ -966,16 +969,16 @@
"Manual Show/Hide": "手動顯示/隱藏"
},
"Material Colors": {
"Material Colors": "手動調整色"
"Material Colors": "手動調整色"
},
"Matugen Palette": {
"Matugen Palette": ""
"Matugen Palette": "Matugen 調色板"
},
"Matugen Settings": {
"Matugen Settings": "Matugen 設定"
},
"Matugen Target Monitor": {
"Matugen Target Monitor": ""
"Matugen Target Monitor": "Matugen 目標監視器"
},
"Max apps to show": {
"Max apps to show": "最多顯示App"
@@ -1017,7 +1020,7 @@
"Monitor Selection:": "螢幕選擇:"
},
"Monitor whose wallpaper drives dynamic theming colors": {
"Monitor whose wallpaper drives dynamic theming colors": ""
"Monitor whose wallpaper drives dynamic theming colors": "系統介面顏色依據哪一個螢幕上的桌布來決定"
},
"Monospace Font": {
"Monospace Font": "等寬字體"
@@ -1074,7 +1077,7 @@
"No Active Players": "無播放器"
},
"No Background": {
"No Background": "無背景"
"No Background": "部件無背景"
},
"No Bluetooth adapter found": {
"No Bluetooth adapter found": "未找到藍芽適配器"
@@ -1113,7 +1116,7 @@
"Notepad": "筆記本"
},
"Notepad Font Settings": {
"Notepad Font Settings": "筆記本字設定"
"Notepad Font Settings": "筆記本字設定"
},
"Notepad Slideout": {
"Notepad Slideout": "記事本滑出"
@@ -1146,7 +1149,7 @@
"Numbers": "數字"
},
"OS Logo": {
"OS Logo": "發行版Logo"
"OS Logo": "發行版 Logo"
},
"Office": {
"Office": "辦公"
@@ -1194,10 +1197,10 @@
"Password": "密碼"
},
"Per-Mode Wallpapers": {
"Per-Mode Wallpapers": "每種模式的桌布"
"Per-Mode Wallpapers": "以主題區分桌布"
},
"Per-Monitor Wallpapers": {
"Per-Monitor Wallpapers": "每台螢幕的桌布"
"Per-Monitor Wallpapers": "以螢幕區分桌布"
},
"Per-Monitor Workspaces": {
"Per-Monitor Workspaces": "每台螢幕的工作區"
@@ -1272,10 +1275,10 @@
"Power Options": "電源選項"
},
"Power Profile Degradation": {
"Power Profile Degradation": ""
"Power Profile Degradation": "電源配置降級"
},
"Pressure": {
"Pressure": "壓"
"Pressure": "壓"
},
"Prevent screen timeout": {
"Prevent screen timeout": "防止螢幕超時"
@@ -1284,7 +1287,7 @@
"Primary": "主要"
},
"Privacy Indicator": {
"Privacy Indicator": ""
"Privacy Indicator": "隱私指示器"
},
"Process": {
"Process": "程序"
@@ -1404,10 +1407,10 @@
"Select Launcher Logo": "選擇啟動器 Logo"
},
"Select a color from the palette or use custom sliders": {
"Select a color from the palette or use custom sliders": ""
"Select a color from the palette or use custom sliders": "從調色板中選取一個顏色,或使用滑條調整"
},
"Select a preset or drag the slider to customize": {
"Select a preset or drag the slider to customize": ""
"Select a preset or drag the slider to customize": "選擇上方預設選項或拉動滑條調整"
},
"Select a widget to add to the ": {
"Select a widget to add to the ": "選擇一個部件加到 "
@@ -1416,13 +1419,13 @@
"Select an image file...": "選擇一張圖片..."
},
"Select font weight": {
"Select font weight": "選擇字型寬度"
"Select font weight": "選擇字體粗細"
},
"Select monitor to configure wallpaper": {
"Select monitor to configure wallpaper": "選擇指定螢幕桌布"
},
"Select monospace font for process list and technical displays": {
"Select monospace font for process list and technical displays": ""
"Select monospace font for process list and technical displays": "寬字體用於處理程序列表和技術顯示"
},
"Select system font family": {
"Select system font family": "選擇系統字體"
@@ -1431,7 +1434,7 @@
"Select system sound theme": "選擇系統音效主題"
},
"Select the palette algorithm used for wallpaper-based colors": {
"Select the palette algorithm used for wallpaper-based colors": ""
"Select the palette algorithm used for wallpaper-based colors": "請選擇調色板演算法,以桌布的顏色為基底。"
},
"Select which transitions to include in randomization": {
"Select which transitions to include in randomization": "選擇要包含在隨機中的轉換效果"
@@ -1446,7 +1449,7 @@
"Set different wallpapers for each connected monitor": "為每個連接的螢幕設定不同的桌布"
},
"Set different wallpapers for light and dark mode": {
"Set different wallpapers for light and dark mode": "為明暗模式設定不同的桌布"
"Set different wallpapers for light and dark mode": "為淺色/深色主題設定不同的桌布"
},
"Settings": {
"Settings": "設定"
@@ -1454,6 +1457,9 @@
"Shift+Del: Clear All • Esc: Close": {
"Shift+Del: Clear All • Esc: Close": "Shift+Del: 清除所有 • Esc: 關閉"
},
"Show All Tags": {
"Show All Tags": "顯示所有標籤"
},
"Show Confirmation on Power Actions": {
"Show Confirmation on Power Actions": "顯示電源操作確認"
},
@@ -1461,7 +1467,7 @@
"Show Dock": "顯示 Dock"
},
"Show Line Numbers": {
"Show Line Numbers": ""
"Show Line Numbers": "顯示行數"
},
"Show Power Actions": {
"Show Power Actions": "顯示電源選項"
@@ -1469,6 +1475,9 @@
"Show Workspace Apps": {
"Show Workspace Apps": "顯示工作區應用程式"
},
"Show all 9 tags instead of only occupied tags (DWL only)": {
"Show all 9 tags instead of only occupied tags (DWL only)": "顯示所有 9 個標籤,而非僅顯示已佔用的標籤 (僅限 DWL)"
},
"Show on Last Display": {
"Show on Last Display": "在最後顯示幕上顯示"
},
@@ -1524,7 +1533,7 @@
"Sound Theme": "音效主題"
},
"Spacer": {
"Spacer": ""
"Spacer": "空白間隔"
},
"Spacing": {
"Spacing": "間距"
@@ -1563,7 +1572,7 @@
"Switch User": "切換使用者"
},
"Sync Mode with Portal": {
"Sync Mode with Portal": ""
"Sync Mode with Portal": "透過 Portal 同步主題模式"
},
"Sync dark mode with settings portals for system-wide theme hints": {
"Sync dark mode with settings portals for system-wide theme hints": "將暗模式與設定入口網站同步以取得系統範圍的主題提示"
@@ -1584,7 +1593,7 @@
"System Monitoring:": "系統監視器:"
},
"System Tray": {
"System Tray": "系統任務欄"
"System Tray": "系統"
},
"System Update": {
"System Update": "系統更新"
@@ -1599,19 +1608,19 @@
"System bar with widgets and system information": "帶有部件和系統資訊的系統欄"
},
"System notification area icons": {
"System notification area icons": "系統通知區域圖示"
"System notification area icons": "顯示常駐程式狀態圖示和系統通知"
},
"System toast notifications": {
"System toast notifications": ""
"System toast notifications": "系統快顯通知"
},
"System tray icons": {
"System tray icons": "系統任務欄圖示"
"System tray icons": "系統圖示"
},
"System update custom command": {
"System update custom command": "自訂系統更新指令"
},
"Tab/Shift+Tab: Nav • ←→↑↓: Grid Nav • Enter/Space: Select": {
"Tab/Shift+Tab: Nav • ←→↑↓: Grid Nav • Enter/Space: Select": ""
"Tab/Shift+Tab: Nav • ←→↑↓: Grid Nav • Enter/Space: Select": "Tab/Shift+Tab: 切換焦點 • ←→↑↓: 切換焦點 • Enter/Space: 選擇"
},
"Technical Details": {
"Technical Details": "技術細節"
@@ -1629,7 +1638,7 @@
"The DMS_SOCKET environment variable is not set or the socket is unavailable. Automated plugin management requires the DMS_SOCKET.": "DMS_SOCKET 環境變數未設定或套接字不可用。自動插件管理需要 DMS_SOCKET。"
},
"The below settings will modify your GTK and Qt settings. If you wish to preserve your current configurations, please back them up (qt5ct.conf|qt6ct.conf and ~/.config/gtk-3.0|gtk-4.0).": {
"The below settings will modify your GTK and Qt settings. If you wish to preserve your current configurations, please back them up (qt5ct.conf|qt6ct.conf and ~/.config/gtk-3.0|gtk-4.0).": "以下設定將修改您的 GTK 和 Qt 設定。如果您希望保留目前配置,請備份它們(qt5ct.conf|qt6ct.conf 和 ~/.config/gtk-3.0|gtk-4.0)。"
"The below settings will modify your GTK and Qt settings. If you wish to preserve your current configurations, please back them up (qt5ct.conf|qt6ct.conf and ~/.config/gtk-3.0|gtk-4.0).": "以下設定將修改您的 GTK 和 Qt 設定。如果您希望保留目前配置,請備份 (qt5ct.conf|qt6ct.conf 和 ~/.config/gtk-3.0|gtk-4.0)。"
},
"Theme & Colors": {
"Theme & Colors": "主題和顏色"
@@ -1659,7 +1668,7 @@
"To update, run the following command:": "若要更新,請執行以下命令:"
},
"Toast Messages": {
"Toast Messages": ""
"Toast Messages": "快顯通知"
},
"Today": {
"Today": "今天"
@@ -1680,7 +1689,7 @@
"Top Section": "上方區塊"
},
"Transition Effect": {
"Transition Effect": "過渡效果"
"Transition Effect": "切換動畫效果"
},
"Turn off monitors after": {
"Turn off monitors after": "關閉螢幕之後"
@@ -1734,7 +1743,7 @@
"Use animated wave progress bars for media playback": "在媒體播放使用動畫波浪進度條"
},
"Use automatic location detection (geoclue2)": {
"Use automatic location detection (geoclue2)": ""
"Use automatic location detection (geoclue2)": "使用自動位置偵測geoclue2"
},
"Use custom command for update your system": {
"Use custom command for update your system": "使用自訂指令更新您的系統"
@@ -1773,7 +1782,7 @@
"VPN status and quick connect": "VPN 狀態和快速連接"
},
"Vibrant palette with playful saturation.": {
"Vibrant palette with playful saturation.": ""
"Vibrant palette with playful saturation.": "色彩鮮明且飽和度活潑的調色板。"
},
"Visibility": {
"Visibility": "能見度"
@@ -1788,7 +1797,7 @@
"Volume Changed": "音量改變"
},
"Volume, brightness, and other system OSDs": {
"Volume, brightness, and other system OSDs": ""
"Volume, brightness, and other system OSDs": "音量、亮度及其他系統OSD"
},
"Wallpaper": {
"Wallpaper": "桌布"
@@ -1833,7 +1842,7 @@
"Widgets": "部件"
},
"Wind": {
"Wind": "風"
"Wind": "風"
},
"Window Scrolling": {
"Window Scrolling": "視窗滾動"

View File

@@ -853,6 +853,13 @@
"reference": "",
"comment": ""
},
{
"term": "Compositor",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Configuration activated",
"translation": "",
@@ -3156,13 +3163,6 @@
"reference": "",
"comment": ""
},
{
"term": "Scroll through windows, rather than workspaces",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Search file contents",
"translation": "",
@@ -3331,6 +3331,13 @@
"reference": "",
"comment": ""
},
{
"term": "Show All Tags",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Show Confirmation on Power Actions",
"translation": "",
@@ -3366,6 +3373,13 @@
"reference": "",
"comment": ""
},
{
"term": "Show all 9 tags instead of only occupied tags (DWL only)",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Show on Last Display",
"translation": "",
@@ -4206,13 +4220,6 @@
"reference": "",
"comment": ""
},
{
"term": "Window Scrolling",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Workspace",
"translation": "",