mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-06 05:25:41 -05:00
Compare commits
95 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1406875aa | ||
|
|
6ab96898a3 | ||
|
|
7973b2f3da | ||
|
|
90284625af | ||
|
|
6b3442512a | ||
|
|
f9208136af | ||
|
|
9895fbde0d | ||
|
|
30370e4985 | ||
|
|
7a45f370b5 | ||
|
|
38068eeaac | ||
|
|
62df30ed6c | ||
|
|
7e75c9e510 | ||
|
|
42f9edf566 | ||
|
|
c72b6144a5 | ||
|
|
032777e32e | ||
|
|
607b5320fd | ||
|
|
959766b265 | ||
|
|
9774991b56 | ||
|
|
e1587995d0 | ||
|
|
08e6e22046 | ||
|
|
454d8bdc88 | ||
|
|
d0ae7431eb | ||
|
|
d88dc17b21 | ||
|
|
705f569571 | ||
|
|
abc7badfa9 | ||
|
|
e8c2469227 | ||
|
|
1e5e8cd246 | ||
|
|
adc81cfb95 | ||
|
|
e175fa64cb | ||
|
|
f9994d0e42 | ||
|
|
3e5d1c514a | ||
|
|
6310394034 | ||
|
|
f32596053b | ||
|
|
cf4a6969d3 | ||
|
|
0918412916 | ||
|
|
41b1718587 | ||
|
|
ca2acbc704 | ||
|
|
1abd3ef8b1 | ||
|
|
cedba3770c | ||
|
|
f733be1fd1 | ||
|
|
01e02232d7 | ||
|
|
771920b38b | ||
|
|
0f55bbc148 | ||
|
|
ab4e9646ad | ||
|
|
884b73599a | ||
|
|
492c0e7ef7 | ||
|
|
0865ae000b | ||
|
|
049c9b44e4 | ||
|
|
199edd3771 | ||
|
|
8806217d25 | ||
|
|
cc1588debd | ||
|
|
d2ba4b32fe | ||
|
|
b3d5054966 | ||
|
|
57a921425c | ||
|
|
061aaeb933 | ||
|
|
0c7af9c740 | ||
|
|
d5c4b990dc | ||
|
|
a650a79dfc | ||
|
|
7ac6e94348 | ||
|
|
b4abdf3d51 | ||
|
|
b59b87d84e | ||
|
|
799ae1a20e | ||
|
|
1e58e69c59 | ||
|
|
c667bab5ca | ||
|
|
2a744fb174 | ||
|
|
a9744a0cad | ||
|
|
0aab22f242 | ||
|
|
b0f65225a9 | ||
|
|
1311da7258 | ||
|
|
61d68b1f76 | ||
|
|
5dd1769536 | ||
|
|
a45a9bda9e | ||
|
|
4e43c797e2 | ||
|
|
beab1a7b01 | ||
|
|
85c00a9c4e | ||
|
|
b2e5565110 | ||
|
|
95785afec9 | ||
|
|
d9d16eccfe | ||
|
|
26900c9b62 | ||
|
|
8113ddc809 | ||
|
|
3cd6a1a558 | ||
|
|
9128141be0 | ||
|
|
0b0af20a84 | ||
|
|
225144cb46 | ||
|
|
bbe802037e | ||
|
|
1db4e92779 | ||
|
|
072883dcd4 | ||
|
|
a25e929200 | ||
|
|
6c4d27be8a | ||
|
|
8825382502 | ||
|
|
9ce3c5bd73 | ||
|
|
771346c8fa | ||
|
|
a56b2d6a9f | ||
|
|
342f980bad | ||
|
|
dbc1bdeb3b |
84
.github/workflows/copr-release.yml
vendored
84
.github/workflows/copr-release.yml
vendored
@@ -1,11 +1,10 @@
|
||||
name: DMS Copr Stable Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
release:
|
||||
types: [published]
|
||||
workflow_run:
|
||||
workflows: ["Create Release from DMS"]
|
||||
types: [completed]
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
@@ -16,7 +15,8 @@ on:
|
||||
jobs:
|
||||
build-and-upload:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
@@ -27,20 +27,14 @@ jobs:
|
||||
if [ -n "${{ github.event.inputs.version }}" ]; then
|
||||
VERSION="${{ github.event.inputs.version }}"
|
||||
echo "Using manual version: $VERSION"
|
||||
elif [ "${{ github.event_name }}" = "release" ]; then
|
||||
VERSION="${{ github.event.release.tag_name }}"
|
||||
VERSION="${VERSION#v}"
|
||||
echo "Using release version: $VERSION"
|
||||
elif [ "${{ github.event_name }}" = "push" ] && [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
||||
VERSION="${{ github.ref_name }}"
|
||||
VERSION="${VERSION#v}"
|
||||
echo "Using tag version: $VERSION"
|
||||
elif [ "${{ github.event_name }}" = "workflow_run" ]; then
|
||||
VERSION=$(curl -s https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r '.tag_name' | sed 's/^v//')
|
||||
echo "Using latest release version from workflow_run: $VERSION"
|
||||
else
|
||||
# Fallback to latest release
|
||||
VERSION=$(curl -s https://api.github.com/repos/AvengeMedia/DankMaterialShell/releases/latest | jq -r '.tag_name' | sed 's/^v//')
|
||||
VERSION=$(curl -s https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r '.tag_name' | sed 's/^v//')
|
||||
echo "Using latest release version: $VERSION"
|
||||
fi
|
||||
|
||||
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "✅ Building DMS stable version: $VERSION"
|
||||
|
||||
@@ -94,6 +88,7 @@ jobs:
|
||||
BuildRequires: wget
|
||||
|
||||
Requires: (quickshell or quickshell-git)
|
||||
Requires: accountsservice
|
||||
Requires: dms-cli
|
||||
Requires: dgop
|
||||
Requires: fira-code-fonts
|
||||
@@ -179,18 +174,59 @@ jobs:
|
||||
install -Dm755 %{_builddir}/dms-cli %{buildroot}%{_bindir}/dms
|
||||
install -Dm755 %{_builddir}/dgop %{buildroot}%{_bindir}/dgop
|
||||
|
||||
install -dm755 %{buildroot}%{_sysconfdir}/xdg/quickshell/dms
|
||||
cp -r %{_builddir}/dms-qml/* %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/
|
||||
install -dm755 %{buildroot}%{_datadir}/quickshell/dms
|
||||
cp -r %{_builddir}/dms-qml/* %{buildroot}%{_datadir}/quickshell/dms/
|
||||
|
||||
rm -rf %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/.git*
|
||||
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/.gitignore
|
||||
rm -rf %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/.github
|
||||
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/*.spec
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.git*
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms/*.spec
|
||||
|
||||
%posttrans
|
||||
# Clean up old installation path from previous versions (only if empty)
|
||||
if [ -d "%{_sysconfdir}/xdg/quickshell/dms" ]; then
|
||||
# Remove directories only if empty (preserves any user-added files)
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell/dms" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Restart DMS for active users after upgrade
|
||||
if [ "$1" -ge 2 ]; then
|
||||
# Find all quickshell DMS processes (PID and username)
|
||||
while read pid cmd; do
|
||||
username=$(ps -o user= -p "$pid" 2>/dev/null)
|
||||
|
||||
[ "$username" = "root" ] && continue
|
||||
[ -z "$username" ] && continue
|
||||
|
||||
# Get user's UID and validate session
|
||||
user_uid=$(id -u "$username" 2>/dev/null)
|
||||
[ -z "$user_uid" ] && continue
|
||||
[ ! -d "/run/user/$user_uid" ] && continue
|
||||
|
||||
wayland_display=$(tr '\0' '\n' < /proc/$pid/environ 2>/dev/null | grep '^WAYLAND_DISPLAY=' | cut -d= -f2)
|
||||
[ -z "$wayland_display" ] && continue
|
||||
|
||||
echo "Restarting DMS for user: $username"
|
||||
|
||||
# Run as user with full Wayland session environment
|
||||
runuser -u "$username" -- /bin/sh -c "
|
||||
export XDG_RUNTIME_DIR=/run/user/$user_uid
|
||||
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$user_uid/bus
|
||||
export WAYLAND_DISPLAY=$wayland_display
|
||||
export PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:\$PATH
|
||||
dms restart >/dev/null 2>&1
|
||||
" 2>/dev/null || true
|
||||
|
||||
break
|
||||
done < <(pgrep -a -f 'quickshell.*dms' 2>/dev/null)
|
||||
fi
|
||||
|
||||
%files
|
||||
%license LICENSE
|
||||
%doc README.md CONTRIBUTING.md
|
||||
%{_sysconfdir}/xdg/quickshell/dms/
|
||||
%{_datadir}/quickshell/dms/
|
||||
|
||||
%files -n dms-cli
|
||||
%{_bindir}/dms
|
||||
|
||||
1
.github/workflows/poeditor-export.yml
vendored
1
.github/workflows/poeditor-export.yml
vendored
@@ -113,6 +113,7 @@ jobs:
|
||||
"ja:translations/poexports/ja.json"
|
||||
"zh-Hans:translations/poexports/zh_CN.json"
|
||||
"pt-br:translations/poexports/pt.json"
|
||||
"tr:translations/poexports/tr.json"
|
||||
)
|
||||
|
||||
ANY_CHANGED=false
|
||||
|
||||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -7,6 +7,7 @@ on:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
actions: write
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.event.client_payload.tag }}
|
||||
@@ -48,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)" | head -50)
|
||||
CHANGELOG=$(git log --oneline --pretty=format:"- %s (%h)" --author='^(?!github-actions\[bot\])' | head -50)
|
||||
else
|
||||
CHANGELOG=$(git log --oneline --pretty=format:"- %s (%h)" "${PREVIOUS_TAG}..${TAG}")
|
||||
CHANGELOG=$(git log --oneline --pretty=format:"- %s (%h)" --author='^(?!github-actions\[bot\])' "${PREVIOUS_TAG}..${TAG}")
|
||||
fi
|
||||
|
||||
cat > RELEASE_BODY.md << 'EOF'
|
||||
@@ -216,4 +217,5 @@ jobs:
|
||||
tag_name: ${{ env.TAG }}
|
||||
files: _release_assets/**
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ Singleton {
|
||||
}
|
||||
|
||||
function migrateFromUndefinedToV1(cache) {
|
||||
console.log("CacheData: Migrating configuration from undefined to version 1")
|
||||
console.info("CacheData: Migrating configuration from undefined to version 1")
|
||||
}
|
||||
|
||||
function cleanupUnusedKeys() {
|
||||
@@ -115,7 +115,7 @@ Singleton {
|
||||
}
|
||||
onLoadFailed: error => {
|
||||
if (!isGreeterMode) {
|
||||
console.log("CacheData: No cache file found, starting fresh")
|
||||
console.info("CacheData: No cache file found, starting fresh")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ Singleton {
|
||||
try {
|
||||
root.translations = JSON.parse(text())
|
||||
root.translationsLoaded = true
|
||||
console.log(`I18n: Loaded translations for '${root.currentLocale}' ` +
|
||||
console.info(`I18n: Loaded translations for '${root.currentLocale}' ` +
|
||||
`(${Object.keys(root.translations).length} contexts)`)
|
||||
} catch (e) {
|
||||
console.warn(`I18n: Error parsing '${root.currentLocale}':`, e,
|
||||
@@ -84,7 +84,7 @@ Singleton {
|
||||
_selectedPath = fileUrl
|
||||
translationsLoaded = false
|
||||
translations = ({})
|
||||
console.log(`I18n: Using locale '${localeTag}' from ${fileUrl}`)
|
||||
console.info(`I18n: Using locale '${localeTag}' from ${fileUrl}`)
|
||||
}
|
||||
|
||||
function _fallbackToEnglish() {
|
||||
|
||||
@@ -196,7 +196,7 @@ Singleton {
|
||||
}
|
||||
|
||||
function migrateFromUndefinedToV1(settings) {
|
||||
console.log("SessionData: Migrating configuration from undefined to version 1")
|
||||
console.info("SessionData: Migrating configuration from undefined to version 1")
|
||||
if (typeof SettingsData !== "undefined") {
|
||||
if (settings.acMonitorTimeout !== undefined) {
|
||||
SettingsData.setAcMonitorTimeout(settings.acMonitorTimeout)
|
||||
@@ -707,7 +707,7 @@ Singleton {
|
||||
running: false
|
||||
onExited: exitCode => {
|
||||
if (exitCode === 0) {
|
||||
console.log("Copied default-session.json to session.json")
|
||||
console.info("Copied default-session.json to session.json")
|
||||
settingsFile.reload()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,8 @@ Singleton {
|
||||
property int animationSpeed: SettingsData.AnimationSpeed.Short
|
||||
property int customAnimationDuration: 500
|
||||
property string wallpaperFillMode: "Fill"
|
||||
property bool blurredWallpaperLayer: false
|
||||
property bool blurWallpaperOnOverview: false
|
||||
|
||||
property bool showLauncherButton: true
|
||||
property bool showWorkspaceSwitcher: true
|
||||
@@ -189,6 +191,7 @@ Singleton {
|
||||
property bool lockBeforeSuspend: false
|
||||
property bool loginctlLockIntegration: true
|
||||
property string launchPrefix: ""
|
||||
property var brightnessDevicePins: ({})
|
||||
|
||||
property bool gtkThemingEnabled: false
|
||||
property bool qtThemingEnabled: false
|
||||
@@ -202,6 +205,7 @@ Singleton {
|
||||
property real dockSpacing: 4
|
||||
property real dockBottomGap: 0
|
||||
property real dockIconSize: 40
|
||||
property string dockIndicatorStyle: "circle"
|
||||
|
||||
property bool notificationOverlayEnabled: false
|
||||
property bool dankBarAutoHide: false
|
||||
@@ -257,6 +261,7 @@ Singleton {
|
||||
property string updaterTerminalAdditionalParams: ""
|
||||
|
||||
property var screenPreferences: ({})
|
||||
property var showOnLastDisplay: ({})
|
||||
|
||||
signal forceDankBarLayoutRefresh
|
||||
signal forceDockLayoutRefresh
|
||||
@@ -318,7 +323,7 @@ Singleton {
|
||||
} else if (settings.themeIndex >= 0 && settings.themeIndex < themeNames.length) {
|
||||
currentThemeName = themeNames[settings.themeIndex]
|
||||
}
|
||||
console.log("Auto-migrated theme from index", settings.themeIndex, "to", currentThemeName)
|
||||
console.info("Auto-migrated theme from index", settings.themeIndex, "to", currentThemeName)
|
||||
} else {
|
||||
currentThemeName = settings.currentThemeName !== undefined ? settings.currentThemeName : "blue"
|
||||
}
|
||||
@@ -450,6 +455,7 @@ Singleton {
|
||||
dockSpacing = settings.dockSpacing !== undefined ? settings.dockSpacing : 4
|
||||
dockBottomGap = settings.dockBottomGap !== undefined ? settings.dockBottomGap : 0
|
||||
dockIconSize = settings.dockIconSize !== undefined ? settings.dockIconSize : 40
|
||||
dockIndicatorStyle = settings.dockIndicatorStyle !== undefined ? settings.dockIndicatorStyle : "circle"
|
||||
cornerRadius = settings.cornerRadius !== undefined ? settings.cornerRadius : 12
|
||||
notificationOverlayEnabled = settings.notificationOverlayEnabled !== undefined ? settings.notificationOverlayEnabled : false
|
||||
dankBarAutoHide = settings.dankBarAutoHide !== undefined ? settings.dankBarAutoHide : (settings.topBarAutoHide !== undefined ? settings.topBarAutoHide : false)
|
||||
@@ -491,7 +497,10 @@ Singleton {
|
||||
widgetBackgroundColor = settings.widgetBackgroundColor !== undefined ? settings.widgetBackgroundColor : "sch"
|
||||
surfaceBase = settings.surfaceBase !== undefined ? settings.surfaceBase : "s"
|
||||
screenPreferences = settings.screenPreferences !== undefined ? settings.screenPreferences : ({})
|
||||
showOnLastDisplay = settings.showOnLastDisplay !== undefined ? settings.showOnLastDisplay : ({})
|
||||
wallpaperFillMode = settings.wallpaperFillMode !== undefined ? settings.wallpaperFillMode : "Fill"
|
||||
blurredWallpaperLayer = settings.blurredWallpaperLayer !== undefined ? settings.blurredWallpaperLayer : false
|
||||
blurWallpaperOnOverview = settings.blurWallpaperOnOverview !== undefined ? settings.blurWallpaperOnOverview : false
|
||||
animationSpeed = settings.animationSpeed !== undefined ? settings.animationSpeed : SettingsData.AnimationSpeed.Short
|
||||
customAnimationDuration = settings.customAnimationDuration !== undefined ? settings.customAnimationDuration : 500
|
||||
acMonitorTimeout = settings.acMonitorTimeout !== undefined ? settings.acMonitorTimeout : 0
|
||||
@@ -505,6 +514,7 @@ Singleton {
|
||||
lockBeforeSuspend = settings.lockBeforeSuspend !== undefined ? settings.lockBeforeSuspend : false
|
||||
loginctlLockIntegration = settings.loginctlLockIntegration !== undefined ? settings.loginctlLockIntegration : true
|
||||
launchPrefix = settings.launchPrefix !== undefined ? settings.launchPrefix : ""
|
||||
brightnessDevicePins = settings.brightnessDevicePins !== undefined ? settings.brightnessDevicePins : ({})
|
||||
|
||||
if (settings.configVersion === undefined) {
|
||||
migrateFromUndefinedToV1(settings)
|
||||
@@ -631,6 +641,7 @@ Singleton {
|
||||
"dockSpacing": dockSpacing,
|
||||
"dockBottomGap": dockBottomGap,
|
||||
"dockIconSize": dockIconSize,
|
||||
"dockIndicatorStyle": dockIndicatorStyle,
|
||||
"cornerRadius": cornerRadius,
|
||||
"notificationOverlayEnabled": notificationOverlayEnabled,
|
||||
"dankBarAutoHide": dankBarAutoHide,
|
||||
@@ -656,6 +667,8 @@ Singleton {
|
||||
"widgetBackgroundColor": widgetBackgroundColor,
|
||||
"surfaceBase": surfaceBase,
|
||||
"wallpaperFillMode": wallpaperFillMode,
|
||||
"blurredWallpaperLayer": blurredWallpaperLayer,
|
||||
"blurWallpaperOnOverview": blurWallpaperOnOverview,
|
||||
"notificationTimeoutLow": notificationTimeoutLow,
|
||||
"notificationTimeoutNormal": notificationTimeoutNormal,
|
||||
"notificationTimeoutCritical": notificationTimeoutCritical,
|
||||
@@ -672,6 +685,7 @@ Singleton {
|
||||
"updaterCustomCommand": updaterCustomCommand,
|
||||
"updaterTerminalAdditionalParams": updaterTerminalAdditionalParams,
|
||||
"screenPreferences": screenPreferences,
|
||||
"showOnLastDisplay": showOnLastDisplay,
|
||||
"animationSpeed": animationSpeed,
|
||||
"customAnimationDuration": customAnimationDuration,
|
||||
"acMonitorTimeout": acMonitorTimeout,
|
||||
@@ -685,6 +699,7 @@ Singleton {
|
||||
"lockBeforeSuspend": lockBeforeSuspend,
|
||||
"loginctlLockIntegration": loginctlLockIntegration,
|
||||
"launchPrefix": launchPrefix,
|
||||
"brightnessDevicePins": brightnessDevicePins,
|
||||
"configVersion": settingsConfigVersion
|
||||
}, null, 2))
|
||||
}
|
||||
@@ -696,7 +711,7 @@ Singleton {
|
||||
}
|
||||
|
||||
function migrateFromUndefinedToV1(settings) {
|
||||
console.log("SettingsData: Migrating configuration from undefined to version 1")
|
||||
console.info("SettingsData: Migrating configuration from undefined to version 1")
|
||||
}
|
||||
|
||||
function cleanupUnusedKeys() {
|
||||
@@ -724,7 +739,7 @@ Singleton {
|
||||
"notepadTransparencyOverride", "notepadLastCustomTransparency", "soundsEnabled",
|
||||
"useSystemSoundTheme", "soundNewNotification", "soundVolumeChanged", "soundPluggedIn", "gtkThemingEnabled",
|
||||
"qtThemingEnabled", "syncModeWithPortal", "showDock", "dockAutoHide", "dockGroupByApp",
|
||||
"dockOpenOnOverview", "dockPosition", "dockSpacing", "dockBottomGap", "dockIconSize",
|
||||
"dockOpenOnOverview", "dockPosition", "dockSpacing", "dockBottomGap", "dockIconSize", "dockIndicatorStyle",
|
||||
"cornerRadius", "notificationOverlayEnabled", "dankBarAutoHide",
|
||||
"dankBarOpenOnOverview", "dankBarVisible", "dankBarSpacing", "dankBarBottomGap",
|
||||
"dankBarInnerPadding", "dankBarSquareCorners", "dankBarNoBackground",
|
||||
@@ -732,15 +747,15 @@ Singleton {
|
||||
"dankBarBorderOpacity", "dankBarBorderThickness", "popupGapsAuto", "popupGapsManual",
|
||||
"dankBarPosition", "lockScreenShowPowerActions", "enableFprint", "maxFprintTries",
|
||||
"hideBrightnessSlider", "widgetBackgroundColor", "surfaceBase", "wallpaperFillMode",
|
||||
"notificationTimeoutLow", "notificationTimeoutNormal", "notificationTimeoutCritical",
|
||||
"blurredWallpaperLayer", "blurWallpaperOnOverview", "notificationTimeoutLow", "notificationTimeoutNormal", "notificationTimeoutCritical",
|
||||
"notificationPopupPosition", "osdAlwaysShowValue", "powerActionConfirm",
|
||||
"customPowerActionLock", "customPowerActionLogout", "customPowerActionSuspend",
|
||||
"customPowerActionHibernate", "customPowerActionReboot", "customPowerActionPowerOff",
|
||||
"updaterUseCustomCommand", "updaterCustomCommand", "updaterTerminalAdditionalParams",
|
||||
"screenPreferences", "animationSpeed", "customAnimationDuration", "acMonitorTimeout", "acLockTimeout",
|
||||
"screenPreferences", "showOnLastDisplay", "animationSpeed", "customAnimationDuration", "acMonitorTimeout", "acLockTimeout",
|
||||
"acSuspendTimeout", "acHibernateTimeout", "batteryMonitorTimeout", "batteryLockTimeout",
|
||||
"batterySuspendTimeout", "batteryHibernateTimeout", "lockBeforeSuspend",
|
||||
"loginctlLockIntegration", "launchPrefix", "configVersion"
|
||||
"loginctlLockIntegration", "launchPrefix", "brightnessDevicePins", "configVersion"
|
||||
]
|
||||
|
||||
try {
|
||||
@@ -937,7 +952,11 @@ Singleton {
|
||||
if (prefs.includes("all")) {
|
||||
return Quickshell.screens
|
||||
}
|
||||
return Quickshell.screens.filter(screen => prefs.includes(screen.name))
|
||||
var filtered = Quickshell.screens.filter(screen => prefs.includes(screen.name))
|
||||
if (filtered.length === 0 && showOnLastDisplay && showOnLastDisplay[componentId] && Quickshell.screens.length === 1) {
|
||||
return Quickshell.screens
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
function sendTestNotifications() {
|
||||
@@ -1037,6 +1056,7 @@ Singleton {
|
||||
function setCornerRadius(radius) {
|
||||
cornerRadius = radius
|
||||
saveSettings()
|
||||
NiriService.generateNiriLayoutConfig()
|
||||
}
|
||||
|
||||
function setClockFormat(use24Hour) {
|
||||
@@ -1074,6 +1094,16 @@ Singleton {
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setBlurredWallpaperLayer(enabled) {
|
||||
blurredWallpaperLayer = enabled
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setBlurWallpaperOnOverview(enabled) {
|
||||
blurWallpaperOnOverview = enabled
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setShowLauncherButton(enabled) {
|
||||
showLauncherButton = enabled
|
||||
saveSettings()
|
||||
@@ -1607,6 +1637,11 @@ Singleton {
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setDockIndicatorStyle(style) {
|
||||
dockIndicatorStyle = style
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setNotificationOverlayEnabled(enabled) {
|
||||
notificationOverlayEnabled = enabled
|
||||
saveSettings()
|
||||
@@ -1801,6 +1836,16 @@ Singleton {
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setShowOnLastDisplay(prefs) {
|
||||
showOnLastDisplay = prefs
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setBrightnessDevicePins(pins) {
|
||||
brightnessDevicePins = pins
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function getPluginSetting(pluginId, key, defaultValue) {
|
||||
if (!pluginSettings[pluginId]) {
|
||||
return defaultValue
|
||||
@@ -1995,7 +2040,7 @@ Singleton {
|
||||
running: false
|
||||
onExited: exitCode => {
|
||||
if (exitCode === 0) {
|
||||
console.log("Copied default-settings.json to settings.json")
|
||||
console.info("Copied default-settings.json to settings.json")
|
||||
settingsFile.reload()
|
||||
} else {
|
||||
applyStoredTheme()
|
||||
|
||||
@@ -92,7 +92,7 @@ Singleton {
|
||||
}
|
||||
|
||||
if (colorsFileLoadFailed && currentTheme === dynamic && wallpaperPath) {
|
||||
console.log("Theme: Matugen now available, regenerating colors for dynamic theme")
|
||||
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"])
|
||||
@@ -701,7 +701,7 @@ Singleton {
|
||||
return
|
||||
}
|
||||
|
||||
console.log("Theme: Setting desired theme -", kind, "mode:", isLight ? "light" : "dark", "type:", matugenType)
|
||||
console.info("Theme: Setting desired theme -", kind, "mode:", isLight ? "light" : "dark", "type:", matugenType)
|
||||
|
||||
if (typeof NiriService !== "undefined" && CompositorService.isNiri) {
|
||||
NiriService.suppressNextToast()
|
||||
@@ -908,7 +908,7 @@ Singleton {
|
||||
workerRunning = false
|
||||
|
||||
if (exitCode === 0) {
|
||||
console.log("Theme: Matugen worker completed successfully")
|
||||
console.info("Theme: Matugen worker completed successfully")
|
||||
if (currentTheme === dynamic) {
|
||||
console.log("Theme: Reloading dynamic colors file")
|
||||
dynamicColorsFileView.reload()
|
||||
@@ -983,7 +983,7 @@ Singleton {
|
||||
|
||||
onLoaded: {
|
||||
if (currentTheme === dynamic) {
|
||||
console.log("Theme: Dynamic colors file loaded successfully")
|
||||
console.info("Theme: Dynamic colors file loaded successfully")
|
||||
colorsFileLoadFailed = false
|
||||
parseAndLoadColors()
|
||||
}
|
||||
@@ -997,7 +997,7 @@ Singleton {
|
||||
|
||||
onLoadFailed: function (error) {
|
||||
if (currentTheme === dynamic) {
|
||||
console.log("Theme: Dynamic colors file load failed, marking for regeneration")
|
||||
console.warn("Theme: Dynamic colors file load failed, marking for regeneration")
|
||||
colorsFileLoadFailed = true
|
||||
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode)
|
||||
if (!isGreeterMode && matugenAvailable && wallpaperPath) {
|
||||
|
||||
14
DMSShell.qml
14
DMSShell.qml
@@ -46,12 +46,20 @@ Item {
|
||||
item.popoutService = PopoutService
|
||||
}
|
||||
item.pluginId = pluginId
|
||||
console.log("Daemon plugin loaded:", pluginId)
|
||||
console.info("Daemon plugin loaded:", pluginId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: blurredWallpaperBackgroundLoader
|
||||
active: SettingsData.blurredWallpaperLayer
|
||||
asynchronous: false
|
||||
|
||||
sourceComponent: BlurredWallpaperBackground {}
|
||||
}
|
||||
|
||||
WallpaperBackground {}
|
||||
|
||||
Lock {
|
||||
@@ -208,8 +216,8 @@ Item {
|
||||
Connections {
|
||||
target: NetworkService
|
||||
|
||||
function onCredentialsNeeded(token, ssid, setting, fields, hints, reason) {
|
||||
wifiPasswordModal.showFromPrompt(token, ssid, setting, fields, hints, reason)
|
||||
function onCredentialsNeeded(token, ssid, setting, fields, hints, reason, connType, connName, vpnService) {
|
||||
wifiPasswordModal.showFromPrompt(token, ssid, setting, fields, hints, reason, connType, connName, vpnService)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
362
Modals/BluetoothPairingModal.qml
Normal file
362
Modals/BluetoothPairingModal.qml
Normal file
@@ -0,0 +1,362 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Modals.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
DankModal {
|
||||
id: root
|
||||
|
||||
property string deviceName: ""
|
||||
property string deviceAddress: ""
|
||||
property string requestType: ""
|
||||
property string token: ""
|
||||
property int passkey: 0
|
||||
property string pinInput: ""
|
||||
property string passkeyInput: ""
|
||||
|
||||
function show(pairingData) {
|
||||
token = pairingData.token || ""
|
||||
deviceName = pairingData.deviceName || ""
|
||||
deviceAddress = pairingData.deviceAddr || ""
|
||||
requestType = pairingData.requestType || ""
|
||||
passkey = pairingData.passkey || 0
|
||||
pinInput = ""
|
||||
passkeyInput = ""
|
||||
|
||||
open()
|
||||
Qt.callLater(() => {
|
||||
if (contentLoader.item) {
|
||||
if (requestType === "pin" && contentLoader.item.pinInputField) {
|
||||
contentLoader.item.pinInputField.forceActiveFocus()
|
||||
} else if (requestType === "passkey" && contentLoader.item.passkeyInputField) {
|
||||
contentLoader.item.passkeyInputField.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
shouldBeVisible: false
|
||||
width: 420
|
||||
height: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 240
|
||||
|
||||
onShouldBeVisibleChanged: () => {
|
||||
if (!shouldBeVisible) {
|
||||
pinInput = ""
|
||||
passkeyInput = ""
|
||||
}
|
||||
}
|
||||
|
||||
onOpened: {
|
||||
Qt.callLater(() => {
|
||||
if (contentLoader.item) {
|
||||
if (requestType === "pin" && contentLoader.item.pinInputField) {
|
||||
contentLoader.item.pinInputField.forceActiveFocus()
|
||||
} else if (requestType === "passkey" && contentLoader.item.passkeyInputField) {
|
||||
contentLoader.item.passkeyInputField.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onBackgroundClicked: () => {
|
||||
DMSService.bluetoothCancelPairing(token)
|
||||
close()
|
||||
pinInput = ""
|
||||
passkeyInput = ""
|
||||
}
|
||||
|
||||
content: Component {
|
||||
FocusScope {
|
||||
id: pairingContent
|
||||
|
||||
property alias pinInputField: pinInputField
|
||||
property alias passkeyInputField: passkeyInputField
|
||||
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
implicitHeight: mainColumn.implicitHeight
|
||||
|
||||
Keys.onEscapePressed: event => {
|
||||
DMSService.bluetoothCancelPairing(token)
|
||||
close()
|
||||
pinInput = ""
|
||||
passkeyInput = ""
|
||||
event.accepted = true
|
||||
}
|
||||
|
||||
Column {
|
||||
id: mainColumn
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
anchors.topMargin: Theme.spacingM
|
||||
spacing: requestType === "pin" || requestType === "passkey" ? Theme.spacingM : Theme.spacingS
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Pair Bluetooth Device")
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (requestType === "confirm")
|
||||
return I18n.tr("Confirm passkey for ") + deviceName
|
||||
if (requestType === "authorize")
|
||||
return I18n.tr("Authorize pairing with ") + deviceName
|
||||
if (requestType.startsWith("authorize-service"))
|
||||
return I18n.tr("Authorize service for ") + deviceName
|
||||
if (requestType === "pin")
|
||||
return I18n.tr("Enter PIN for ") + deviceName
|
||||
if (requestType === "passkey")
|
||||
return I18n.tr("Enter passkey for ") + deviceName
|
||||
return deviceName
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceTextMedium
|
||||
width: parent.width - 40
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceHover
|
||||
border.color: pinInputField.activeFocus ? Theme.primary : Theme.outlineStrong
|
||||
border.width: pinInputField.activeFocus ? 2 : 1
|
||||
visible: requestType === "pin"
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: () => {
|
||||
pinInputField.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: pinInputField
|
||||
|
||||
anchors.fill: parent
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
textColor: Theme.surfaceText
|
||||
text: pinInput
|
||||
placeholderText: I18n.tr("Enter PIN")
|
||||
backgroundColor: "transparent"
|
||||
enabled: root.shouldBeVisible
|
||||
onTextEdited: () => {
|
||||
pinInput = text
|
||||
}
|
||||
onAccepted: () => {
|
||||
submitPairing()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceHover
|
||||
border.color: passkeyInputField.activeFocus ? Theme.primary : Theme.outlineStrong
|
||||
border.width: passkeyInputField.activeFocus ? 2 : 1
|
||||
visible: requestType === "passkey"
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: () => {
|
||||
passkeyInputField.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: passkeyInputField
|
||||
|
||||
anchors.fill: parent
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
textColor: Theme.surfaceText
|
||||
text: passkeyInput
|
||||
placeholderText: I18n.tr("Enter 6-digit passkey")
|
||||
backgroundColor: "transparent"
|
||||
enabled: root.shouldBeVisible
|
||||
onTextEdited: () => {
|
||||
passkeyInput = text
|
||||
}
|
||||
onAccepted: () => {
|
||||
submitPairing()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 56
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHighest
|
||||
visible: requestType === "confirm"
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: 2
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Passkey:")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceTextMedium
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: String(passkey).padStart(6, "0")
|
||||
font.pixelSize: Theme.fontSizeXLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Bold
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 36
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Rectangle {
|
||||
width: Math.max(70, cancelText.contentWidth + Theme.spacingM * 2)
|
||||
height: 36
|
||||
radius: Theme.cornerRadius
|
||||
color: cancelArea.containsMouse ? Theme.surfaceTextHover : "transparent"
|
||||
border.color: Theme.surfaceVariantAlpha
|
||||
border.width: 1
|
||||
|
||||
StyledText {
|
||||
id: cancelText
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: I18n.tr("Cancel")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: cancelArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: () => {
|
||||
DMSService.bluetoothCancelPairing(token)
|
||||
close()
|
||||
pinInput = ""
|
||||
passkeyInput = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: Math.max(80, pairText.contentWidth + Theme.spacingM * 2)
|
||||
height: 36
|
||||
radius: Theme.cornerRadius
|
||||
color: pairArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
||||
enabled: {
|
||||
if (requestType === "pin")
|
||||
return pinInput.length > 0
|
||||
if (requestType === "passkey")
|
||||
return passkeyInput.length === 6
|
||||
return true
|
||||
}
|
||||
opacity: enabled ? 1 : 0.5
|
||||
|
||||
StyledText {
|
||||
id: pairText
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: {
|
||||
if (requestType === "confirm")
|
||||
return I18n.tr("Confirm")
|
||||
if (requestType === "authorize" || requestType.startsWith("authorize-service"))
|
||||
return I18n.tr("Authorize")
|
||||
return I18n.tr("Pair")
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.background
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: pairArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: parent.enabled
|
||||
onClicked: () => {
|
||||
submitPairing()
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: Theme.spacingM
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
iconName: "close"
|
||||
iconSize: Theme.iconSize - 4
|
||||
iconColor: Theme.surfaceText
|
||||
onClicked: () => {
|
||||
DMSService.bluetoothCancelPairing(token)
|
||||
close()
|
||||
pinInput = ""
|
||||
passkeyInput = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function submitPairing() {
|
||||
const secrets = {}
|
||||
|
||||
if (requestType === "pin") {
|
||||
secrets["pin"] = pinInput
|
||||
} else if (requestType === "passkey") {
|
||||
secrets["passkey"] = passkeyInput
|
||||
} else if (requestType === "confirm" || requestType === "authorize" || requestType.startsWith("authorize-service")) {
|
||||
secrets["decision"] = "yes"
|
||||
}
|
||||
|
||||
DMSService.bluetoothSubmitPairing(token, secrets, true, response => {
|
||||
if (response.error) {
|
||||
ToastService.showError(I18n.tr("Pairing failed"), response.error)
|
||||
}
|
||||
})
|
||||
|
||||
close()
|
||||
pinInput = ""
|
||||
passkeyInput = ""
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,11 @@ DankModal {
|
||||
property var promptFields: []
|
||||
property string promptSetting: ""
|
||||
|
||||
property bool isVpnPrompt: false
|
||||
property string connectionName: ""
|
||||
property string vpnServiceType: ""
|
||||
property string connectionType: ""
|
||||
|
||||
function show(ssid) {
|
||||
wifiPasswordSSID = ssid
|
||||
wifiPasswordInput = ""
|
||||
@@ -32,6 +37,10 @@ DankModal {
|
||||
promptReason = ""
|
||||
promptFields = []
|
||||
promptSetting = ""
|
||||
isVpnPrompt = false
|
||||
connectionName = ""
|
||||
vpnServiceType = ""
|
||||
connectionType = ""
|
||||
|
||||
const network = NetworkService.wifiNetworks.find(n => n.ssid === ssid)
|
||||
requiresEnterprise = network?.enterprise || false
|
||||
@@ -48,13 +57,18 @@ DankModal {
|
||||
})
|
||||
}
|
||||
|
||||
function showFromPrompt(token, ssid, setting, fields, hints, reason) {
|
||||
wifiPasswordSSID = ssid
|
||||
function showFromPrompt(token, ssid, setting, fields, hints, reason, connType, connName, vpnService) {
|
||||
isPromptMode = true
|
||||
promptToken = token
|
||||
promptReason = reason
|
||||
promptFields = fields || []
|
||||
promptSetting = setting || "802-11-wireless-security"
|
||||
connectionType = connType || "802-11-wireless"
|
||||
connectionName = connName || ssid || ""
|
||||
vpnServiceType = vpnService || ""
|
||||
|
||||
isVpnPrompt = (connectionType === "vpn" || connectionType === "wireguard")
|
||||
wifiPasswordSSID = isVpnPrompt ? connectionName : ssid
|
||||
|
||||
requiresEnterprise = setting === "802-1x"
|
||||
|
||||
@@ -163,7 +177,12 @@ DankModal {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Connect to Wi-Fi")
|
||||
text: {
|
||||
if (isVpnPrompt) {
|
||||
return I18n.tr("Connect to VPN")
|
||||
}
|
||||
return I18n.tr("Connect to Wi-Fi")
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
@@ -175,6 +194,9 @@ DankModal {
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (isVpnPrompt) {
|
||||
return I18n.tr("Enter password for ") + wifiPasswordSSID
|
||||
}
|
||||
const prefix = requiresEnterprise ? I18n.tr("Enter credentials for ") : I18n.tr("Enter password for ")
|
||||
return prefix + wifiPasswordSSID
|
||||
}
|
||||
@@ -218,7 +240,7 @@ DankModal {
|
||||
color: Theme.surfaceHover
|
||||
border.color: usernameInput.activeFocus ? Theme.primary : Theme.outlineStrong
|
||||
border.width: usernameInput.activeFocus ? 2 : 1
|
||||
visible: requiresEnterprise
|
||||
visible: requiresEnterprise && !isVpnPrompt
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
@@ -271,7 +293,7 @@ DankModal {
|
||||
textColor: Theme.surfaceText
|
||||
text: wifiPasswordInput
|
||||
echoMode: showPasswordCheckbox.checked ? TextInput.Normal : TextInput.Password
|
||||
placeholderText: requiresEnterprise ? I18n.tr("Password") : ""
|
||||
placeholderText: (requiresEnterprise && !isVpnPrompt) ? I18n.tr("Password") : ""
|
||||
backgroundColor: "transparent"
|
||||
focus: !requiresEnterprise
|
||||
enabled: root.shouldBeVisible
|
||||
@@ -281,7 +303,9 @@ DankModal {
|
||||
onAccepted: () => {
|
||||
if (isPromptMode) {
|
||||
const secrets = {}
|
||||
if (promptSetting === "802-11-wireless-security") {
|
||||
if (isVpnPrompt) {
|
||||
if (passwordInput.text) secrets["password"] = passwordInput.text
|
||||
} else if (promptSetting === "802-11-wireless-security") {
|
||||
secrets["psk"] = passwordInput.text
|
||||
} else if (promptSetting === "802-1x") {
|
||||
if (usernameInput.text) secrets["identity"] = usernameInput.text
|
||||
@@ -340,7 +364,7 @@ DankModal {
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: requiresEnterprise
|
||||
visible: requiresEnterprise && !isVpnPrompt
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
@@ -372,7 +396,7 @@ DankModal {
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: requiresEnterprise
|
||||
visible: requiresEnterprise && !isVpnPrompt
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
@@ -495,7 +519,12 @@ DankModal {
|
||||
height: 36
|
||||
radius: Theme.cornerRadius
|
||||
color: connectArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
||||
enabled: requiresEnterprise ? (usernameInput.text.length > 0 && passwordInput.text.length > 0) : passwordInput.text.length > 0
|
||||
enabled: {
|
||||
if (isVpnPrompt) {
|
||||
return passwordInput.text.length > 0
|
||||
}
|
||||
return requiresEnterprise ? (usernameInput.text.length > 0 && passwordInput.text.length > 0) : passwordInput.text.length > 0
|
||||
}
|
||||
opacity: enabled ? 1 : 0.5
|
||||
|
||||
StyledText {
|
||||
@@ -518,7 +547,9 @@ DankModal {
|
||||
onClicked: () => {
|
||||
if (isPromptMode) {
|
||||
const secrets = {}
|
||||
if (promptSetting === "802-11-wireless-security") {
|
||||
if (isVpnPrompt) {
|
||||
if (passwordInput.text) secrets["password"] = passwordInput.text
|
||||
} else if (promptSetting === "802-11-wireless-security") {
|
||||
secrets["psk"] = passwordInput.text
|
||||
} else if (promptSetting === "802-1x") {
|
||||
if (usernameInput.text) secrets["identity"] = usernameInput.text
|
||||
|
||||
135
Modules/BlurredWallpaperBackground.qml
Normal file
135
Modules/BlurredWallpaperBackground.qml
Normal file
@@ -0,0 +1,135 @@
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
import qs.Modules
|
||||
|
||||
Variants {
|
||||
model: {
|
||||
if (SessionData.isGreeterMode) {
|
||||
return Quickshell.screens
|
||||
}
|
||||
return SettingsData.getFilteredScreens("wallpaper")
|
||||
}
|
||||
|
||||
PanelWindow {
|
||||
id: blurWallpaperWindow
|
||||
|
||||
required property var modelData
|
||||
|
||||
screen: modelData
|
||||
|
||||
WlrLayershell.layer: WlrLayer.Background
|
||||
WlrLayershell.namespace: "dms:blurwallpaper"
|
||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||
|
||||
anchors.top: true
|
||||
anchors.bottom: true
|
||||
anchors.left: true
|
||||
anchors.right: true
|
||||
|
||||
color: "transparent"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
|
||||
property string source: SessionData.getMonitorWallpaper(modelData.name) || ""
|
||||
property bool isColorSource: source.startsWith("#")
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
function onIsLightModeChanged() {
|
||||
if (SessionData.perModeWallpaper) {
|
||||
var newSource = SessionData.getMonitorWallpaper(modelData.name) || ""
|
||||
if (newSource !== root.source) {
|
||||
root.source = newSource
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
WallpaperEngineProc {
|
||||
id: weProc
|
||||
monitor: modelData.name
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (source) {
|
||||
const formattedSource = source.startsWith("file://") ? source : "file://" + source
|
||||
wallpaperImage.source = formattedSource
|
||||
}
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
weProc.stop()
|
||||
}
|
||||
|
||||
onSourceChanged: {
|
||||
const isWE = source.startsWith("we:")
|
||||
const isColor = source.startsWith("#")
|
||||
|
||||
if (isWE) {
|
||||
wallpaperImage.source = ""
|
||||
weProc.start(source.substring(3))
|
||||
} else {
|
||||
weProc.stop()
|
||||
if (!source) {
|
||||
wallpaperImage.source = ""
|
||||
} else if (isColor) {
|
||||
wallpaperImage.source = ""
|
||||
} else {
|
||||
wallpaperImage.source = source.startsWith("file://") ? source : "file://" + source
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
active: !root.source || root.isColorSource
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: DankBackdrop {
|
||||
screenName: modelData.name
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: wallpaperImage
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
asynchronous: true
|
||||
smooth: true
|
||||
cache: true
|
||||
fillMode: root.getFillMode(SettingsData.wallpaperFillMode)
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
anchors.fill: parent
|
||||
source: wallpaperImage
|
||||
blurEnabled: true
|
||||
blur: 0.8
|
||||
blurMax: 48
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,26 +10,26 @@ PluginComponent {
|
||||
id: root
|
||||
|
||||
Ref {
|
||||
service: VpnService
|
||||
service: DMSNetworkService
|
||||
}
|
||||
|
||||
ccWidgetIcon: VpnService.isBusy ? "sync" : (VpnService.connected ? "vpn_lock" : "vpn_key_off")
|
||||
ccWidgetIcon: DMSNetworkService.isBusy ? "sync" : (DMSNetworkService.connected ? "vpn_lock" : "vpn_key_off")
|
||||
ccWidgetPrimaryText: "VPN"
|
||||
ccWidgetSecondaryText: {
|
||||
if (!VpnService.connected)
|
||||
if (!DMSNetworkService.connected)
|
||||
return "Disconnected"
|
||||
const names = VpnService.activeNames || []
|
||||
const names = DMSNetworkService.activeNames || []
|
||||
if (names.length <= 1)
|
||||
return names[0] || "Connected"
|
||||
return names[0] + " +" + (names.length - 1)
|
||||
}
|
||||
ccWidgetIsActive: VpnService.connected
|
||||
ccWidgetIsActive: DMSNetworkService.connected
|
||||
|
||||
onCcWidgetToggled: {
|
||||
if (VpnService.connected) {
|
||||
VpnService.disconnectAllActive()
|
||||
} else if (VpnService.profiles.length > 0) {
|
||||
VpnService.connect(VpnService.profiles[0].uuid)
|
||||
if (DMSNetworkService.connected) {
|
||||
DMSNetworkService.disconnectAllActive()
|
||||
} else if (DMSNetworkService.profiles.length > 0) {
|
||||
DMSNetworkService.connect(DMSNetworkService.profiles[0].uuid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,9 +52,9 @@ PluginComponent {
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (!VpnService.connected)
|
||||
if (!DMSNetworkService.connected)
|
||||
return "Active: None"
|
||||
const names = VpnService.activeNames || []
|
||||
const names = DMSNetworkService.activeNames || []
|
||||
if (names.length <= 1)
|
||||
return "Active: " + (names[0] || "VPN")
|
||||
return "Active: " + names[0] + " +" + (names.length - 1)
|
||||
@@ -72,7 +72,7 @@ PluginComponent {
|
||||
height: 28
|
||||
radius: 14
|
||||
color: discAllArea.containsMouse ? Theme.errorHover : Theme.surfaceLight
|
||||
visible: VpnService.connected
|
||||
visible: DMSNetworkService.connected
|
||||
width: 110
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
|
||||
@@ -99,7 +99,7 @@ PluginComponent {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: VpnService.disconnectAllActive()
|
||||
onClicked: DMSNetworkService.disconnectAllActive()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,7 +123,7 @@ PluginComponent {
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: VpnService.profiles.length === 0 ? 120 : 0
|
||||
height: DMSNetworkService.profiles.length === 0 ? 120 : 0
|
||||
visible: height > 0
|
||||
|
||||
Column {
|
||||
@@ -154,7 +154,7 @@ PluginComponent {
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: VpnService.profiles
|
||||
model: DMSNetworkService.profiles
|
||||
|
||||
delegate: Rectangle {
|
||||
required property var modelData
|
||||
@@ -162,9 +162,9 @@ PluginComponent {
|
||||
width: parent ? parent.width : 300
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: rowArea.containsMouse ? Theme.primaryHoverLight : (VpnService.isActiveUuid(modelData.uuid) ? Theme.primaryPressed : Theme.surfaceLight)
|
||||
border.width: VpnService.isActiveUuid(modelData.uuid) ? 2 : 1
|
||||
border.color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.outlineLight
|
||||
color: rowArea.containsMouse ? Theme.primaryHoverLight : (DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primaryPressed : Theme.surfaceLight)
|
||||
border.width: DMSNetworkService.isActiveUuid(modelData.uuid) ? 2 : 1
|
||||
border.color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.outlineLight
|
||||
|
||||
RowLayout {
|
||||
anchors.left: parent.left
|
||||
@@ -174,9 +174,9 @@ PluginComponent {
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: VpnService.isActiveUuid(modelData.uuid) ? "vpn_lock" : "vpn_key_off"
|
||||
name: DMSNetworkService.isActiveUuid(modelData.uuid) ? "vpn_lock" : "vpn_key_off"
|
||||
size: Theme.iconSize - 4
|
||||
color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||
color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ PluginComponent {
|
||||
StyledText {
|
||||
text: modelData.name
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||
color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
@@ -234,7 +234,7 @@ PluginComponent {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: VpnService.toggle(modelData.uuid)
|
||||
onClicked: DMSNetworkService.toggle(modelData.uuid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ Item {
|
||||
property string expandedSection: ""
|
||||
property var expandedWidgetData: null
|
||||
property var bluetoothCodecSelector: null
|
||||
property string screenName: ""
|
||||
|
||||
property var pluginDetailInstance: null
|
||||
property var widgetModel: null
|
||||
@@ -205,8 +206,9 @@ Item {
|
||||
Component {
|
||||
id: brightnessDetailComponent
|
||||
BrightnessDetail {
|
||||
currentDeviceName: root.expandedWidgetData?.deviceName || ""
|
||||
initialDeviceName: root.expandedWidgetData?.deviceName || ""
|
||||
instanceId: root.expandedWidgetData?.instanceId || ""
|
||||
screenName: root.screenName
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@ Column {
|
||||
property var expandedWidgetData: null
|
||||
property var bluetoothCodecSelector: null
|
||||
property bool darkModeTransitionPending: false
|
||||
property string screenName: ""
|
||||
property var parentScreen: null
|
||||
|
||||
signal expandClicked(var widgetData, int globalIndex)
|
||||
signal removeWidget(int index)
|
||||
@@ -182,6 +184,7 @@ Column {
|
||||
bluetoothCodecSelector: root.bluetoothCodecSelector
|
||||
widgetModel: root.model
|
||||
collapseCallback: root.requestCollapse
|
||||
screenName: root.screenName
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -329,7 +332,10 @@ Column {
|
||||
return "Select device"
|
||||
if (AudioService.sink.audio.muted)
|
||||
return "Muted"
|
||||
return Math.round(AudioService.sink.audio.volume * 100) + "%"
|
||||
const volume = AudioService.sink.audio.volume
|
||||
if (typeof volume !== "number" || isNaN(volume))
|
||||
return "0%"
|
||||
return Math.round(volume * 100) + "%"
|
||||
}
|
||||
case "audioInput":
|
||||
{
|
||||
@@ -337,7 +343,10 @@ Column {
|
||||
return "Select device"
|
||||
if (AudioService.source.audio.muted)
|
||||
return "Muted"
|
||||
return Math.round(AudioService.source.audio.volume * 100) + "%"
|
||||
const volume = AudioService.source.audio.volume
|
||||
if (typeof volume !== "number" || isNaN(volume))
|
||||
return "0%"
|
||||
return Math.round(volume * 100) + "%"
|
||||
}
|
||||
default:
|
||||
return widgetDef?.description || ""
|
||||
@@ -472,6 +481,8 @@ Column {
|
||||
height: 14
|
||||
deviceName: widgetData.deviceName || ""
|
||||
instanceId: widgetData.instanceId || ""
|
||||
screenName: root.screenName
|
||||
parentScreen: root.parentScreen
|
||||
property color sliderTrackColor: Theme.surfaceContainerHigh
|
||||
|
||||
onIconClicked: {
|
||||
|
||||
@@ -154,6 +154,8 @@ DankPopout {
|
||||
model: widgetModel
|
||||
bluetoothCodecSelector: bluetoothCodecSelector
|
||||
colorPickerModal: root.colorPickerModal
|
||||
screenName: root.triggerScreen?.name || ""
|
||||
parentScreen: root.triggerScreen
|
||||
onExpandClicked: (widgetData, globalIndex) => {
|
||||
root.expandedWidgetIndex = globalIndex
|
||||
root.expandedWidgetData = widgetData
|
||||
|
||||
@@ -5,8 +5,11 @@ import Quickshell.Bluetooth
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modals
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
implicitHeight: BluetoothService.adapter && BluetoothService.adapter.enabled ? headerRow.height + bluetoothContent.height + Theme.spacingM : headerRow.height
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHigh
|
||||
@@ -14,9 +17,33 @@ Rectangle {
|
||||
border.width: 0
|
||||
|
||||
property var bluetoothCodecModalRef: null
|
||||
property var devicesBeingPaired: new Set()
|
||||
|
||||
signal showCodecSelector(var device)
|
||||
|
||||
function isDeviceBeingPaired(deviceAddress) {
|
||||
return devicesBeingPaired.has(deviceAddress)
|
||||
}
|
||||
|
||||
function handlePairDevice(device) {
|
||||
if (!device) return
|
||||
|
||||
const deviceAddr = device.address
|
||||
devicesBeingPaired.add(deviceAddr)
|
||||
devicesBeingPairedChanged()
|
||||
|
||||
BluetoothService.pairDevice(device, function(response) {
|
||||
devicesBeingPaired.delete(deviceAddr)
|
||||
devicesBeingPairedChanged()
|
||||
|
||||
if (response.error) {
|
||||
ToastService.showError(I18n.tr("Pairing failed"), response.error)
|
||||
} else if (!BluetoothService.enhancedPairingAvailable) {
|
||||
ToastService.showSuccess(I18n.tr("Device paired"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function updateDeviceCodecDisplay(deviceAddress, codecName) {
|
||||
for (let i = 0; i < pairedRepeater.count; i++) {
|
||||
let item = pairedRepeater.itemAt(i)
|
||||
@@ -327,7 +354,7 @@ Rectangle {
|
||||
required property int index
|
||||
|
||||
property bool canConnect: BluetoothService.canConnect(modelData)
|
||||
property bool isBusy: BluetoothService.isDeviceBusy(modelData)
|
||||
property bool isBusy: BluetoothService.isDeviceBusy(modelData) || isDeviceBeingPaired(modelData.address)
|
||||
|
||||
width: parent.width
|
||||
height: 50
|
||||
@@ -335,7 +362,7 @@ Rectangle {
|
||||
color: availableMouseArea.containsMouse && !isBusy ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHighest
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
border.width: 0
|
||||
opacity: canConnect ? 1 : 0.6
|
||||
opacity: (canConnect && !isBusy) ? 1 : 0.6
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
@@ -367,7 +394,7 @@ Rectangle {
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (modelData.pairing) return "Pairing..."
|
||||
if (modelData.pairing || isBusy) return "Pairing..."
|
||||
if (modelData.blocked) return "Blocked"
|
||||
return BluetoothService.getSignalStrength(modelData)
|
||||
}
|
||||
@@ -390,12 +417,12 @@ Rectangle {
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: {
|
||||
if (modelData.pairing) return "Pairing..."
|
||||
if (isBusy) return "Pairing..."
|
||||
if (!canConnect) return "Cannot pair"
|
||||
return "Pair"
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: canConnect ? Theme.primary : Theme.surfaceVariantText
|
||||
color: (canConnect && !isBusy) ? Theme.primary : Theme.surfaceVariantText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
@@ -406,9 +433,7 @@ Rectangle {
|
||||
cursorShape: canConnect && !isBusy ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
enabled: canConnect && !isBusy
|
||||
onClicked: {
|
||||
if (modelData) {
|
||||
BluetoothService.connectDeviceWithTrust(modelData)
|
||||
}
|
||||
root.handlePairDevice(modelData)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,10 +541,30 @@ Rectangle {
|
||||
|
||||
onTriggered: {
|
||||
if (bluetoothContextMenu.currentDevice) {
|
||||
bluetoothContextMenu.currentDevice.forget()
|
||||
if (BluetoothService.enhancedPairingAvailable) {
|
||||
const devicePath = BluetoothService.getDevicePath(bluetoothContextMenu.currentDevice)
|
||||
DMSService.bluetoothRemove(devicePath, response => {
|
||||
if (response.error) {
|
||||
ToastService.showError(I18n.tr("Failed to remove device"), response.error)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
bluetoothContextMenu.currentDevice.forget()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BluetoothPairingModal {
|
||||
id: bluetoothPairingModal
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: DMSService
|
||||
|
||||
function onBluetoothPairingRequest(data) {
|
||||
bluetoothPairingModal.show(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,76 @@ import qs.Widgets
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string currentDeviceName: ""
|
||||
property string initialDeviceName: ""
|
||||
property string instanceId: ""
|
||||
property string screenName: ""
|
||||
|
||||
signal deviceNameChanged(string newDeviceName)
|
||||
|
||||
property string currentDeviceName: ""
|
||||
|
||||
function resolveDeviceName() {
|
||||
if (!DisplayService.brightnessAvailable || !DisplayService.devices || DisplayService.devices.length === 0) {
|
||||
return ""
|
||||
}
|
||||
|
||||
if (screenName && screenName.length > 0) {
|
||||
const pins = SettingsData.brightnessDevicePins || {}
|
||||
const pinnedDevice = pins[screenName]
|
||||
if (pinnedDevice && pinnedDevice.length > 0) {
|
||||
const found = DisplayService.devices.find(dev => dev.name === pinnedDevice)
|
||||
if (found) {
|
||||
return found.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (initialDeviceName && initialDeviceName.length > 0) {
|
||||
const found = DisplayService.devices.find(dev => dev.name === initialDeviceName)
|
||||
if (found) {
|
||||
return found.name
|
||||
}
|
||||
}
|
||||
|
||||
const currentDeviceNameFromService = DisplayService.currentDevice
|
||||
if (currentDeviceNameFromService) {
|
||||
const found = DisplayService.devices.find(dev => dev.name === currentDeviceNameFromService)
|
||||
if (found) {
|
||||
return found.name
|
||||
}
|
||||
}
|
||||
|
||||
return DisplayService.devices.length > 0 ? DisplayService.devices[0].name : ""
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
currentDeviceName = resolveDeviceName()
|
||||
}
|
||||
|
||||
property bool isPinnedToScreen: {
|
||||
if (!screenName || screenName.length === 0) {
|
||||
return false
|
||||
}
|
||||
const pins = SettingsData.brightnessDevicePins || {}
|
||||
return pins[screenName] === currentDeviceName
|
||||
}
|
||||
|
||||
function togglePinToScreen() {
|
||||
if (!screenName || screenName.length === 0 || !currentDeviceName || currentDeviceName.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const pins = JSON.parse(JSON.stringify(SettingsData.brightnessDevicePins || {}))
|
||||
|
||||
if (isPinnedToScreen) {
|
||||
delete pins[screenName]
|
||||
} else {
|
||||
pins[screenName] = currentDeviceName
|
||||
}
|
||||
|
||||
SettingsData.setBrightnessDevicePins(pins)
|
||||
}
|
||||
|
||||
implicitHeight: brightnessContent.height + Theme.spacingM
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHigh
|
||||
@@ -61,6 +126,74 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 40
|
||||
visible: screenName && screenName.length > 0 && DisplayService.devices && DisplayService.devices.length > 1
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHighest
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "monitor"
|
||||
size: Theme.iconSize
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: screenName || "Unknown Monitor"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: pinRow.width + Theme.spacingS * 2
|
||||
height: 28
|
||||
radius: height / 2
|
||||
color: isPinnedToScreen ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.surfaceText, 0.05)
|
||||
|
||||
Row {
|
||||
id: pinRow
|
||||
anchors.centerIn: parent
|
||||
spacing: 4
|
||||
|
||||
DankIcon {
|
||||
name: isPinnedToScreen ? "push_pin" : "push_pin"
|
||||
size: 16
|
||||
color: isPinnedToScreen ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: isPinnedToScreen ? "Pinned" : "Pin"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: isPinnedToScreen ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.togglePinToScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: DisplayService.devices || []
|
||||
delegate: Rectangle {
|
||||
@@ -90,7 +223,7 @@ Rectangle {
|
||||
const deviceName = modelData.name || ""
|
||||
|
||||
if (deviceClass === "backlight" || deviceClass === "ddc") {
|
||||
const brightness = modelData.percentage || 50
|
||||
const brightness = DisplayService.getDeviceBrightness(modelData.name)
|
||||
if (brightness <= 33) return "brightness_low"
|
||||
if (brightness <= 66) return "brightness_medium"
|
||||
return "brightness_high"
|
||||
@@ -106,7 +239,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: (modelData.percentage || 50) + "%"
|
||||
text: Math.round(DisplayService.getDeviceBrightness(modelData.name)) + "%"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
@@ -34,6 +34,10 @@ Rectangle {
|
||||
return 1
|
||||
}
|
||||
|
||||
if (NetworkService.backend !== "networkmanager" || DMSService.apiVersion <= 10) {
|
||||
return 1
|
||||
}
|
||||
|
||||
const pref = NetworkService.userPreference
|
||||
const status = NetworkService.networkStatus
|
||||
let index = 1
|
||||
@@ -76,7 +80,7 @@ Rectangle {
|
||||
DankButtonGroup {
|
||||
id: preferenceControls
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: DMSService.apiVersion >= 5
|
||||
visible: NetworkService.backend === "networkmanager" && DMSService.apiVersion > 10
|
||||
|
||||
model: ["Ethernet", "WiFi"]
|
||||
currentIndex: currentPreferenceIndex
|
||||
@@ -196,7 +200,7 @@ Rectangle {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: Theme.spacingM
|
||||
anchors.topMargin: Theme.spacingM
|
||||
visible: currentPreferenceIndex === 0 && DMSService.apiVersion >= 5
|
||||
visible: currentPreferenceIndex === 0 && NetworkService.backend === "networkmanager" && DMSService.apiVersion > 10
|
||||
contentHeight: wiredColumn.height
|
||||
clip: true
|
||||
|
||||
|
||||
@@ -143,8 +143,8 @@ QtObject {
|
||||
"description": "VPN connections",
|
||||
"icon": "vpn_key",
|
||||
"type": "builtin_plugin",
|
||||
"enabled": VpnService.available,
|
||||
"warning": !VpnService.available ? "VPN not available" : undefined,
|
||||
"enabled": DMSNetworkService.available,
|
||||
"warning": !DMSNetworkService.available ? "VPN not available" : undefined,
|
||||
"isBuiltinPlugin": true
|
||||
}]
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
|
||||
CompoundPill {
|
||||
id: root
|
||||
|
||||
property var defaultSource: AudioService.source
|
||||
|
||||
iconName: {
|
||||
if (!defaultSource) return "mic_off"
|
||||
|
||||
let volume = defaultSource.audio.volume
|
||||
let muted = defaultSource.audio.muted
|
||||
|
||||
if (muted || volume === 0.0) return "mic_off"
|
||||
return "mic"
|
||||
}
|
||||
|
||||
isActive: defaultSource && !defaultSource.audio.muted
|
||||
|
||||
primaryText: {
|
||||
if (!defaultSource) {
|
||||
return "No input device"
|
||||
}
|
||||
return defaultSource.description || "Audio Input"
|
||||
}
|
||||
|
||||
secondaryText: {
|
||||
if (!defaultSource) {
|
||||
return "Select device"
|
||||
}
|
||||
if (defaultSource.audio.muted) {
|
||||
return "Muted"
|
||||
}
|
||||
return Math.round(defaultSource.audio.volume * 100) + "%"
|
||||
}
|
||||
|
||||
onToggled: {
|
||||
if (defaultSource && defaultSource.audio) {
|
||||
defaultSource.audio.muted = !defaultSource.audio.muted
|
||||
}
|
||||
}
|
||||
|
||||
onWheelEvent: function (wheelEvent) {
|
||||
if (!defaultSource || !defaultSource.audio) return
|
||||
let delta = wheelEvent.angleDelta.y
|
||||
let currentVolume = defaultSource.audio.volume * 100
|
||||
let newVolume
|
||||
if (delta > 0)
|
||||
newVolume = Math.min(100, currentVolume + 5)
|
||||
else
|
||||
newVolume = Math.max(0, currentVolume - 5)
|
||||
defaultSource.audio.muted = false
|
||||
defaultSource.audio.volume = newVolume / 100
|
||||
wheelEvent.accepted = true
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
|
||||
CompoundPill {
|
||||
id: root
|
||||
|
||||
property var defaultSink: AudioService.sink
|
||||
|
||||
iconName: {
|
||||
if (!defaultSink) return "volume_off"
|
||||
|
||||
let volume = defaultSink.audio.volume
|
||||
let muted = defaultSink.audio.muted
|
||||
|
||||
if (muted || volume === 0.0) return "volume_off"
|
||||
if (volume <= 0.33) return "volume_down"
|
||||
if (volume <= 0.66) return "volume_up"
|
||||
return "volume_up"
|
||||
}
|
||||
|
||||
isActive: defaultSink && !defaultSink.audio.muted
|
||||
|
||||
primaryText: {
|
||||
if (!defaultSink) {
|
||||
return "No output device"
|
||||
}
|
||||
return defaultSink.description || "Audio Output"
|
||||
}
|
||||
|
||||
secondaryText: {
|
||||
if (!defaultSink) {
|
||||
return "Select device"
|
||||
}
|
||||
if (defaultSink.audio.muted) {
|
||||
return "Muted"
|
||||
}
|
||||
return Math.round(defaultSink.audio.volume * 100) + "%"
|
||||
}
|
||||
|
||||
onToggled: {
|
||||
if (defaultSink && defaultSink.audio) {
|
||||
defaultSink.audio.muted = !defaultSink.audio.muted
|
||||
}
|
||||
}
|
||||
|
||||
onWheelEvent: function (wheelEvent) {
|
||||
if (!defaultSink || !defaultSink.audio) return
|
||||
let delta = wheelEvent.angleDelta.y
|
||||
let currentVolume = defaultSink.audio.volume * 100
|
||||
let newVolume
|
||||
if (delta > 0)
|
||||
newVolume = Math.min(100, currentVolume + 5)
|
||||
else
|
||||
newVolume = Math.max(0, currentVolume - 5)
|
||||
defaultSink.audio.muted = false
|
||||
defaultSink.audio.volume = newVolume / 100
|
||||
AudioService.volumeChanged()
|
||||
wheelEvent.accepted = true
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ Row {
|
||||
|
||||
property string deviceName: ""
|
||||
property string instanceId: ""
|
||||
property string screenName: ""
|
||||
property var parentScreen: null
|
||||
|
||||
signal iconClicked()
|
||||
|
||||
@@ -21,6 +23,17 @@ Row {
|
||||
return ""
|
||||
}
|
||||
|
||||
if (screenName && screenName.length > 0) {
|
||||
const pins = SettingsData.brightnessDevicePins || {}
|
||||
const pinnedDevice = pins[screenName]
|
||||
if (pinnedDevice && pinnedDevice.length > 0) {
|
||||
const found = DisplayService.devices.find(dev => dev.name === pinnedDevice)
|
||||
if (found) {
|
||||
return found.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deviceName && deviceName.length > 0) {
|
||||
const found = DisplayService.devices.find(dev => dev.name === deviceName)
|
||||
return found ? found.name : ""
|
||||
@@ -76,8 +89,10 @@ Row {
|
||||
tooltipLoader.active = true
|
||||
if (tooltipLoader.item) {
|
||||
const tooltipText = targetDevice ? "bl device: " + targetDevice.name : "Backlight Control"
|
||||
const p = iconArea.mapToItem(null, iconArea.width / 2, 0)
|
||||
tooltipLoader.item.show(tooltipText, p.x, p.y - 40, null)
|
||||
const globalPos = iconArea.mapToGlobal(iconArea.width / 2, iconArea.height / 2)
|
||||
const screenY = root.parentScreen?.y ?? 0
|
||||
const relativeY = globalPos.y - screenY - 55
|
||||
tooltipLoader.item.show(tooltipText, globalPos.x, relativeY, root.parentScreen)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +136,7 @@ Row {
|
||||
value: targetBrightness
|
||||
onSliderValueChanged: function(newValue) {
|
||||
if (DisplayService.brightnessAvailable && targetDeviceName) {
|
||||
DisplayService.setBrightness(newValue, targetDeviceName)
|
||||
DisplayService.setBrightness(newValue, targetDeviceName, true)
|
||||
}
|
||||
}
|
||||
thumbOutlineColor: Theme.surfaceContainer
|
||||
|
||||
@@ -13,8 +13,10 @@ Item {
|
||||
property var parentScreen: null
|
||||
property real widgetThickness: 30
|
||||
property real barThickness: 48
|
||||
property bool overrideAxisLayout: false
|
||||
property bool forceVerticalLayout: false
|
||||
|
||||
readonly property bool isVertical: axis?.isVertical ?? false
|
||||
readonly property bool isVertical: overrideAxisLayout ? forceVerticalLayout : (axis?.isVertical ?? false)
|
||||
readonly property real spacing: noBackground ? 2 : Theme.spacingXS
|
||||
|
||||
property var centerWidgets: []
|
||||
@@ -396,8 +398,47 @@ Item {
|
||||
if ("barThickness" in item) {
|
||||
item.barThickness = Qt.binding(() => root.barThickness)
|
||||
}
|
||||
if ("sectionSpacing" in item) {
|
||||
item.sectionSpacing = Qt.binding(() => root.spacing)
|
||||
}
|
||||
|
||||
if ("isFirst" in item) {
|
||||
item.isFirst = Qt.binding(() => {
|
||||
for (var i = 0; i < centerRepeater.count; i++) {
|
||||
const checkItem = centerRepeater.itemAt(i)
|
||||
if (checkItem && checkItem.active && checkItem.item) {
|
||||
return checkItem.item === item
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
if ("isLast" in item) {
|
||||
item.isLast = Qt.binding(() => {
|
||||
for (var i = centerRepeater.count - 1; i >= 0; i--) {
|
||||
const checkItem = centerRepeater.itemAt(i)
|
||||
if (checkItem && checkItem.active && checkItem.item) {
|
||||
return checkItem.item === item
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
if ("isLeftBarEdge" in item) {
|
||||
item.isLeftBarEdge = false
|
||||
}
|
||||
if ("isRightBarEdge" in item) {
|
||||
item.isRightBarEdge = false
|
||||
}
|
||||
if ("isTopBarEdge" in item) {
|
||||
item.isTopBarEdge = false
|
||||
}
|
||||
if ("isBottomBarEdge" in item) {
|
||||
item.isBottomBarEdge = false
|
||||
}
|
||||
|
||||
// Inject PluginService for plugin widgets
|
||||
if (item.pluginService !== undefined) {
|
||||
var parts = model.widgetId.split(":")
|
||||
var pluginId = parts[0]
|
||||
|
||||
@@ -235,11 +235,11 @@ Item {
|
||||
Connections {
|
||||
target: PluginService
|
||||
function onPluginLoaded(pluginId) {
|
||||
console.log("DankBar: Plugin loaded:", pluginId)
|
||||
console.info("DankBar: Plugin loaded:", pluginId)
|
||||
SettingsData.widgetDataChanged()
|
||||
}
|
||||
function onPluginUnloaded(pluginId) {
|
||||
console.log("DankBar: Plugin unloaded:", pluginId)
|
||||
console.info("DankBar: Plugin unloaded:", pluginId)
|
||||
SettingsData.widgetDataChanged()
|
||||
}
|
||||
}
|
||||
@@ -585,18 +585,18 @@ Item {
|
||||
function getWidgetSection(parentItem) {
|
||||
let current = parentItem
|
||||
while (current) {
|
||||
if (current.objectName === "leftSection" || current === hLeftSection || current === vLeftSection) {
|
||||
if (current.objectName === "leftSection") {
|
||||
return "left"
|
||||
}
|
||||
if (current.objectName === "centerSection" || current === hCenterSection || current === vCenterSection) {
|
||||
if (current.objectName === "centerSection") {
|
||||
return "center"
|
||||
}
|
||||
if (current.objectName === "rightSection" || current === hRightSection || current === vRightSection) {
|
||||
if (current.objectName === "rightSection") {
|
||||
return "right"
|
||||
}
|
||||
current = current.parent
|
||||
}
|
||||
return "left" // fallback
|
||||
return "left"
|
||||
}
|
||||
|
||||
readonly property var widgetVisibility: ({
|
||||
@@ -695,6 +695,9 @@ Item {
|
||||
|
||||
LeftSection {
|
||||
id: hLeftSection
|
||||
objectName: "leftSection"
|
||||
overrideAxisLayout: true
|
||||
forceVerticalLayout: false
|
||||
anchors {
|
||||
left: parent.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
@@ -710,6 +713,9 @@ Item {
|
||||
|
||||
RightSection {
|
||||
id: hRightSection
|
||||
objectName: "rightSection"
|
||||
overrideAxisLayout: true
|
||||
forceVerticalLayout: false
|
||||
anchors {
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
@@ -725,6 +731,9 @@ Item {
|
||||
|
||||
CenterSection {
|
||||
id: hCenterSection
|
||||
objectName: "centerSection"
|
||||
overrideAxisLayout: true
|
||||
forceVerticalLayout: false
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
@@ -746,6 +755,9 @@ Item {
|
||||
|
||||
LeftSection {
|
||||
id: vLeftSection
|
||||
objectName: "leftSection"
|
||||
overrideAxisLayout: true
|
||||
forceVerticalLayout: true
|
||||
width: parent.width
|
||||
anchors {
|
||||
top: parent.top
|
||||
@@ -762,6 +774,9 @@ Item {
|
||||
|
||||
CenterSection {
|
||||
id: vCenterSection
|
||||
objectName: "centerSection"
|
||||
overrideAxisLayout: true
|
||||
forceVerticalLayout: true
|
||||
width: parent.width
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
@@ -778,6 +793,9 @@ Item {
|
||||
|
||||
RightSection {
|
||||
id: vRightSection
|
||||
objectName: "rightSection"
|
||||
overrideAxisLayout: true
|
||||
forceVerticalLayout: true
|
||||
width: parent.width
|
||||
height: implicitHeight
|
||||
anchors {
|
||||
@@ -814,6 +832,7 @@ Item {
|
||||
id: launcherButtonComponent
|
||||
|
||||
LauncherButton {
|
||||
id: launcherButton
|
||||
isActive: false
|
||||
widgetThickness: barWindow.widgetThickness
|
||||
barThickness: barWindow.effectiveBarThickness
|
||||
@@ -823,6 +842,12 @@ Item {
|
||||
hyprlandOverviewLoader: root.hyprlandOverviewLoader
|
||||
onClicked: {
|
||||
appDrawerLoader.active = true
|
||||
if (appDrawerLoader.item && appDrawerLoader.item.setTriggerPosition) {
|
||||
const globalPos = launcherButton.visualContent.mapToGlobal(0, 0)
|
||||
const currentScreen = barWindow.screen
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barWindow.effectiveBarThickness, launcherButton.visualWidth)
|
||||
appDrawerLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, launcherButton.section, currentScreen)
|
||||
}
|
||||
appDrawerLoader.item?.toggle()
|
||||
}
|
||||
}
|
||||
@@ -1214,13 +1239,19 @@ Item {
|
||||
Component {
|
||||
id: separatorComponent
|
||||
|
||||
Rectangle {
|
||||
width: barWindow.isVertical ? barWindow.widgetThickness * 0.67 : 1
|
||||
height: barWindow.isVertical ? 1 : barWindow.widgetThickness * 0.67
|
||||
Item {
|
||||
width: barWindow.isVertical ? parent.barThickness : 1
|
||||
height: barWindow.isVertical ? 1 : parent.barThickness
|
||||
implicitWidth: width
|
||||
implicitHeight: height
|
||||
color: Theme.outline
|
||||
opacity: 0.3
|
||||
|
||||
Rectangle {
|
||||
width: barWindow.isVertical ? parent.width * 0.6 : 1
|
||||
height: barWindow.isVertical ? 1 : parent.height * 0.6
|
||||
anchors.centerIn: parent
|
||||
color: Theme.outline
|
||||
opacity: 0.3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,10 @@ Item {
|
||||
property var parentScreen: null
|
||||
property real widgetThickness: 30
|
||||
property real barThickness: 48
|
||||
property bool overrideAxisLayout: false
|
||||
property bool forceVerticalLayout: false
|
||||
|
||||
readonly property bool isVertical: axis?.isVertical ?? false
|
||||
readonly property bool isVertical: overrideAxisLayout ? forceVerticalLayout : (axis?.isVertical ?? false)
|
||||
|
||||
implicitHeight: layoutLoader.item ? (layoutLoader.item.implicitHeight || layoutLoader.item.height) : 0
|
||||
implicitWidth: layoutLoader.item ? (layoutLoader.item.implicitWidth || layoutLoader.item.width) : 0
|
||||
@@ -26,10 +28,13 @@ Item {
|
||||
Component {
|
||||
id: rowComp
|
||||
Row {
|
||||
spacing: noBackground ? 2 : Theme.spacingXS
|
||||
readonly property real widgetSpacing: noBackground ? 2 : Theme.spacingXS
|
||||
spacing: widgetSpacing
|
||||
Repeater {
|
||||
id: rowRepeater
|
||||
model: root.widgetsModel
|
||||
Item {
|
||||
readonly property real rowSpacing: parent.widgetSpacing
|
||||
width: widgetLoader.item ? widgetLoader.item.width : 0
|
||||
height: widgetLoader.item ? widgetLoader.item.height : 0
|
||||
WidgetHost {
|
||||
@@ -45,6 +50,11 @@ Item {
|
||||
parentScreen: root.parentScreen
|
||||
widgetThickness: root.widgetThickness
|
||||
barThickness: root.barThickness
|
||||
isFirst: model.index === 0
|
||||
isLast: model.index === rowRepeater.count - 1
|
||||
sectionSpacing: parent.rowSpacing
|
||||
isLeftBarEdge: true
|
||||
isRightBarEdge: false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,10 +65,13 @@ Item {
|
||||
id: columnComp
|
||||
Column {
|
||||
width: Math.max(parent.width, 200)
|
||||
spacing: noBackground ? 2 : Theme.spacingXS
|
||||
readonly property real widgetSpacing: noBackground ? 2 : Theme.spacingXS
|
||||
spacing: widgetSpacing
|
||||
Repeater {
|
||||
id: columnRepeater
|
||||
model: root.widgetsModel
|
||||
Item {
|
||||
readonly property real columnSpacing: parent.widgetSpacing
|
||||
width: parent.width
|
||||
height: widgetLoader.item ? widgetLoader.item.height : 0
|
||||
WidgetHost {
|
||||
@@ -74,6 +87,11 @@ Item {
|
||||
parentScreen: root.parentScreen
|
||||
widgetThickness: root.widgetThickness
|
||||
barThickness: root.barThickness
|
||||
isFirst: model.index === 0
|
||||
isLast: model.index === columnRepeater.count - 1
|
||||
sectionSpacing: parent.columnSpacing
|
||||
isTopBarEdge: true
|
||||
isBottomBarEdge: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ DankPopout {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: BatteryService.batteryAvailable ? BatteryService.batteryStatus : "Management"
|
||||
text: BatteryService.batteryStatus
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: {
|
||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
|
||||
@@ -247,6 +247,7 @@ DankPopout {
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
font.weight: Font.Medium
|
||||
visible: BatteryService.batteryAvailable
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ DankPopout {
|
||||
id: root
|
||||
|
||||
Ref {
|
||||
service: VpnService
|
||||
service: DMSNetworkService
|
||||
}
|
||||
|
||||
property var triggerScreen: null
|
||||
@@ -161,11 +161,11 @@ DankPopout {
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (!VpnService.connected) {
|
||||
if (!DMSNetworkService.connected) {
|
||||
return "Active: None";
|
||||
}
|
||||
|
||||
const names = VpnService.activeNames || [];
|
||||
const names = DMSNetworkService.activeNames || [];
|
||||
if (names.length <= 1) {
|
||||
return "Active: " + (names[0] || "VPN");
|
||||
}
|
||||
@@ -193,7 +193,7 @@ DankPopout {
|
||||
height: 28
|
||||
radius: 14
|
||||
color: discAllArea.containsMouse ? Theme.errorHover : Theme.surfaceLight
|
||||
visible: VpnService.connected
|
||||
visible: DMSNetworkService.connected
|
||||
width: 130
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
border.width: 0
|
||||
@@ -224,7 +224,7 @@ DankPopout {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: VpnService.disconnectAllActive()
|
||||
onClicked: DMSNetworkService.disconnectAllActive()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -251,7 +251,7 @@ DankPopout {
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: VpnService.profiles.length === 0 ? 120 : 0
|
||||
height: DMSNetworkService.profiles.length === 0 ? 120 : 0
|
||||
visible: height > 0
|
||||
|
||||
Column {
|
||||
@@ -284,7 +284,7 @@ DankPopout {
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: VpnService.profiles
|
||||
model: DMSNetworkService.profiles
|
||||
|
||||
delegate: Rectangle {
|
||||
required property var modelData
|
||||
@@ -292,9 +292,9 @@ DankPopout {
|
||||
width: parent ? parent.width : 300
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: rowArea.containsMouse ? Theme.primaryHoverLight : (VpnService.isActiveUuid(modelData.uuid) ? Theme.primaryPressed : Theme.surfaceLight)
|
||||
border.width: VpnService.isActiveUuid(modelData.uuid) ? 2 : 1
|
||||
border.color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.outlineLight
|
||||
color: rowArea.containsMouse ? Theme.primaryHoverLight : (DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primaryPressed : Theme.surfaceLight)
|
||||
border.width: DMSNetworkService.isActiveUuid(modelData.uuid) ? 2 : 1
|
||||
border.color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.outlineLight
|
||||
|
||||
RowLayout {
|
||||
anchors.left: parent.left
|
||||
@@ -304,9 +304,9 @@ DankPopout {
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: VpnService.isActiveUuid(modelData.uuid) ? "vpn_lock" : "vpn_key_off"
|
||||
name: DMSNetworkService.isActiveUuid(modelData.uuid) ? "vpn_lock" : "vpn_key_off"
|
||||
size: Theme.iconSize - 4
|
||||
color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||
color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@ DankPopout {
|
||||
StyledText {
|
||||
text: modelData.name
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||
color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
@@ -392,7 +392,7 @@ DankPopout {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: VpnService.toggle(modelData.uuid)
|
||||
onClicked: DMSNetworkService.toggle(modelData.uuid)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,8 +11,10 @@ Item {
|
||||
property var parentScreen: null
|
||||
property real widgetThickness: 30
|
||||
property real barThickness: 48
|
||||
property bool overrideAxisLayout: false
|
||||
property bool forceVerticalLayout: false
|
||||
|
||||
readonly property bool isVertical: axis?.isVertical ?? false
|
||||
readonly property bool isVertical: overrideAxisLayout ? forceVerticalLayout : (axis?.isVertical ?? false)
|
||||
|
||||
implicitHeight: layoutLoader.item ? layoutLoader.item.implicitHeight : 0
|
||||
implicitWidth: layoutLoader.item ? layoutLoader.item.implicitWidth : 0
|
||||
@@ -27,11 +29,14 @@ Item {
|
||||
Component {
|
||||
id: rowComp
|
||||
Row {
|
||||
spacing: noBackground ? 2 : Theme.spacingXS
|
||||
readonly property real widgetSpacing: noBackground ? 2 : Theme.spacingXS
|
||||
spacing: widgetSpacing
|
||||
anchors.right: parent ? parent.right : undefined
|
||||
Repeater {
|
||||
id: rowRepeater
|
||||
model: root.widgetsModel
|
||||
Item {
|
||||
readonly property real rowSpacing: parent.widgetSpacing
|
||||
width: widgetLoader.item ? widgetLoader.item.width : 0
|
||||
height: widgetLoader.item ? widgetLoader.item.height : 0
|
||||
WidgetHost {
|
||||
@@ -47,6 +52,11 @@ Item {
|
||||
parentScreen: root.parentScreen
|
||||
widgetThickness: root.widgetThickness
|
||||
barThickness: root.barThickness
|
||||
isFirst: model.index === 0
|
||||
isLast: model.index === rowRepeater.count - 1
|
||||
sectionSpacing: parent.rowSpacing
|
||||
isLeftBarEdge: false
|
||||
isRightBarEdge: true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,10 +67,13 @@ Item {
|
||||
id: columnComp
|
||||
Column {
|
||||
width: parent ? parent.width : 0
|
||||
spacing: noBackground ? 2 : Theme.spacingXS
|
||||
readonly property real widgetSpacing: noBackground ? 2 : Theme.spacingXS
|
||||
spacing: widgetSpacing
|
||||
Repeater {
|
||||
id: columnRepeater
|
||||
model: root.widgetsModel
|
||||
Item {
|
||||
readonly property real columnSpacing: parent.widgetSpacing
|
||||
width: parent.width
|
||||
height: widgetLoader.item ? widgetLoader.item.height : 0
|
||||
WidgetHost {
|
||||
@@ -76,6 +89,11 @@ Item {
|
||||
parentScreen: root.parentScreen
|
||||
widgetThickness: root.widgetThickness
|
||||
barThickness: root.barThickness
|
||||
isFirst: model.index === 0
|
||||
isLast: model.index === columnRepeater.count - 1
|
||||
sectionSpacing: parent.columnSpacing
|
||||
isTopBarEdge: false
|
||||
isBottomBarEdge: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,20 @@ Loader {
|
||||
property var parentScreen: null
|
||||
property real widgetThickness: 30
|
||||
property real barThickness: 48
|
||||
property bool isFirst: false
|
||||
property bool isLast: false
|
||||
property real sectionSpacing: 0
|
||||
property bool isLeftBarEdge: false
|
||||
property bool isRightBarEdge: false
|
||||
property bool isTopBarEdge: false
|
||||
property bool isBottomBarEdge: false
|
||||
|
||||
asynchronous: false
|
||||
|
||||
active: getWidgetVisible(widgetId, DgopService.dgopAvailable) &&
|
||||
readonly property bool orientationMatches: (axis?.isVertical ?? false) === isInColumn
|
||||
|
||||
active: orientationMatches &&
|
||||
getWidgetVisible(widgetId, DgopService.dgopAvailable) &&
|
||||
(widgetId !== "music" || MprisController.activePlayer !== null)
|
||||
sourceComponent: getWidgetComponent(widgetId, components)
|
||||
opacity: getWidgetEnabled(widgetData?.enabled) ? 1 : 0
|
||||
@@ -73,6 +83,62 @@ Loader {
|
||||
restoreMode: Binding.RestoreNone
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: root.item
|
||||
when: root.item && "isFirst" in root.item
|
||||
property: "isFirst"
|
||||
value: root.isFirst
|
||||
restoreMode: Binding.RestoreNone
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: root.item
|
||||
when: root.item && "isLast" in root.item
|
||||
property: "isLast"
|
||||
value: root.isLast
|
||||
restoreMode: Binding.RestoreNone
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: root.item
|
||||
when: root.item && "sectionSpacing" in root.item
|
||||
property: "sectionSpacing"
|
||||
value: root.sectionSpacing
|
||||
restoreMode: Binding.RestoreNone
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: root.item
|
||||
when: root.item && "isLeftBarEdge" in root.item
|
||||
property: "isLeftBarEdge"
|
||||
value: root.isLeftBarEdge
|
||||
restoreMode: Binding.RestoreNone
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: root.item
|
||||
when: root.item && "isRightBarEdge" in root.item
|
||||
property: "isRightBarEdge"
|
||||
value: root.isRightBarEdge
|
||||
restoreMode: Binding.RestoreNone
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: root.item
|
||||
when: root.item && "isTopBarEdge" in root.item
|
||||
property: "isTopBarEdge"
|
||||
value: root.isTopBarEdge
|
||||
restoreMode: Binding.RestoreNone
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: root.item
|
||||
when: root.item && "isBottomBarEdge" in root.item
|
||||
property: "isBottomBarEdge"
|
||||
value: root.isBottomBarEdge
|
||||
restoreMode: Binding.RestoreNone
|
||||
}
|
||||
|
||||
onLoaded: {
|
||||
if (item) {
|
||||
contentItemReady(item)
|
||||
|
||||
@@ -50,7 +50,6 @@ BasePill {
|
||||
StyledText {
|
||||
text: BatteryService.batteryLevel.toString()
|
||||
font.pixelSize: Theme.barTextSize(battery.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: BatteryService.batteryAvailable
|
||||
@@ -87,7 +86,6 @@ BasePill {
|
||||
StyledText {
|
||||
text: `${BatteryService.batteryLevel}%`
|
||||
font.pixelSize: Theme.barTextSize(battery.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: BatteryService.batteryAvailable
|
||||
@@ -97,8 +95,10 @@ BasePill {
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
x: -battery.leftMargin
|
||||
y: -battery.topMargin
|
||||
width: battery.width + battery.leftMargin + battery.rightMargin
|
||||
height: battery.height + battery.topMargin + battery.bottomMargin
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: {
|
||||
@@ -108,7 +108,7 @@ BasePill {
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, battery.visualWidth)
|
||||
popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||
}
|
||||
toggleBatteryPopup();
|
||||
toggleBatteryPopup()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,9 +37,9 @@ BasePill {
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
|
||||
StyledText {
|
||||
@@ -54,9 +54,9 @@ BasePill {
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,18 +68,18 @@ BasePill {
|
||||
text: String(systemClock?.date?.getMinutes()).padStart(2, '0').charAt(0)
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: String(systemClock?.date?.getMinutes()).padStart(2, '0').charAt(1)
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,18 +92,18 @@ BasePill {
|
||||
text: String(systemClock?.date?.getSeconds()).padStart(2, '0').charAt(0)
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: String(systemClock?.date?.getSeconds()).padStart(2, '0').charAt(1)
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,9 +134,9 @@ BasePill {
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.primary
|
||||
font.weight: Font.Light
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
|
||||
StyledText {
|
||||
@@ -149,9 +149,9 @@ BasePill {
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.primary
|
||||
font.weight: Font.Light
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,9 +169,9 @@ BasePill {
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.primary
|
||||
font.weight: Font.Light
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
|
||||
StyledText {
|
||||
@@ -184,9 +184,9 @@ BasePill {
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.primary
|
||||
font.weight: Font.Light
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,28 +198,30 @@ BasePill {
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
id: timeText
|
||||
text: {
|
||||
return systemClock?.date?.toLocaleTimeString(Qt.locale(), SettingsData.getEffectiveTimeFormat())
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.baseline: dateText.baseline
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: middleDot
|
||||
text: "•"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.outlineButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.baseline: dateText.baseline
|
||||
visible: !SettingsData.clockCompactMode
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: dateText
|
||||
text: {
|
||||
if (SettingsData.clockDateFormat && SettingsData.clockDateFormat.length > 0) {
|
||||
return systemClock?.date?.toLocaleDateString(Qt.locale(), SettingsData.clockDateFormat)
|
||||
}
|
||||
|
||||
return systemClock?.date?.toLocaleDateString(Qt.locale(), "ddd d")
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
@@ -237,15 +239,16 @@ BasePill {
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: clockMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
x: -root.leftMargin
|
||||
y: -root.topMargin
|
||||
width: root.width + root.leftMargin + root.rightMargin
|
||||
height: root.height + root.topMargin + root.bottomMargin
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
if (root.popoutTarget && root.popoutTarget.setTriggerPosition) {
|
||||
const globalPos = mapToGlobal(0, 0)
|
||||
const globalPos = root.visualContent.mapToGlobal(0, 0)
|
||||
const currentScreen = root.parentScreen || Screen
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, root.barThickness, width)
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, root.barThickness, root.visualWidth)
|
||||
root.popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen)
|
||||
}
|
||||
root.clockClicked()
|
||||
|
||||
@@ -27,7 +27,6 @@ BasePill {
|
||||
MouseArea {
|
||||
z: 1
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
root.colorPickerRequested()
|
||||
|
||||
@@ -86,7 +86,6 @@ BasePill {
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
onWheel: function(wheelEvent) {
|
||||
let delta = wheelEvent.angleDelta.y
|
||||
@@ -188,7 +187,6 @@ BasePill {
|
||||
id: audioWheelArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
onWheel: function(wheelEvent) {
|
||||
let delta = wheelEvent.angleDelta.y;
|
||||
@@ -228,8 +226,10 @@ BasePill {
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
x: -root.leftMargin
|
||||
y: -root.topMargin
|
||||
width: root.width + root.leftMargin + root.rightMargin
|
||||
height: root.height + root.topMargin + root.bottomMargin
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: {
|
||||
|
||||
@@ -59,7 +59,6 @@ BasePill {
|
||||
return DgopService.cpuUsage.toFixed(0);
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -97,7 +96,6 @@ BasePill {
|
||||
return DgopService.cpuUsage.toFixed(0) + "%";
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@@ -106,7 +104,6 @@ BasePill {
|
||||
StyledTextMetrics {
|
||||
id: cpuBaseline
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
text: "100%"
|
||||
}
|
||||
|
||||
@@ -125,7 +122,6 @@ BasePill {
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: {
|
||||
|
||||
@@ -59,7 +59,6 @@ BasePill {
|
||||
return Math.round(DgopService.cpuTemperature).toString();
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -97,7 +96,6 @@ BasePill {
|
||||
return Math.round(DgopService.cpuTemperature) + "°";
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@@ -106,7 +104,6 @@ BasePill {
|
||||
StyledTextMetrics {
|
||||
id: tempBaseline
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
text: "100°"
|
||||
}
|
||||
|
||||
@@ -125,7 +122,6 @@ BasePill {
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: {
|
||||
|
||||
@@ -10,6 +10,7 @@ BasePill {
|
||||
|
||||
property var widgetData: null
|
||||
property string mountPath: (widgetData && widgetData.mountPath !== undefined) ? widgetData.mountPath : "/"
|
||||
property bool isHovered: mouseArea.containsMouse
|
||||
|
||||
property var selectedMount: {
|
||||
if (!DgopService.diskMounts || DgopService.diskMounts.length === 0) {
|
||||
@@ -114,7 +115,6 @@ BasePill {
|
||||
return root.diskUsagePercent.toFixed(0)
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -149,7 +149,6 @@ BasePill {
|
||||
return root.selectedMount.mount
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@@ -164,7 +163,6 @@ BasePill {
|
||||
return root.diskUsagePercent.toFixed(0) + "%"
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@@ -173,7 +171,6 @@ BasePill {
|
||||
StyledTextMetrics {
|
||||
id: diskBaseline
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
text: "100%"
|
||||
}
|
||||
|
||||
@@ -197,6 +194,7 @@ BasePill {
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
z: 1
|
||||
anchors.fill: parent
|
||||
hoverEnabled: root.isVerticalOrientation
|
||||
|
||||
@@ -17,6 +17,7 @@ BasePill {
|
||||
readonly property int maxCompactWidth: 288
|
||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||
property var activeDesktopEntry: null
|
||||
property bool isHovered: mouseArea.containsMouse
|
||||
|
||||
Component.onCompleted: {
|
||||
updateDesktopEntry()
|
||||
@@ -98,7 +99,7 @@ BasePill {
|
||||
return compactMode ? Math.min(baseWidth, maxCompactWidth - root.horizontalPadding * 2) : Math.min(baseWidth, maxNormalWidth - root.horizontalPadding * 2)
|
||||
}
|
||||
implicitHeight: root.widgetThickness - root.horizontalPadding * 2
|
||||
clip: true
|
||||
clip: false
|
||||
|
||||
IconImage {
|
||||
id: appIcon
|
||||
@@ -146,7 +147,6 @@ BasePill {
|
||||
}
|
||||
font.pixelSize: 10
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
Row {
|
||||
@@ -166,7 +166,6 @@ BasePill {
|
||||
return desktopEntry && desktopEntry.name ? desktopEntry.name : activeWindow.appId;
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
elide: Text.ElideRight
|
||||
@@ -203,7 +202,6 @@ BasePill {
|
||||
return title;
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
elide: Text.ElideRight
|
||||
|
||||
@@ -123,7 +123,6 @@ BasePill {
|
||||
return Math.round(root.displayTemp).toString();
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -161,7 +160,6 @@ BasePill {
|
||||
return Math.round(root.displayTemp) + "°";
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@@ -170,7 +168,6 @@ BasePill {
|
||||
StyledTextMetrics {
|
||||
id: gpuTempBaseline
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
text: "100°"
|
||||
}
|
||||
|
||||
@@ -189,7 +186,6 @@ BasePill {
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: {
|
||||
|
||||
@@ -26,7 +26,6 @@ BasePill {
|
||||
MouseArea {
|
||||
z: 1
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
SessionService.toggleIdleInhibit()
|
||||
|
||||
@@ -42,7 +42,6 @@ BasePill {
|
||||
return root.currentLayout.substring(0, 2).toUpperCase()
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -67,7 +66,6 @@ BasePill {
|
||||
MouseArea {
|
||||
z: 1
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (CompositorService.isNiri) {
|
||||
|
||||
@@ -84,7 +84,6 @@ BasePill {
|
||||
MouseArea {
|
||||
id: customMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.RightButton
|
||||
onPressed: function (mouse){
|
||||
|
||||
@@ -45,53 +45,26 @@ BasePill {
|
||||
implicitHeight: root.playerAvailable ? root.currentContentHeight : 0
|
||||
opacity: root.playerAvailable ? 1 : 0
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "shown"
|
||||
when: root.playerAvailable
|
||||
PropertyChanges {
|
||||
target: parent
|
||||
opacity: 1
|
||||
implicitWidth: root.currentContentWidth
|
||||
implicitHeight: root.currentContentHeight
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "hidden"
|
||||
when: !root.playerAvailable
|
||||
PropertyChanges {
|
||||
target: parent
|
||||
opacity: 0
|
||||
implicitWidth: 0
|
||||
implicitHeight: 0
|
||||
}
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
]
|
||||
transitions: [
|
||||
Transition {
|
||||
from: "shown"
|
||||
to: "hidden"
|
||||
SequentialAnimation {
|
||||
PauseAnimation {
|
||||
duration: 500
|
||||
}
|
||||
NumberAnimation {
|
||||
properties: "opacity,implicitWidth,implicitHeight"
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: "hidden"
|
||||
to: "shown"
|
||||
NumberAnimation {
|
||||
properties: "opacity,implicitWidth,implicitHeight"
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: verticalLayout
|
||||
@@ -189,7 +162,7 @@ BasePill {
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: textWidth
|
||||
height: 20
|
||||
height: root.widgetThickness
|
||||
visible: SettingsData.mediaSize > 0
|
||||
clip: true
|
||||
color: "transparent"
|
||||
@@ -203,7 +176,6 @@ BasePill {
|
||||
text: textContainer.displayText
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
wrapMode: Text.NoWrap
|
||||
x: needsScrolling ? -scrollOffset : 0
|
||||
onTextChanged: {
|
||||
@@ -284,7 +256,6 @@ BasePill {
|
||||
id: prevArea
|
||||
anchors.fill: parent
|
||||
enabled: root.playerAvailable
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (activePlayer) {
|
||||
@@ -342,7 +313,6 @@ BasePill {
|
||||
id: nextArea
|
||||
anchors.fill: parent
|
||||
enabled: root.playerAvailable
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (activePlayer) {
|
||||
|
||||
@@ -54,7 +54,6 @@ BasePill {
|
||||
return (rate / (1024 * 1024)).toFixed(0) + "M"
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.info
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -67,7 +66,6 @@ BasePill {
|
||||
return (rate / (1024 * 1024)).toFixed(0) + "M"
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.error
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -99,7 +97,6 @@ BasePill {
|
||||
StyledText {
|
||||
text: DgopService.networkRxRate > 0 ? root.formatNetworkSpeed(DgopService.networkRxRate) : "0 B/s"
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@@ -109,7 +106,6 @@ BasePill {
|
||||
StyledTextMetrics {
|
||||
id: rxBaseline
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
text: "88.8 MB/s"
|
||||
}
|
||||
|
||||
@@ -137,7 +133,6 @@ BasePill {
|
||||
StyledText {
|
||||
text: DgopService.networkTxRate > 0 ? root.formatNetworkSpeed(DgopService.networkTxRate) : "0 B/s"
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@@ -147,7 +142,6 @@ BasePill {
|
||||
StyledTextMetrics {
|
||||
id: txBaseline
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
text: "88.8 MB/s"
|
||||
}
|
||||
|
||||
|
||||
@@ -47,19 +47,6 @@ BasePill {
|
||||
size: Theme.barIconSize(root.barThickness, -4)
|
||||
color: root.isActive ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 6
|
||||
height: 6
|
||||
radius: 3
|
||||
color: Theme.primary
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.rightMargin: SettingsData.dankBarNoBackground ? 0 : 4
|
||||
anchors.topMargin: SettingsData.dankBarNoBackground ? 0 : 4
|
||||
visible: NotepadStorageService.tabs && NotepadStorageService.tabs.length > 0
|
||||
opacity: 0.8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,6 @@ BasePill {
|
||||
return DgopService.memoryUsage.toFixed(0);
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -97,7 +96,6 @@ BasePill {
|
||||
return DgopService.memoryUsage.toFixed(0) + "%";
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@@ -106,7 +104,6 @@ BasePill {
|
||||
StyledTextMetrics {
|
||||
id: ramBaseline
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
text: "100%"
|
||||
}
|
||||
|
||||
@@ -125,7 +122,6 @@ BasePill {
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
@@ -20,16 +21,27 @@ Item {
|
||||
property real barThickness: 48
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
|
||||
property Item windowRoot: (Window.window ? Window.window.contentItem : null)
|
||||
property int _workspaceUpdateTrigger: 0
|
||||
property int _desktopEntriesUpdateTrigger: 0
|
||||
readonly property var sortedToplevels: {
|
||||
_workspaceUpdateTrigger
|
||||
const toplevels = CompositorService.sortedToplevels
|
||||
if (!toplevels)
|
||||
if (!toplevels || toplevels.length === 0)
|
||||
return []
|
||||
|
||||
if (SettingsData.runningAppsCurrentWorkspace) {
|
||||
return CompositorService.filterCurrentWorkspace(toplevels, parentScreen?.name) || []
|
||||
const filtered = CompositorService.filterCurrentWorkspace(toplevels, parentScreen?.name)
|
||||
return filtered || []
|
||||
}
|
||||
return toplevels
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: DesktopEntries
|
||||
function onApplicationsChanged() {
|
||||
_desktopEntriesUpdateTrigger++
|
||||
}
|
||||
}
|
||||
readonly property var groupedWindows: {
|
||||
if (!SettingsData.runningAppsGroupByApp) {
|
||||
return []
|
||||
@@ -77,6 +89,37 @@ Item {
|
||||
height: isVertical ? calculatedSize : barThickness
|
||||
visible: windowCount > 0
|
||||
|
||||
Connections {
|
||||
target: NiriService
|
||||
function onAllWorkspacesChanged() {
|
||||
_workspaceUpdateTrigger++
|
||||
}
|
||||
function onWindowsChanged() {
|
||||
_workspaceUpdateTrigger++
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Hyprland
|
||||
function onFocusedWorkspaceChanged() {
|
||||
_workspaceUpdateTrigger++
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Hyprland.workspaces
|
||||
function onValuesChanged() {
|
||||
_workspaceUpdateTrigger++
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Hyprland.toplevels
|
||||
function onValuesChanged() {
|
||||
_workspaceUpdateTrigger++
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: visualBackground
|
||||
width: root.isVertical ? root.widgetThickness : root.calculatedSize
|
||||
@@ -212,6 +255,7 @@ Item {
|
||||
property var toplevelObject: toplevelData
|
||||
property int windowCount: isGrouped ? modelData.windows.length : 1
|
||||
property string tooltipText: {
|
||||
root._desktopEntriesUpdateTrigger
|
||||
let appName = "Unknown"
|
||||
if (appId) {
|
||||
const desktopEntry = DesktopEntries.heuristicLookup(appId)
|
||||
@@ -250,6 +294,7 @@ Item {
|
||||
width: 18
|
||||
height: 18
|
||||
source: {
|
||||
root._desktopEntriesUpdateTrigger
|
||||
const moddedId = Paths.moddedAppId(appId)
|
||||
if (moddedId.toLowerCase().includes("steam_app")) {
|
||||
return ""
|
||||
@@ -284,6 +329,7 @@ Item {
|
||||
return !iconImg.visible && !isSteamApp
|
||||
}
|
||||
text: {
|
||||
root._desktopEntriesUpdateTrigger
|
||||
if (!appId) {
|
||||
return "?"
|
||||
}
|
||||
@@ -297,7 +343,6 @@ Item {
|
||||
}
|
||||
font.pixelSize: 10
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -317,7 +362,6 @@ Item {
|
||||
text: windowCount > 9 ? "9+" : windowCount
|
||||
font.pixelSize: 9
|
||||
color: Theme.surface
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,7 +376,6 @@ Item {
|
||||
text: windowTitle
|
||||
font.pixelSize: Theme.barTextSize(barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
}
|
||||
@@ -441,6 +484,7 @@ Item {
|
||||
property var toplevelObject: toplevelData
|
||||
property int windowCount: isGrouped ? modelData.windows.length : 1
|
||||
property string tooltipText: {
|
||||
root._desktopEntriesUpdateTrigger
|
||||
let appName = "Unknown"
|
||||
if (appId) {
|
||||
const desktopEntry = DesktopEntries.heuristicLookup(appId)
|
||||
@@ -478,6 +522,7 @@ Item {
|
||||
width: 18
|
||||
height: 18
|
||||
source: {
|
||||
root._desktopEntriesUpdateTrigger
|
||||
const moddedId = Paths.moddedAppId(appId)
|
||||
if (moddedId.toLowerCase().includes("steam_app")) {
|
||||
return ""
|
||||
@@ -511,6 +556,7 @@ Item {
|
||||
return !iconImg.visible && !isSteamApp
|
||||
}
|
||||
text: {
|
||||
root._desktopEntriesUpdateTrigger
|
||||
if (!appId) {
|
||||
return "?"
|
||||
}
|
||||
@@ -524,7 +570,6 @@ Item {
|
||||
}
|
||||
font.pixelSize: 10
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -544,7 +589,6 @@ Item {
|
||||
text: windowCount > 9 ? "9+" : windowCount
|
||||
font.pixelSize: 9
|
||||
color: Theme.surface
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,7 +602,6 @@ Item {
|
||||
text: windowTitle
|
||||
font.pixelSize: Theme.barTextSize(barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
}
|
||||
@@ -745,7 +788,6 @@ Item {
|
||||
text: I18n.tr("Close")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
|
||||
@@ -131,7 +131,6 @@ Item {
|
||||
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: (mouse) => {
|
||||
if (!delegateRoot.trayItem) {
|
||||
@@ -218,7 +217,6 @@ Item {
|
||||
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: (mouse) => {
|
||||
if (!delegateRoot.trayItem) {
|
||||
@@ -486,7 +484,6 @@ Item {
|
||||
MouseArea {
|
||||
id: backArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: menuRoot.goBack()
|
||||
}
|
||||
@@ -522,7 +519,6 @@ Item {
|
||||
id: itemArea
|
||||
anchors.fill: parent
|
||||
enabled: !menuEntry?.isSeparator && (menuEntry?.enabled !== false)
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
|
||||
@@ -112,7 +112,6 @@ BasePill {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: SystemUpdateService.updateCount.toString()
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
visible: root.hasUpdates && !root.isChecking
|
||||
}
|
||||
@@ -123,7 +122,6 @@ BasePill {
|
||||
MouseArea {
|
||||
z: 1
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
if (popoutTarget && popoutTarget.setTriggerPosition) {
|
||||
|
||||
@@ -9,10 +9,11 @@ BasePill {
|
||||
id: root
|
||||
|
||||
Ref {
|
||||
service: VpnService
|
||||
service: DMSNetworkService
|
||||
}
|
||||
|
||||
property var popoutTarget: null
|
||||
property bool isHovered: clickArea.containsMouse
|
||||
|
||||
signal toggleVpnPopup()
|
||||
|
||||
@@ -24,9 +25,9 @@ BasePill {
|
||||
DankIcon {
|
||||
id: icon
|
||||
|
||||
name: VpnService.isBusy ? "sync" : (VpnService.connected ? "vpn_lock" : "vpn_key_off")
|
||||
name: DMSNetworkService.isBusy ? "sync" : (DMSNetworkService.connected ? "vpn_lock" : "vpn_key_off")
|
||||
size: Theme.barIconSize(root.barThickness, -4)
|
||||
color: VpnService.connected ? Theme.primary : Theme.surfaceText
|
||||
color: DMSNetworkService.connected ? Theme.primary : Theme.surfaceText
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
@@ -59,10 +60,10 @@ BasePill {
|
||||
tooltipLoader.active = true
|
||||
if (tooltipLoader.item) {
|
||||
let tooltipText = ""
|
||||
if (!VpnService.connected) {
|
||||
if (!DMSNetworkService.connected) {
|
||||
tooltipText = "VPN Disconnected"
|
||||
} else {
|
||||
const names = VpnService.activeNames || []
|
||||
const names = DMSNetworkService.activeNames || []
|
||||
if (names.length <= 1) {
|
||||
tooltipText = "VPN Connected • " + (names[0] || "")
|
||||
} else {
|
||||
|
||||
@@ -18,7 +18,7 @@ Item {
|
||||
property var hyprlandOverviewLoader: null
|
||||
property var parentScreen: null
|
||||
readonly property var sortedToplevels: {
|
||||
return CompositorService.filterCurrentWorkspace(CompositorService.sortedToplevels, parentScreen?.name);
|
||||
return CompositorService.filterCurrentWorkspace(CompositorService.sortedToplevels, screenName);
|
||||
}
|
||||
property int currentWorkspace: {
|
||||
if (CompositorService.isNiri) {
|
||||
@@ -224,12 +224,28 @@ Item {
|
||||
|
||||
const currentIndex = realWorkspaces.findIndex(ws => ws === root.currentWorkspace)
|
||||
const validIndex = currentIndex === -1 ? 0 : currentIndex
|
||||
const nextIndex = direction > 0 ? (validIndex + 1) % realWorkspaces.length : (validIndex - 1 + realWorkspaces.length) % realWorkspaces.length
|
||||
const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0)
|
||||
|
||||
if (nextIndex === validIndex) {
|
||||
return
|
||||
}
|
||||
|
||||
NiriService.switchToWorkspace(realWorkspaces[nextIndex] - 1)
|
||||
} else if (CompositorService.isHyprland) {
|
||||
const command = direction > 0 ? "workspace r+1" : "workspace r-1"
|
||||
Hyprland.dispatch(command)
|
||||
const realWorkspaces = getRealWorkspaces()
|
||||
if (realWorkspaces.length < 2) {
|
||||
return
|
||||
}
|
||||
|
||||
const currentIndex = realWorkspaces.findIndex(ws => ws.id === 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
|
||||
}
|
||||
|
||||
Hyprland.dispatch(`workspace ${realWorkspaces[nextIndex].id}`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,6 +439,26 @@ Item {
|
||||
return SettingsData.showWorkspaceApps ? widgetHeight * 0.7 : widgetHeight * 0.5
|
||||
}
|
||||
}
|
||||
|
||||
//DO NOT move this MouseArea. It should be on this level in order for the appMouseArea to work
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: !isPlaceholder
|
||||
cursorShape: isPlaceholder ? Qt.ArrowCursor : Qt.PointingHandCursor
|
||||
enabled: !isPlaceholder
|
||||
onClicked: {
|
||||
if (isPlaceholder) {
|
||||
return
|
||||
}
|
||||
|
||||
if (CompositorService.isNiri) {
|
||||
NiriService.switchToWorkspace(modelData - 1)
|
||||
} else if (CompositorService.isHyprland && modelData?.id) {
|
||||
Hyprland.dispatch(`workspace ${modelData.id}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: dataUpdateTimer
|
||||
@@ -552,7 +588,6 @@ Item {
|
||||
|
||||
MouseArea {
|
||||
id: appMouseArea
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
enabled: isActive
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
@@ -621,7 +656,6 @@ Item {
|
||||
|
||||
MouseArea {
|
||||
id: appMouseArea
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
enabled: isActive
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
@@ -715,25 +749,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: !isPlaceholder
|
||||
cursorShape: isPlaceholder ? Qt.ArrowCursor : Qt.PointingHandCursor
|
||||
enabled: !isPlaceholder
|
||||
onClicked: {
|
||||
if (isPlaceholder) {
|
||||
return
|
||||
}
|
||||
|
||||
if (CompositorService.isNiri) {
|
||||
NiriService.switchToWorkspace(modelData - 1)
|
||||
} else if (CompositorService.isHyprland && modelData?.id) {
|
||||
Hyprland.dispatch(`workspace ${modelData.id}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: updateAllData()
|
||||
|
||||
Connections {
|
||||
@@ -745,6 +760,7 @@ Item {
|
||||
enabled: CompositorService.isNiri
|
||||
function onAllWorkspacesChanged() { delegateRoot.updateAllData() }
|
||||
function onWindowUrgentChanged() { delegateRoot.updateAllData() }
|
||||
function onWindowsChanged() { delegateRoot.updateAllData() }
|
||||
}
|
||||
Connections {
|
||||
target: SettingsData
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import Qt.labs.folderlistmodel
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Modals.FileBrowser
|
||||
import qs.Services
|
||||
@@ -16,11 +16,10 @@ Item {
|
||||
implicitWidth: 700
|
||||
implicitHeight: 410
|
||||
|
||||
property var wallpaperList: []
|
||||
property string wallpaperDir: ""
|
||||
property int currentPage: 0
|
||||
property int itemsPerPage: 16
|
||||
property int totalPages: Math.max(1, Math.ceil(wallpaperList.length / itemsPerPage))
|
||||
property int totalPages: Math.max(1, Math.ceil(wallpaperFolderModel.count / itemsPerPage))
|
||||
property bool active: false
|
||||
property Item focusTarget: wallpaperGrid
|
||||
property Item tabBarItem: null
|
||||
@@ -28,6 +27,8 @@ Item {
|
||||
property Item keyForwardTarget: null
|
||||
property int lastPage: 0
|
||||
property bool enableAnimation: false
|
||||
property string homeDir: StandardPaths.writableLocation(StandardPaths.HomeLocation)
|
||||
property string selectedFileName: ""
|
||||
|
||||
signal requestTabChange(int newIndex)
|
||||
|
||||
@@ -36,6 +37,11 @@ Item {
|
||||
enableAnimation = false
|
||||
lastPage = currentPage
|
||||
}
|
||||
updateSelectedFileName()
|
||||
}
|
||||
|
||||
onGridIndexChanged: {
|
||||
updateSelectedFileName()
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
@@ -45,10 +51,7 @@ Item {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
loadWallpapers()
|
||||
if (visible && active) {
|
||||
setInitialSelection()
|
||||
}
|
||||
loadWallpaperDirectory()
|
||||
}
|
||||
|
||||
onActiveChanged: {
|
||||
@@ -59,16 +62,17 @@ Item {
|
||||
|
||||
function handleKeyEvent(event) {
|
||||
const columns = 4
|
||||
const rows = 4
|
||||
const currentRow = Math.floor(gridIndex / columns)
|
||||
const currentCol = gridIndex % columns
|
||||
const visibleCount = wallpaperGrid.model.length
|
||||
const visibleCount = Math.min(itemsPerPage, wallpaperFolderModel.count - currentPage * itemsPerPage)
|
||||
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
if (gridIndex >= 0) {
|
||||
const item = wallpaperGrid.currentItem
|
||||
if (item && item.wallpaperPath) {
|
||||
SessionData.setWallpaper(item.wallpaperPath)
|
||||
if (gridIndex >= 0 && gridIndex < visibleCount) {
|
||||
const absoluteIndex = currentPage * itemsPerPage + gridIndex
|
||||
if (absoluteIndex < wallpaperFolderModel.count) {
|
||||
const filePath = wallpaperFolderModel.get(absoluteIndex, "filePath")
|
||||
if (filePath) {
|
||||
SessionData.setWallpaper(filePath.toString().replace(/^file:\/\//, ''))
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -76,10 +80,8 @@ Item {
|
||||
|
||||
if (event.key === Qt.Key_Right) {
|
||||
if (gridIndex + 1 < visibleCount) {
|
||||
// Move right within current page
|
||||
gridIndex++
|
||||
} else if (gridIndex === visibleCount - 1 && currentPage < totalPages - 1) {
|
||||
// At last item in page, go to next page
|
||||
} else if (currentPage < totalPages - 1) {
|
||||
gridIndex = 0
|
||||
currentPage++
|
||||
}
|
||||
@@ -88,22 +90,19 @@ Item {
|
||||
|
||||
if (event.key === Qt.Key_Left) {
|
||||
if (gridIndex > 0) {
|
||||
// Move left within current page
|
||||
gridIndex--
|
||||
} else if (gridIndex === 0 && currentPage > 0) {
|
||||
// At first item in page, go to previous page (last item)
|
||||
} else if (currentPage > 0) {
|
||||
currentPage--
|
||||
gridIndex = Math.min(itemsPerPage - 1, wallpaperList.length - currentPage * itemsPerPage - 1)
|
||||
const prevPageCount = Math.min(itemsPerPage, wallpaperFolderModel.count - currentPage * itemsPerPage)
|
||||
gridIndex = prevPageCount - 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_Down) {
|
||||
if (gridIndex + columns < visibleCount) {
|
||||
// Move down within current page
|
||||
gridIndex += columns
|
||||
} else if (gridIndex >= visibleCount - columns && currentPage < totalPages - 1) {
|
||||
// In last row, go to next page
|
||||
} else if (currentPage < totalPages - 1) {
|
||||
gridIndex = currentCol
|
||||
currentPage++
|
||||
}
|
||||
@@ -112,12 +111,10 @@ Item {
|
||||
|
||||
if (event.key === Qt.Key_Up) {
|
||||
if (gridIndex >= columns) {
|
||||
// Move up within current page
|
||||
gridIndex -= columns
|
||||
} else if (gridIndex < columns && currentPage > 0) {
|
||||
// In first row, go to previous page (last row)
|
||||
} else if (currentPage > 0) {
|
||||
currentPage--
|
||||
const prevPageCount = Math.min(itemsPerPage, wallpaperList.length - currentPage * itemsPerPage)
|
||||
const prevPageCount = Math.min(itemsPerPage, wallpaperFolderModel.count - currentPage * itemsPerPage)
|
||||
const prevPageRows = Math.ceil(prevPageCount / columns)
|
||||
gridIndex = (prevPageRows - 1) * columns + currentCol
|
||||
gridIndex = Math.min(gridIndex, prevPageCount - 1)
|
||||
@@ -144,8 +141,9 @@ Item {
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_End && event.modifiers & Qt.ControlModifier) {
|
||||
gridIndex = 0
|
||||
currentPage = totalPages - 1
|
||||
const lastPageCount = Math.min(itemsPerPage, wallpaperFolderModel.count - currentPage * itemsPerPage)
|
||||
gridIndex = Math.max(0, lastPageCount - 1)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -153,40 +151,38 @@ Item {
|
||||
}
|
||||
|
||||
function setInitialSelection() {
|
||||
if (!SessionData.wallpaperPath) {
|
||||
if (!SessionData.wallpaperPath || wallpaperFolderModel.count === 0) {
|
||||
gridIndex = 0
|
||||
updateSelectedFileName()
|
||||
Qt.callLater(() => { enableAnimation = true })
|
||||
return
|
||||
}
|
||||
|
||||
const startIndex = currentPage * itemsPerPage
|
||||
const endIndex = Math.min(startIndex + itemsPerPage, wallpaperList.length)
|
||||
const pageWallpapers = wallpaperList.slice(startIndex, endIndex)
|
||||
|
||||
for (let i = 0; i < pageWallpapers.length; i++) {
|
||||
if (pageWallpapers[i] === SessionData.wallpaperPath) {
|
||||
gridIndex = i
|
||||
for (let i = 0; i < wallpaperFolderModel.count; i++) {
|
||||
const filePath = wallpaperFolderModel.get(i, "filePath")
|
||||
if (filePath && filePath.toString().replace(/^file:\/\//, '') === SessionData.wallpaperPath) {
|
||||
const targetPage = Math.floor(i / itemsPerPage)
|
||||
const targetIndex = i % itemsPerPage
|
||||
currentPage = targetPage
|
||||
gridIndex = targetIndex
|
||||
updateSelectedFileName()
|
||||
Qt.callLater(() => { enableAnimation = true })
|
||||
return
|
||||
}
|
||||
}
|
||||
gridIndex = 0
|
||||
updateSelectedFileName()
|
||||
Qt.callLater(() => { enableAnimation = true })
|
||||
}
|
||||
|
||||
onWallpaperListChanged: {
|
||||
if (visible && active) {
|
||||
setInitialSelection()
|
||||
}
|
||||
}
|
||||
|
||||
function loadWallpapers() {
|
||||
function loadWallpaperDirectory() {
|
||||
const currentWallpaper = SessionData.wallpaperPath
|
||||
|
||||
// Try current wallpaper path / fallback to wallpaperLastPath
|
||||
|
||||
if (!currentWallpaper || currentWallpaper.startsWith("#") || currentWallpaper.startsWith("we:")) {
|
||||
if (CacheData.wallpaperLastPath && CacheData.wallpaperLastPath !== "") {
|
||||
wallpaperDir = CacheData.wallpaperLastPath
|
||||
} else {
|
||||
wallpaperDir = ""
|
||||
wallpaperList = []
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -194,44 +190,51 @@ Item {
|
||||
wallpaperDir = currentWallpaper.substring(0, currentWallpaper.lastIndexOf('/'))
|
||||
}
|
||||
|
||||
function updateWallpaperList() {
|
||||
if (!wallpaperFolderModel || wallpaperFolderModel.count === 0) {
|
||||
wallpaperList = []
|
||||
currentPage = 0
|
||||
gridIndex = 0
|
||||
function updateSelectedFileName() {
|
||||
if (wallpaperFolderModel.count === 0) {
|
||||
selectedFileName = ""
|
||||
return
|
||||
}
|
||||
|
||||
// Build list from FolderListModel
|
||||
const files = []
|
||||
for (let i = 0; i < wallpaperFolderModel.count; i++) {
|
||||
const filePath = wallpaperFolderModel.get(i, "filePath")
|
||||
const absoluteIndex = currentPage * itemsPerPage + gridIndex
|
||||
if (absoluteIndex < wallpaperFolderModel.count) {
|
||||
const filePath = wallpaperFolderModel.get(absoluteIndex, "filePath")
|
||||
if (filePath) {
|
||||
// Remove file:// prefix if present
|
||||
const cleanPath = filePath.toString().replace(/^file:\/\//, '')
|
||||
files.push(cleanPath)
|
||||
const pathStr = filePath.toString().replace(/^file:\/\//, '')
|
||||
selectedFileName = pathStr.substring(pathStr.lastIndexOf('/') + 1)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
wallpaperList = files
|
||||
|
||||
const currentPath = SessionData.wallpaperPath
|
||||
const selectedIndex = currentPath ? wallpaperList.indexOf(currentPath) : -1
|
||||
|
||||
if (selectedIndex >= 0) {
|
||||
currentPage = Math.floor(selectedIndex / itemsPerPage)
|
||||
gridIndex = selectedIndex % itemsPerPage
|
||||
} else {
|
||||
const maxPage = Math.max(0, Math.ceil(files.length / itemsPerPage) - 1)
|
||||
currentPage = Math.min(Math.max(0, currentPage), maxPage)
|
||||
gridIndex = 0
|
||||
}
|
||||
selectedFileName = ""
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
function onWallpaperPathChanged() {
|
||||
loadWallpapers()
|
||||
loadWallpaperDirectory()
|
||||
if (visible && active) {
|
||||
setInitialSelection()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: wallpaperFolderModel
|
||||
function onCountChanged() {
|
||||
if (wallpaperFolderModel.status === FolderListModel.Ready) {
|
||||
if (visible && active) {
|
||||
setInitialSelection()
|
||||
}
|
||||
updateSelectedFileName()
|
||||
}
|
||||
}
|
||||
function onStatusChanged() {
|
||||
if (wallpaperFolderModel.status === FolderListModel.Ready && wallpaperFolderModel.count > 0) {
|
||||
if (visible && active) {
|
||||
setInitialSelection()
|
||||
}
|
||||
updateSelectedFileName()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,18 +249,6 @@ Item {
|
||||
showDirs: false
|
||||
sortField: FolderListModel.Name
|
||||
folder: wallpaperDir ? "file://" + wallpaperDir : ""
|
||||
|
||||
onStatusChanged: {
|
||||
if (status === FolderListModel.Ready) {
|
||||
updateWallpaperList()
|
||||
}
|
||||
}
|
||||
|
||||
onCountChanged: {
|
||||
if (status === FolderListModel.Ready) {
|
||||
updateWallpaperList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
@@ -275,13 +266,11 @@ Item {
|
||||
showHiddenFiles: false
|
||||
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
||||
allowStacking: true
|
||||
|
||||
|
||||
onFileSelected: (path) => {
|
||||
// Set the selected wallpaper
|
||||
const cleanPath = path.replace(/^file:\/\//, '')
|
||||
SessionData.setWallpaper(cleanPath)
|
||||
|
||||
// Extract directory from the selected file and load all wallpapers
|
||||
|
||||
const dirPath = cleanPath.substring(0, cleanPath.lastIndexOf('/'))
|
||||
if (dirPath) {
|
||||
wallpaperDir = dirPath
|
||||
@@ -290,7 +279,7 @@ Item {
|
||||
}
|
||||
close()
|
||||
}
|
||||
|
||||
|
||||
onDialogClosed: {
|
||||
Qt.callLater(() => wallpaperBrowserLoader.active = false)
|
||||
}
|
||||
@@ -336,8 +325,15 @@ Item {
|
||||
|
||||
model: {
|
||||
const startIndex = currentPage * itemsPerPage
|
||||
const endIndex = Math.min(startIndex + itemsPerPage, wallpaperList.length)
|
||||
return wallpaperList.slice(startIndex, endIndex)
|
||||
const endIndex = Math.min(startIndex + itemsPerPage, wallpaperFolderModel.count)
|
||||
const items = []
|
||||
for (let i = startIndex; i < endIndex; i++) {
|
||||
const filePath = wallpaperFolderModel.get(i, "filePath")
|
||||
if (filePath) {
|
||||
items.push(filePath.toString().replace(/^file:\/\//, ''))
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
onModelChanged: {
|
||||
@@ -359,8 +355,11 @@ Item {
|
||||
Connections {
|
||||
target: root
|
||||
function onGridIndexChanged() {
|
||||
if (enableAnimation && wallpaperGrid.count > 0) {
|
||||
if (wallpaperGrid.count > 0) {
|
||||
wallpaperGrid.currentIndex = gridIndex
|
||||
if (!enableAnimation) {
|
||||
wallpaperGrid.positionViewAtIndex(gridIndex, GridView.Contain)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -440,7 +439,6 @@ Item {
|
||||
if (modelData) {
|
||||
SessionData.setWallpaper(modelData)
|
||||
}
|
||||
// Don't steal focus - let mainContainer keep it for keyboard nav
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -449,7 +447,7 @@ Item {
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
visible: wallpaperList.length === 0
|
||||
visible: wallpaperFolderModel.count === 0
|
||||
text: "No wallpapers found\n\nClick the folder icon below to browse"
|
||||
font.pixelSize: 14
|
||||
color: Theme.outline
|
||||
@@ -457,67 +455,84 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
Column {
|
||||
width: parent.width
|
||||
height: 50
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Item {
|
||||
width: (parent.width - controlsRow.width - browseButton.width - Theme.spacingS) / 2
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
Row {
|
||||
id: controlsRow
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
height: 32
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankActionButton {
|
||||
Item {
|
||||
width: (parent.width - controlsRow.width - browseButton.width - Theme.spacingS) / 2
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
Row {
|
||||
id: controlsRow
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: "skip_previous"
|
||||
iconSize: 20
|
||||
buttonSize: 32
|
||||
enabled: currentPage > 0
|
||||
opacity: enabled ? 1.0 : 0.3
|
||||
onClicked: {
|
||||
if (currentPage > 0) {
|
||||
currentPage--
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankActionButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: "skip_previous"
|
||||
iconSize: 20
|
||||
buttonSize: 32
|
||||
enabled: currentPage > 0
|
||||
opacity: enabled ? 1.0 : 0.3
|
||||
onClicked: {
|
||||
if (currentPage > 0) {
|
||||
currentPage--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: wallpaperFolderModel.count > 0 ? `${wallpaperFolderModel.count} wallpapers • ${currentPage + 1} / ${totalPages}` : "No wallpapers"
|
||||
font.pixelSize: 14
|
||||
color: Theme.surfaceText
|
||||
opacity: 0.7
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: "skip_next"
|
||||
iconSize: 20
|
||||
buttonSize: 32
|
||||
enabled: currentPage < totalPages - 1
|
||||
opacity: enabled ? 1.0 : 0.3
|
||||
onClicked: {
|
||||
if (currentPage < totalPages - 1) {
|
||||
currentPage++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
DankActionButton {
|
||||
id: browseButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: wallpaperList.length > 0 ? `${wallpaperList.length} wallpapers • ${currentPage + 1} / ${totalPages}` : "No wallpapers"
|
||||
font.pixelSize: 14
|
||||
color: Theme.surfaceText
|
||||
iconName: "folder_open"
|
||||
iconSize: 20
|
||||
buttonSize: 32
|
||||
opacity: 0.7
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: "skip_next"
|
||||
iconSize: 20
|
||||
buttonSize: 32
|
||||
enabled: currentPage < totalPages - 1
|
||||
opacity: enabled ? 1.0 : 0.3
|
||||
onClicked: {
|
||||
if (currentPage < totalPages - 1) {
|
||||
currentPage++
|
||||
}
|
||||
}
|
||||
onClicked: wallpaperBrowserLoader.active = true
|
||||
}
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
id: browseButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: "folder_open"
|
||||
iconSize: 20
|
||||
buttonSize: 32
|
||||
opacity: 0.7
|
||||
onClicked: wallpaperBrowserLoader.active = true
|
||||
StyledText {
|
||||
width: parent.width
|
||||
height: 18
|
||||
text: selectedFileName
|
||||
font.pixelSize: 12
|
||||
color: Theme.surfaceText
|
||||
opacity: 0.5
|
||||
visible: selectedFileName !== ""
|
||||
elide: Text.ElideMiddle
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,10 +452,10 @@ Item {
|
||||
anchors.top: SettingsData.dockPosition === SettingsData.Position.Top ? parent.top : undefined
|
||||
anchors.left: SettingsData.dockPosition === SettingsData.Position.Left ? parent.left : undefined
|
||||
anchors.right: SettingsData.dockPosition === SettingsData.Position.Right ? parent.right : undefined
|
||||
anchors.bottomMargin: SettingsData.dockPosition === SettingsData.Position.Bottom ? -2 : 0
|
||||
anchors.topMargin: SettingsData.dockPosition === SettingsData.Position.Top ? -2 : 0
|
||||
anchors.leftMargin: SettingsData.dockPosition === SettingsData.Position.Left ? -2 : 0
|
||||
anchors.rightMargin: SettingsData.dockPosition === SettingsData.Position.Right ? -2 : 0
|
||||
anchors.bottomMargin: SettingsData.dockPosition === SettingsData.Position.Bottom ? -(SettingsData.dockSpacing / 2) : 0
|
||||
anchors.topMargin: SettingsData.dockPosition === SettingsData.Position.Top ? -(SettingsData.dockSpacing / 2) : 0
|
||||
anchors.leftMargin: SettingsData.dockPosition === SettingsData.Position.Left ? -(SettingsData.dockSpacing / 2) : 0
|
||||
anchors.rightMargin: SettingsData.dockPosition === SettingsData.Position.Right ? -(SettingsData.dockSpacing / 2) : 0
|
||||
|
||||
sourceComponent: SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right ? columnIndicator : rowIndicator
|
||||
|
||||
@@ -486,9 +486,19 @@ Item {
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: appData && appData.type === "grouped" && appData.windowCount > 1 ? Math.max(3, actualIconSize * 0.1) : Math.max(6, actualIconSize * 0.2)
|
||||
height: Math.max(2, actualIconSize * 0.05)
|
||||
radius: Theme.cornerRadius
|
||||
width: {
|
||||
if (SettingsData.dockIndicatorStyle === "circle") {
|
||||
return Math.max(4, actualIconSize * 0.1)
|
||||
}
|
||||
return appData && appData.type === "grouped" && appData.windowCount > 1 ? Math.max(3, actualIconSize * 0.1) : Math.max(6, actualIconSize * 0.2)
|
||||
}
|
||||
height: {
|
||||
if (SettingsData.dockIndicatorStyle === "circle") {
|
||||
return Math.max(4, actualIconSize * 0.1)
|
||||
}
|
||||
return Math.max(2, actualIconSize * 0.05)
|
||||
}
|
||||
radius: SettingsData.dockIndicatorStyle === "circle" ? width / 2 : Theme.cornerRadius
|
||||
color: {
|
||||
if (!appData) {
|
||||
return "transparent"
|
||||
@@ -533,9 +543,19 @@ Item {
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: Math.max(2, actualIconSize * 0.05)
|
||||
height: appData && appData.type === "grouped" && appData.windowCount > 1 ? Math.max(3, actualIconSize * 0.1) : Math.max(6, actualIconSize * 0.2)
|
||||
radius: Theme.cornerRadius
|
||||
width: {
|
||||
if (SettingsData.dockIndicatorStyle === "circle") {
|
||||
return Math.max(4, actualIconSize * 0.1)
|
||||
}
|
||||
return Math.max(2, actualIconSize * 0.05)
|
||||
}
|
||||
height: {
|
||||
if (SettingsData.dockIndicatorStyle === "circle") {
|
||||
return Math.max(4, actualIconSize * 0.1)
|
||||
}
|
||||
return appData && appData.type === "grouped" && appData.windowCount > 1 ? Math.max(3, actualIconSize * 0.1) : Math.max(6, actualIconSize * 0.2)
|
||||
}
|
||||
radius: SettingsData.dockIndicatorStyle === "circle" ? width / 2 : Theme.cornerRadius
|
||||
color: {
|
||||
if (!appData) {
|
||||
return "transparent"
|
||||
|
||||
@@ -20,6 +20,7 @@ Singleton {
|
||||
property string customThemeFile: ""
|
||||
property string matugenScheme: "scheme-tonal-spot"
|
||||
property bool use24HourClock: true
|
||||
property bool showSeconds: false
|
||||
property bool useFahrenheit: false
|
||||
property bool nightModeEnabled: false
|
||||
property string weatherLocation: "New York, NY"
|
||||
@@ -54,6 +55,7 @@ Singleton {
|
||||
customThemeFile = settings.customThemeFile !== undefined ? settings.customThemeFile : ""
|
||||
matugenScheme = settings.matugenScheme !== undefined ? settings.matugenScheme : "scheme-tonal-spot"
|
||||
use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true
|
||||
showSeconds = settings.showSeconds !== undefined ? settings.showSeconds : false
|
||||
useFahrenheit = settings.useFahrenheit !== undefined ? settings.useFahrenheit : false
|
||||
nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false
|
||||
weatherLocation = settings.weatherLocation !== undefined ? settings.weatherLocation : "New York, NY"
|
||||
|
||||
@@ -186,7 +186,7 @@ Item {
|
||||
|
||||
SystemClock {
|
||||
id: systemClock
|
||||
precision: SystemClock.Minutes
|
||||
precision: SystemClock.Seconds
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -196,40 +196,136 @@ Item {
|
||||
Item {
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -100
|
||||
width: 400
|
||||
width: parent.width
|
||||
height: 140
|
||||
|
||||
StyledText {
|
||||
Row {
|
||||
id: clockText
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
text: {
|
||||
const format = GreetdSettings.use24HourClock ? "HH:mm" : "h:mm AP"
|
||||
spacing: 0
|
||||
|
||||
property string fullTimeStr: {
|
||||
const format = GreetdSettings.use24HourClock
|
||||
? (GreetdSettings.showSeconds ? "HH:mm:ss" : "HH:mm")
|
||||
: (GreetdSettings.showSeconds ? "h:mm:ss AP" : "h:mm AP")
|
||||
return systemClock.date.toLocaleTimeString(Qt.locale(), format)
|
||||
}
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
lineHeight: 0.8
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: clockText.bottom
|
||||
anchors.topMargin: -20
|
||||
text: {
|
||||
if (GreetdSettings.lockDateFormat && GreetdSettings.lockDateFormat.length > 0) {
|
||||
return systemClock.date.toLocaleDateString(Qt.locale(), GreetdSettings.lockDateFormat)
|
||||
}
|
||||
return systemClock.date.toLocaleDateString(Qt.locale(), Locale.LongFormat)
|
||||
property var timeParts: fullTimeStr.split(':')
|
||||
property string hours: timeParts[0] || ""
|
||||
property string minutes: timeParts[1] || ""
|
||||
property string secondsWithAmPm: timeParts.length > 2 ? timeParts[2] : ""
|
||||
property string seconds: secondsWithAmPm.replace(/\s*(AM|PM|am|pm)$/i, '')
|
||||
property string ampm: {
|
||||
const match = fullTimeStr.match(/\s*(AM|PM|am|pm)$/i)
|
||||
return match ? match[0].trim() : ""
|
||||
}
|
||||
property bool hasSeconds: timeParts.length > 2
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hours.length > 1 ? clockText.hours[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hours.length > 1 ? clockText.hours[1] : clockText.hours.length > 0 ? clockText.hours[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: ":"
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.minutes.length > 0 ? clockText.minutes[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.minutes.length > 1 ? clockText.minutes[1] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: clockText.hasSeconds ? ":" : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hasSeconds && clockText.seconds.length > 0 ? clockText.seconds[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hasSeconds && clockText.seconds.length > 1 ? clockText.seconds[1] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 20
|
||||
text: " "
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.ampm !== ""
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: clockText.ampm
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.ampm !== ""
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeXLarge
|
||||
color: "white"
|
||||
opacity: 0.9
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -10
|
||||
text: {
|
||||
if (GreetdSettings.lockDateFormat && GreetdSettings.lockDateFormat.length > 0) {
|
||||
return systemClock.date.toLocaleDateString(Qt.locale(), GreetdSettings.lockDateFormat)
|
||||
}
|
||||
return systemClock.date.toLocaleDateString(Qt.locale(), Locale.LongFormat)
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeXLarge
|
||||
color: "white"
|
||||
opacity: 0.9
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: 80
|
||||
|
||||
@@ -52,7 +52,7 @@ sudo chmod -R g+rX ~/.config/DankMaterialShell ~/.local/state/DankMaterialShell
|
||||
# Create symlinks
|
||||
sudo ln -sf ~/.config/DankMaterialShell/settings.json /var/cache/dms-greeter/settings.json
|
||||
sudo ln -sf ~/.local/state/DankMaterialShell/session.json /var/cache/dms-greeter/session.json
|
||||
sudo ln -sf ~/.cache/quickshell/dankshell/dms-colors.json /var/cache/dms-greeter/colors.json
|
||||
sudo ln -sf ~/.cache/DankMaterialShell/dms-colors.json /var/cache/dms-greeter/colors.json
|
||||
|
||||
# Logout and login for group membership to take effect
|
||||
```
|
||||
@@ -251,11 +251,11 @@ sudo chmod -R g+rX ~/.config/DankMaterialShell ~/.local/state/DankMaterialShell
|
||||
# Create symlinks for theme files
|
||||
sudo ln -sf ~/.config/DankMaterialShell/settings.json /var/cache/dms-greeter/settings.json
|
||||
sudo ln -sf ~/.local/state/DankMaterialShell/session.json /var/cache/dms-greeter/session.json
|
||||
sudo ln -sf ~/.cache/quickshell/dankshell/dms-colors.json /var/cache/dms-greeter/colors.json
|
||||
sudo ln -sf ~/.cache/DankMaterialShell/dms-colors.json /var/cache/dms-greeter/colors.json
|
||||
|
||||
# Logout and login for group membership to take effect
|
||||
```
|
||||
|
||||
**Advanced:** You can override the configuration path with the `DMS_GREET_CFG_DIR` environment variable or the `--cache-dir` flag when using `dms-greeter`. The default is `/var/cache/dms-greeter`.
|
||||
|
||||
The cache directory should be owned by `greeter:greeter` with `770` permissions.
|
||||
The cache directory should be owned by `greeter:greeter` with `770` permissions.
|
||||
|
||||
@@ -68,6 +68,33 @@ if [[ -z "$COMPOSITOR" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
locate_dms_config() {
|
||||
local config_name="$1"
|
||||
local search_paths=()
|
||||
|
||||
local config_home="${XDG_CONFIG_HOME:-$HOME/.config}"
|
||||
search_paths+=("$config_home/quickshell/$config_name")
|
||||
|
||||
search_paths+=("/usr/share/quickshell/$config_name")
|
||||
|
||||
local config_dirs="${XDG_CONFIG_DIRS:-/etc/xdg}"
|
||||
IFS=':' read -ra dirs <<< "$config_dirs"
|
||||
for dir in "${dirs[@]}"; do
|
||||
if [[ -n "$dir" ]]; then
|
||||
search_paths+=("$dir/quickshell/$config_name")
|
||||
fi
|
||||
done
|
||||
|
||||
for path in "${search_paths[@]}"; do
|
||||
if [[ -f "$path/shell.qml" ]]; then
|
||||
echo "$path"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
export XDG_SESSION_TYPE=wayland
|
||||
export QT_QPA_PLATFORM=wayland
|
||||
export QT_WAYLAND_DISABLE_WINDOWDECORATION=1
|
||||
@@ -81,7 +108,14 @@ QS_CMD="qs"
|
||||
if [[ "$DMS_PATH" == /* ]]; then
|
||||
QS_CMD="qs -p $DMS_PATH"
|
||||
else
|
||||
QS_CMD="qs -c $DMS_PATH"
|
||||
RESOLVED_PATH=$(locate_dms_config "$DMS_PATH")
|
||||
if [[ $? -eq 0 && -n "$RESOLVED_PATH" ]]; then
|
||||
echo "Located DMS config at: $RESOLVED_PATH" >&2
|
||||
QS_CMD="qs -p $RESOLVED_PATH"
|
||||
else
|
||||
echo "Error: Could not find DMS config '$DMS_PATH' (shell.qml) in any valid config path" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
case "$COMPOSITOR" in
|
||||
|
||||
@@ -18,7 +18,7 @@ Item {
|
||||
keyboard.target = keyboard_controller.target;
|
||||
isKeyboardActive = true;
|
||||
} else
|
||||
console.info("The keyboard is already shown");
|
||||
console.log("The keyboard is already shown");
|
||||
}
|
||||
|
||||
function hide() {
|
||||
@@ -26,7 +26,7 @@ Item {
|
||||
keyboard.destroy();
|
||||
isKeyboardActive = false;
|
||||
} else
|
||||
console.info("The keyboard is already hidden");
|
||||
console.log("The keyboard is already hidden");
|
||||
}
|
||||
|
||||
// private
|
||||
|
||||
@@ -4,6 +4,7 @@ import QtCore
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.Mpris
|
||||
@@ -23,6 +24,8 @@ Item {
|
||||
property string hyprlandCurrentLayout: ""
|
||||
property string hyprlandKeyboard: ""
|
||||
property int hyprlandLayoutCount: 0
|
||||
property bool lockerReadySent: false
|
||||
property bool lockerReadyArmed: false
|
||||
|
||||
signal unlockRequested
|
||||
|
||||
@@ -43,15 +46,7 @@ Item {
|
||||
hyprlandLayoutUpdateTimer.start()
|
||||
}
|
||||
|
||||
if (SessionService.loginctlAvailable && DMSService.apiVersion >= 2) {
|
||||
DMSService.sendRequest("loginctl.lockerReady", null, response => {
|
||||
if (response.error) {
|
||||
console.warn("LockScreenContent: Failed to signal locker ready:", response.error)
|
||||
} else {
|
||||
console.log("LockScreenContent: Locker ready signaled, inhibitor released")
|
||||
}
|
||||
})
|
||||
}
|
||||
lockerReadyArmed = true
|
||||
}
|
||||
onDemoModeChanged: {
|
||||
if (demoMode) {
|
||||
@@ -65,6 +60,37 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
function sendLockerReadyOnce() {
|
||||
if (lockerReadySent) return;
|
||||
lockerReadySent = true;
|
||||
if (SessionService.loginctlAvailable && DMSService.apiVersion >= 2) {
|
||||
DMSService.sendRequest("loginctl.lockerReady", null, resp => {
|
||||
if (resp?.error) console.warn("lockerReady failed:", resp.error)
|
||||
else console.log("lockerReady sent (afterAnimating/afterRendering)");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function maybeSend() {
|
||||
if (!lockerReadyArmed) return;
|
||||
if (!root.visible || root.opacity <= 0) return;
|
||||
Qt.callLater(() => {
|
||||
if (root.visible && root.opacity > 0)
|
||||
sendLockerReadyOnce();
|
||||
});
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.Window.window
|
||||
enabled: target !== null
|
||||
|
||||
function onAfterAnimating() { maybeSend(); }
|
||||
function onAfterRendering() { maybeSend(); }
|
||||
}
|
||||
|
||||
onVisibleChanged: maybeSend()
|
||||
onOpacityChanged: maybeSend()
|
||||
|
||||
function updateHyprlandLayout() {
|
||||
if (CompositorService.isHyprland) {
|
||||
hyprlandLayoutProcess.running = true
|
||||
@@ -171,7 +197,7 @@ Item {
|
||||
SystemClock {
|
||||
id: systemClock
|
||||
|
||||
precision: SystemClock.Minutes
|
||||
precision: SystemClock.Seconds
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -181,39 +207,134 @@ Item {
|
||||
Item {
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -100
|
||||
width: 400
|
||||
width: parent.width
|
||||
height: 140
|
||||
|
||||
StyledText {
|
||||
Row {
|
||||
id: clockText
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
text: {
|
||||
return systemClock.date.toLocaleTimeString(Qt.locale(), SettingsData.getEffectiveTimeFormat())
|
||||
}
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
lineHeight: 0.8
|
||||
}
|
||||
spacing: 0
|
||||
|
||||
StyledText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: clockText.bottom
|
||||
anchors.topMargin: -20
|
||||
text: {
|
||||
if (SettingsData.lockDateFormat && SettingsData.lockDateFormat.length > 0) {
|
||||
return systemClock.date.toLocaleDateString(Qt.locale(), SettingsData.lockDateFormat)
|
||||
}
|
||||
return systemClock.date.toLocaleDateString(Qt.locale(), Locale.LongFormat)
|
||||
property string fullTimeStr: {
|
||||
const format = SettingsData.getEffectiveTimeFormat()
|
||||
return systemClock.date.toLocaleTimeString(Qt.locale(), format)
|
||||
}
|
||||
property var timeParts: fullTimeStr.split(':')
|
||||
property string hours: timeParts[0] || ""
|
||||
property string minutes: timeParts[1] || ""
|
||||
property string secondsWithAmPm: timeParts.length > 2 ? timeParts[2] : ""
|
||||
property string seconds: secondsWithAmPm.replace(/\s*(AM|PM|am|pm)$/i, '')
|
||||
property string ampm: {
|
||||
const match = fullTimeStr.match(/\s*(AM|PM|am|pm)$/i)
|
||||
return match ? match[0].trim() : ""
|
||||
}
|
||||
property bool hasSeconds: timeParts.length > 2
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hours.length > 1 ? clockText.hours[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hours.length > 1 ? clockText.hours[1] : clockText.hours.length > 0 ? clockText.hours[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: ":"
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.minutes.length > 0 ? clockText.minutes[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.minutes.length > 1 ? clockText.minutes[1] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: clockText.hasSeconds ? ":" : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hasSeconds && clockText.seconds.length > 0 ? clockText.seconds[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hasSeconds && clockText.seconds.length > 1 ? clockText.seconds[1] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 20
|
||||
text: " "
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.ampm !== ""
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: clockText.ampm
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.ampm !== ""
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeXLarge
|
||||
color: "white"
|
||||
opacity: 0.9
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -25
|
||||
text: {
|
||||
if (SettingsData.lockDateFormat && SettingsData.lockDateFormat.length > 0) {
|
||||
return systemClock.date.toLocaleDateString(Qt.locale(), SettingsData.lockDateFormat)
|
||||
}
|
||||
return systemClock.date.toLocaleDateString(Qt.locale(), Locale.LongFormat)
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeXLarge
|
||||
color: "white"
|
||||
opacity: 0.9
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: 50
|
||||
|
||||
@@ -14,10 +14,23 @@ Item {
|
||||
property real barThickness: 48
|
||||
property alias content: contentLoader.sourceComponent
|
||||
property bool isVerticalOrientation: axis?.isVertical ?? false
|
||||
property bool isFirst: false
|
||||
property bool isLast: false
|
||||
property real sectionSpacing: 0
|
||||
property bool isLeftBarEdge: false
|
||||
property bool isRightBarEdge: false
|
||||
property bool isTopBarEdge: false
|
||||
property bool isBottomBarEdge: false
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||
readonly property real visualWidth: isVerticalOrientation ? widgetThickness : (contentLoader.item ? (contentLoader.item.implicitWidth + horizontalPadding * 2) : 0)
|
||||
readonly property real visualHeight: isVerticalOrientation ? (contentLoader.item ? (contentLoader.item.implicitHeight + horizontalPadding * 2) : 0) : widgetThickness
|
||||
readonly property alias visualContent: visualContent
|
||||
readonly property real barEdgeExtension: 1000
|
||||
readonly property real gapExtension: sectionSpacing
|
||||
readonly property real leftMargin: !isVerticalOrientation ? (isLeftBarEdge && isFirst ? barEdgeExtension : (isFirst ? gapExtension : gapExtension / 2)) : 0
|
||||
readonly property real rightMargin: !isVerticalOrientation ? (isRightBarEdge && isLast ? barEdgeExtension : (isLast ? gapExtension : gapExtension / 2)) : 0
|
||||
readonly property real topMargin: isVerticalOrientation ? (isTopBarEdge && isFirst ? barEdgeExtension : (isFirst ? gapExtension : gapExtension / 2)) : 0
|
||||
readonly property real bottomMargin: isVerticalOrientation ? (isBottomBarEdge && isLast ? barEdgeExtension : (isLast ? gapExtension : gapExtension / 2)) : 0
|
||||
|
||||
signal clicked()
|
||||
|
||||
@@ -35,7 +48,8 @@ Item {
|
||||
return "transparent"
|
||||
}
|
||||
|
||||
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor
|
||||
const isHovered = mouseArea.containsMouse || (root.isHovered ?? false)
|
||||
const baseColor = isHovered ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency)
|
||||
}
|
||||
|
||||
@@ -49,17 +63,21 @@ Item {
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
z: -1
|
||||
anchors.fill: parent
|
||||
x: -root.leftMargin
|
||||
y: -root.topMargin
|
||||
width: root.width + root.leftMargin + root.rightMargin
|
||||
height: root.height + root.topMargin + root.bottomMargin
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
root.clicked()
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: {
|
||||
if (popoutTarget && popoutTarget.setTriggerPosition) {
|
||||
const globalPos = mapToGlobal(0, 0)
|
||||
const globalPos = root.visualContent.mapToGlobal(0, 0)
|
||||
const currentScreen = parentScreen || Screen
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth)
|
||||
popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||
}
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,23 @@ Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Component.onCompleted: {
|
||||
property bool isInitialized: false
|
||||
|
||||
function loadValue() {
|
||||
const settings = findSettings()
|
||||
if (settings) {
|
||||
value = settings.loadValue(settingKey, defaultValue)
|
||||
if (settings && settings.pluginService) {
|
||||
const loadedValue = settings.loadValue(settingKey, defaultValue)
|
||||
value = loadedValue
|
||||
isInitialized = true
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
loadValue()
|
||||
}
|
||||
|
||||
onValueChanged: {
|
||||
if (!isInitialized) return
|
||||
const settings = findSettings()
|
||||
if (settings) {
|
||||
settings.saveValue(settingKey, value)
|
||||
|
||||
@@ -278,7 +278,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// Technical Details
|
||||
StyledRect {
|
||||
width: parent.width
|
||||
height: techSection.implicitHeight + Theme.spacingL * 2
|
||||
@@ -307,7 +306,7 @@ Item {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Technical Details")
|
||||
text: I18n.tr("Resources")
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
@@ -322,14 +321,14 @@ Item {
|
||||
rowSpacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Framework:")
|
||||
text: I18n.tr("Website:")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: `<a href="https://quickshell.org" style="text-decoration:none; color:${Theme.primary};">Quickshell</a>`
|
||||
text: `<a href="https://danklinux.com" style="text-decoration:none; color:${Theme.primary};">danklinux.com</a>`
|
||||
linkColor: Theme.primary
|
||||
textFormat: Text.RichText
|
||||
onLinkActivated: url => Qt.openUrlExternally(url)
|
||||
@@ -345,67 +344,25 @@ Item {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Language:")
|
||||
text: I18n.tr("Plugins:")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("QML, JavaScript, Go")
|
||||
text: `<a href="https://plugins.danklinux.com" style="text-decoration:none; color:${Theme.primary};">plugins.danklinux.com</a>`
|
||||
linkColor: Theme.primary
|
||||
textFormat: Text.RichText
|
||||
onLinkActivated: url => Qt.openUrlExternally(url)
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Compositor:")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 4
|
||||
|
||||
StyledText {
|
||||
text: `<a href="https://github.com/YaLTeR/niri" style="text-decoration:none; color:${Theme.primary};">niri</a>`
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
linkColor: Theme.primary
|
||||
textFormat: Text.RichText
|
||||
color: Theme.surfaceVariantText
|
||||
onLinkActivated: url => Qt.openUrlExternally(url)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "&"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceVariantText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: `<a href="https://github.com/hyprwm/Hyprland" style="text-decoration:none; color:${Theme.primary};">hyprland</a>`
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
linkColor: Theme.primary
|
||||
textFormat: Text.RichText
|
||||
color: Theme.surfaceVariantText
|
||||
onLinkActivated: url => Qt.openUrlExternally(url)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,34 +435,61 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Support Section
|
||||
StyledRect {
|
||||
width: parent.width
|
||||
height: supportSection.implicitHeight + Theme.spacingL * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHigh
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
|
||||
Theme.outline.b, 0.2)
|
||||
border.width: 0
|
||||
|
||||
Row {
|
||||
id: supportSection
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
DankIcon {
|
||||
name: "volunteer_activism"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Dank Suite:")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
text: I18n.tr("Support Development")
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 4
|
||||
Item {
|
||||
width: parent.width - parent.spacing - kofiButton.width - supportSection.children[0].width
|
||||
height: 1
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: `<a href="https://danklinux.com" style="text-decoration:none; color:${Theme.primary};">danklinux.com</a>`
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceVariantText
|
||||
linkColor: Theme.primary
|
||||
textFormat: Text.RichText
|
||||
onLinkActivated: url => Qt.openUrlExternally(url)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
}
|
||||
}
|
||||
}
|
||||
DankButton {
|
||||
id: kofiButton
|
||||
text: I18n.tr("Donate on Ko-fi")
|
||||
iconName: "favorite"
|
||||
iconSize: 20
|
||||
backgroundColor: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
||||
textColor: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: Qt.openUrlExternally("https://ko-fi.com/danklinux")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,10 +56,21 @@ Item {
|
||||
}
|
||||
|
||||
function setScreenPreferences(componentId, screenNames) {
|
||||
var prefs = SettingsData.screenPreferences || {
|
||||
};
|
||||
prefs[componentId] = screenNames;
|
||||
SettingsData.setScreenPreferences(prefs);
|
||||
var prefs = SettingsData.screenPreferences || {};
|
||||
var newPrefs = Object.assign({}, prefs);
|
||||
newPrefs[componentId] = screenNames;
|
||||
SettingsData.setScreenPreferences(newPrefs);
|
||||
}
|
||||
|
||||
function getShowOnLastDisplay(componentId) {
|
||||
return SettingsData.showOnLastDisplay && SettingsData.showOnLastDisplay[componentId] || false;
|
||||
}
|
||||
|
||||
function setShowOnLastDisplay(componentId, enabled) {
|
||||
var prefs = SettingsData.showOnLastDisplay || {};
|
||||
var newPrefs = Object.assign({}, prefs);
|
||||
newPrefs[componentId] = enabled;
|
||||
SettingsData.setShowOnLastDisplay(newPrefs);
|
||||
}
|
||||
|
||||
DankFlickable {
|
||||
@@ -660,7 +671,6 @@ Item {
|
||||
|
||||
Column {
|
||||
property string componentId: modelData.id
|
||||
property var selectedScreens: displaysTab.getScreenPreferences(componentId)
|
||||
|
||||
width: parent.width
|
||||
spacing: Theme.spacingXS
|
||||
@@ -669,12 +679,27 @@ Item {
|
||||
width: parent.width
|
||||
text: I18n.tr("All displays")
|
||||
description: I18n.tr("Show on all connected displays")
|
||||
checked: parent.selectedScreens.includes("all")
|
||||
checked: displaysTab.getScreenPreferences(parent.componentId).includes("all")
|
||||
onToggled: (checked) => {
|
||||
if (checked)
|
||||
if (checked) {
|
||||
displaysTab.setScreenPreferences(parent.componentId, ["all"]);
|
||||
else
|
||||
} else {
|
||||
displaysTab.setScreenPreferences(parent.componentId, []);
|
||||
if (["dankBar", "dock", "notifications", "osd", "toast"].includes(parent.componentId)) {
|
||||
displaysTab.setShowOnLastDisplay(parent.componentId, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
width: parent.width
|
||||
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)
|
||||
onToggled: (checked) => {
|
||||
displaysTab.setShowOnLastDisplay(parent.componentId, checked);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,13 +708,13 @@ Item {
|
||||
height: 1
|
||||
color: Theme.outline
|
||||
opacity: 0.2
|
||||
visible: !parent.selectedScreens.includes("all")
|
||||
visible: !displaysTab.getScreenPreferences(parent.componentId).includes("all")
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingXS
|
||||
visible: !parent.selectedScreens.includes("all")
|
||||
visible: !displaysTab.getScreenPreferences(parent.componentId).includes("all")
|
||||
|
||||
Repeater {
|
||||
model: Quickshell.screens
|
||||
|
||||
@@ -329,6 +329,72 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// Indicator Style Section
|
||||
StyledRect {
|
||||
width: parent.width
|
||||
height: indicatorStyleSection.implicitHeight + Theme.spacingL * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHigh
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
|
||||
Theme.outline.b, 0.2)
|
||||
border.width: 0
|
||||
visible: SettingsData.showDock
|
||||
opacity: visible ? 1 : 0
|
||||
|
||||
Column {
|
||||
id: indicatorStyleSection
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "fiber_manual_record"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: indicatorStyleText
|
||||
text: I18n.tr("Indicator Style")
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width - Theme.iconSize - Theme.spacingM - indicatorStyleText.width - indicatorStyleButtonGroup.width - Theme.spacingM * 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
DankButtonGroup {
|
||||
id: indicatorStyleButtonGroup
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
model: ["Circle", "Line"]
|
||||
currentIndex: SettingsData.dockIndicatorStyle === "circle" ? 0 : 1
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (selected) {
|
||||
SettingsData.setDockIndicatorStyle(index === 0 ? "circle" : "line")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Icon Size Section
|
||||
StyledRect {
|
||||
width: parent.width
|
||||
|
||||
@@ -460,6 +460,58 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Theme.outline
|
||||
opacity: 0.2
|
||||
visible: CompositorService.isNiri
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
visible: CompositorService.isNiri
|
||||
|
||||
DankIcon {
|
||||
name: "blur_on"
|
||||
size: Theme.iconSize
|
||||
color: SettingsData.blurWallpaperOnOverview ? Theme.primary : Theme.surfaceVariantText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width - Theme.iconSize - Theme.spacingM - blurOverviewToggle.width - Theme.spacingM
|
||||
spacing: Theme.spacingXS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Blur on Overview")
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Blur wallpaper when niri overview is open")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
id: blurOverviewToggle
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
checked: SettingsData.blurWallpaperOnOverview
|
||||
onToggled: checked => {
|
||||
SettingsData.setBlurWallpaperOnOverview(checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Per-Mode Wallpaper Section - Full Width
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
@@ -959,6 +1011,67 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
width: parent.width
|
||||
height: blurLayerColumn.implicitHeight + Theme.spacingL * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHigh
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||
border.width: 0
|
||||
visible: CompositorService.isNiri
|
||||
|
||||
Column {
|
||||
id: blurLayerColumn
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "blur_on"
|
||||
size: Theme.iconSize
|
||||
color: SettingsData.blurredWallpaperLayer ? Theme.primary : Theme.surfaceVariantText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width - Theme.iconSize - Theme.spacingM - blurLayerToggle.width - Theme.spacingM
|
||||
spacing: Theme.spacingXS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Blur Layer")
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
id: blurLayerToggle
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
checked: SettingsData.blurredWallpaperLayer
|
||||
onToggled: checked => {
|
||||
SettingsData.setBlurredWallpaperLayer(checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
width: parent.width
|
||||
height: lightModeRow.implicitHeight + Theme.spacingL * 2
|
||||
|
||||
@@ -247,8 +247,8 @@ FocusScope {
|
||||
property bool hasSettings: pluginData && pluginData.settings !== undefined && pluginData.settings !== ""
|
||||
property bool isExpanded: pluginsTab.expandedPluginId === pluginId
|
||||
property bool hasUpdate: {
|
||||
if (DMSService.apiVersion < 8) return true
|
||||
return pluginsTab.installedPluginsData[pluginDirectoryName] || pluginsTab.installedPluginsData[pluginId] || pluginsTab.installedPluginsData[pluginName] || false
|
||||
if (DMSService.apiVersion < 8) return false
|
||||
return pluginsTab.installedPluginsData[pluginId] || pluginsTab.installedPluginsData[pluginName] || false
|
||||
}
|
||||
|
||||
|
||||
@@ -346,10 +346,9 @@ FocusScope {
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
const currentPluginDirName = pluginDelegate.pluginDirectoryName
|
||||
const currentPluginName = pluginDelegate.pluginName
|
||||
const currentPluginId = pluginDelegate.pluginId
|
||||
DMSService.update(currentPluginDirName, response => {
|
||||
DMSService.update(currentPluginName, response => {
|
||||
if (response.error) {
|
||||
ToastService.showError("Update failed: " + response.error)
|
||||
} else {
|
||||
@@ -397,9 +396,8 @@ FocusScope {
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
const currentPluginDirName = pluginDelegate.pluginDirectoryName
|
||||
const currentPluginName = pluginDelegate.pluginName
|
||||
DMSService.uninstall(currentPluginDirName, response => {
|
||||
DMSService.uninstall(currentPluginName, response => {
|
||||
if (response.error) {
|
||||
ToastService.showError("Uninstall failed: " + response.error)
|
||||
} else {
|
||||
@@ -677,8 +675,8 @@ FocusScope {
|
||||
for (var i = 0; i < plugins.length; i++) {
|
||||
var plugin = plugins[i]
|
||||
var hasUpdate = plugin.hasUpdate || false
|
||||
if (plugin.path) {
|
||||
pluginMap[plugin.path] = hasUpdate
|
||||
if (plugin.id) {
|
||||
pluginMap[plugin.id] = hasUpdate
|
||||
}
|
||||
if (plugin.name) {
|
||||
pluginMap[plugin.name] = hasUpdate
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
@@ -6,6 +7,7 @@ import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
import qs.Modules
|
||||
import qs.Services
|
||||
|
||||
Variants {
|
||||
model: {
|
||||
@@ -467,6 +469,15 @@ Variants {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
anchors.fill: parent
|
||||
source: effectLoader.active ? effectLoader.item : (root.actualTransitionType === "none" ? currentWallpaper : null)
|
||||
visible: CompositorService.isNiri && SettingsData.blurWallpaperOnOverview && NiriService.inOverview
|
||||
blurEnabled: true
|
||||
blur: 0.8
|
||||
blurMax: 48
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ Item {
|
||||
signal itemsChanged()
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("LauncherExample: Plugin loaded")
|
||||
console.info("LauncherExample: Plugin loaded")
|
||||
|
||||
// Load custom trigger from settings
|
||||
if (pluginService) {
|
||||
|
||||
@@ -57,10 +57,10 @@ PluginComponent {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("WallpaperWatcherDaemon: Started monitoring wallpaper changes")
|
||||
console.info("WallpaperWatcherDaemon: Started monitoring wallpaper changes")
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
console.log("WallpaperWatcherDaemon: Stopped monitoring wallpaper changes")
|
||||
console.info("WallpaperWatcherDaemon: Stopped monitoring wallpaper changes")
|
||||
}
|
||||
}
|
||||
|
||||
36
README.md
36
README.md
@@ -324,11 +324,11 @@ make && sudo make install
|
||||
#### 4.1 Core optional dependencies
|
||||
```bash
|
||||
# Arch Linux
|
||||
sudo pacman -S cava wl-clipboard cliphist brightnessctl qt6-multimedia
|
||||
sudo pacman -S cava wl-clipboard cliphist brightnessctl qt6-multimedia accountsservice
|
||||
paru -S matugen-bin dgop
|
||||
|
||||
# Fedora
|
||||
sudo dnf install cava wl-clipboard brightnessctl qt6-qtmultimedia
|
||||
sudo dnf install cava wl-clipboard brightnessctl qt6-qtmultimedia accountsservice
|
||||
sudo dnf copr enable avengemedia/danklinux && sudo dnf install cliphist ghostty hyprpicker material-symbols-fonts matugen
|
||||
```
|
||||
Note: by enabling and installing the avengemedia/dms copr above, these core dependencies will automatically be available for use.
|
||||
@@ -352,6 +352,7 @@ sudo sh -c "curl -L https://github.com/AvengeMedia/dgop/releases/latest/download
|
||||
- `cava`: Audio visualizer
|
||||
- `cliphist`: Clipboard history
|
||||
- `qt6-multimedia`: System sound support
|
||||
- `accountsservice`: Ability to sync
|
||||
|
||||
## Compositor Configuration
|
||||
|
||||
@@ -438,6 +439,37 @@ binds {
|
||||
spawn "dms" "ipc" "call" "night" "toggle";
|
||||
}
|
||||
}
|
||||
|
||||
// You probably want this too
|
||||
debug {
|
||||
honor-xdg-activation-with-invalid-serial
|
||||
}
|
||||
```
|
||||
|
||||
#### niri - place wallpaper on overview (blurred or non-blurred)
|
||||
|
||||
Place the wallpaper on backdrop using a layer-rule for a contiguous surface.
|
||||
|
||||
```kdl
|
||||
layer-rule {
|
||||
match namespace="quickshell"
|
||||
place-within-backdrop true
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
}
|
||||
```
|
||||
|
||||
#### niri theming
|
||||
|
||||
@@ -573,7 +573,7 @@ Singleton {
|
||||
if (!detectSoundsAvailability()) {
|
||||
console.warn("AudioService: QtMultimedia not available - sound effects disabled")
|
||||
} else {
|
||||
console.log("AudioService: Sound effects enabled")
|
||||
console.info("AudioService: Sound effects enabled")
|
||||
checkGsettings()
|
||||
Qt.callLater(createSoundPlayers)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Bluetooth
|
||||
import qs.Services
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
@@ -15,6 +16,7 @@ Singleton {
|
||||
readonly property bool enabled: (adapter && adapter.enabled) ?? false
|
||||
readonly property bool discovering: (adapter && adapter.discovering) ?? false
|
||||
readonly property var devices: adapter ? adapter.devices : null
|
||||
readonly property bool enhancedPairingAvailable: DMSService.dmsAvailable && DMSService.apiVersion >= 9 && DMSService.capabilities.includes("bluetooth")
|
||||
readonly property bool connected: {
|
||||
if (!adapter || !adapter.devices) {
|
||||
return false
|
||||
@@ -173,6 +175,25 @@ Singleton {
|
||||
device.connect()
|
||||
}
|
||||
|
||||
function pairDevice(device, callback) {
|
||||
if (!device) {
|
||||
if (callback) callback({error: "Invalid device"})
|
||||
return
|
||||
}
|
||||
|
||||
// The DMS backend actually implements a bluez agent, so we can pair anything
|
||||
if (enhancedPairingAvailable) {
|
||||
const devicePath = getDevicePath(device)
|
||||
DMSService.bluetoothPair(devicePath, callback)
|
||||
return
|
||||
}
|
||||
|
||||
// Quickshell does not implement a bluez agent, so we can try to pair but only with devices that don't require a passcode
|
||||
device.trusted = true
|
||||
device.connect()
|
||||
if (callback) callback({success: true})
|
||||
}
|
||||
|
||||
function getCardName(device) {
|
||||
if (!device) {
|
||||
return ""
|
||||
@@ -180,6 +201,14 @@ Singleton {
|
||||
return `bluez_card.${device.address.replace(/:/g, "_")}`
|
||||
}
|
||||
|
||||
function getDevicePath(device) {
|
||||
if (!device || !device.address) {
|
||||
return ""
|
||||
}
|
||||
const adapterPath = adapter ? "/org/bluez/hci0" : "/org/bluez/hci0"
|
||||
return `${adapterPath}/dev_${device.address.replace(/:/g, "_")}`
|
||||
}
|
||||
|
||||
function isAudioDevice(device) {
|
||||
if (!device) {
|
||||
return false
|
||||
|
||||
@@ -76,6 +76,10 @@ Singleton {
|
||||
target: Hyprland
|
||||
function onFocusedWorkspaceChanged() { root.scheduleSort() }
|
||||
}
|
||||
Connections {
|
||||
target: NiriService
|
||||
function onWindowsChanged() { root.scheduleSort() }
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
detectCompositor()
|
||||
@@ -328,7 +332,7 @@ Singleton {
|
||||
isHyprland = true
|
||||
isNiri = false
|
||||
compositor = "hyprland"
|
||||
console.log("CompositorService: Detected Hyprland")
|
||||
console.info("CompositorService: Detected Hyprland")
|
||||
try {
|
||||
Hyprland.refreshToplevels()
|
||||
} catch(e) {}
|
||||
@@ -341,7 +345,7 @@ Singleton {
|
||||
isNiri = true
|
||||
isHyprland = false
|
||||
compositor = "niri"
|
||||
console.log("CompositorService: Detected Niri with socket:", niriSocket)
|
||||
console.info("CompositorService: Detected Niri with socket:", niriSocket)
|
||||
NiriService.generateNiriBinds()
|
||||
} else {
|
||||
isHyprland = false
|
||||
|
||||
@@ -11,6 +11,7 @@ Singleton {
|
||||
id: root
|
||||
|
||||
property bool networkAvailable: false
|
||||
property string backend: ""
|
||||
|
||||
property string networkStatus: "disconnected"
|
||||
property string primaryConnection: ""
|
||||
@@ -68,6 +69,24 @@ Singleton {
|
||||
property string wifiPassword: ""
|
||||
property string forgetSSID: ""
|
||||
|
||||
property var vpnProfiles: []
|
||||
property var vpnActive: []
|
||||
property bool vpnAvailable: false
|
||||
property bool vpnIsBusy: false
|
||||
|
||||
property alias profiles: root.vpnProfiles
|
||||
property alias activeConnections: root.vpnActive
|
||||
property var activeUuids: vpnActive.map(v => v.uuid).filter(u => !!u)
|
||||
property var activeNames: vpnActive.map(v => v.name).filter(n => !!n)
|
||||
property string activeUuid: activeUuids.length > 0 ? activeUuids[0] : ""
|
||||
property string activeName: activeNames.length > 0 ? activeNames[0] : ""
|
||||
property string activeDevice: vpnActive.length > 0 ? (vpnActive[0].device || "") : ""
|
||||
property string activeState: vpnActive.length > 0 ? (vpnActive[0].state || "") : ""
|
||||
property bool vpnConnected: activeUuids.length > 0
|
||||
property alias available: root.vpnAvailable
|
||||
property alias isBusy: root.vpnIsBusy
|
||||
property alias connected: root.vpnConnected
|
||||
|
||||
property string networkInfoSSID: ""
|
||||
property string networkInfoDetails: ""
|
||||
property bool networkInfoLoading: false
|
||||
@@ -93,7 +112,7 @@ Singleton {
|
||||
|
||||
signal networksUpdated
|
||||
signal connectionChanged
|
||||
signal credentialsNeeded(string token, string ssid, string setting, var fields, var hints, string reason)
|
||||
signal credentialsNeeded(string token, string ssid, string setting, var fields, var hints, string reason, string connType, string connName, string vpnService)
|
||||
|
||||
readonly property string socketPath: Quickshell.env("DMS_SOCKET")
|
||||
|
||||
@@ -108,10 +127,8 @@ Singleton {
|
||||
target: DMSService
|
||||
|
||||
function onNetworkStateUpdate(data) {
|
||||
if (DMSService.verboseLogs) {
|
||||
const networksCount = data.wifiNetworks?.length ?? "null"
|
||||
console.log("NetworkManagerService: Subscription update received, networks:", networksCount)
|
||||
}
|
||||
const networksCount = data.wifiNetworks?.length ?? "null"
|
||||
console.log("DMSNetworkService: Subscription update received, networks:", networksCount)
|
||||
updateState(data)
|
||||
}
|
||||
}
|
||||
@@ -150,10 +167,6 @@ Singleton {
|
||||
|
||||
networkAvailable = DMSService.capabilities.includes("network")
|
||||
|
||||
if (DMSService.verboseLogs) {
|
||||
console.log("NetworkManagerService: Network available:", networkAvailable)
|
||||
}
|
||||
|
||||
if (networkAvailable && !stateInitialized) {
|
||||
stateInitialized = true
|
||||
getState()
|
||||
@@ -169,7 +182,11 @@ Singleton {
|
||||
credentialsReason = data.reason || "Credentials required"
|
||||
credentialsRequested = true
|
||||
|
||||
credentialsNeeded(credentialsToken, credentialsSSID, credentialsSetting, credentialsFields, credentialsHints, credentialsReason)
|
||||
const connType = data.connType || ""
|
||||
const connName = data.name || data.connectionId || ""
|
||||
const vpnService = data.vpnService || ""
|
||||
|
||||
credentialsNeeded(credentialsToken, credentialsSSID, credentialsSetting, credentialsFields, credentialsHints, credentialsReason, connType, connName, vpnService)
|
||||
}
|
||||
|
||||
function addRef() {
|
||||
@@ -195,9 +212,6 @@ Singleton {
|
||||
if (response.result) {
|
||||
updateState(response.result)
|
||||
if (!initialStateFetched && response.result.wifiEnabled && (!response.result.wifiNetworks || response.result.wifiNetworks.length === 0)) {
|
||||
if (DMSService.verboseLogs) {
|
||||
console.log("NetworkManagerService: Initial state has no networks, triggering scan")
|
||||
}
|
||||
initialStateFetched = true
|
||||
Qt.callLater(() => scanWifi())
|
||||
}
|
||||
@@ -209,6 +223,8 @@ Singleton {
|
||||
const previousConnecting = isConnecting
|
||||
const previousConnectingSSID = connectingSSID
|
||||
|
||||
backend = state.backend || ""
|
||||
vpnAvailable = networkAvailable && backend === "networkmanager"
|
||||
networkStatus = state.networkStatus || "disconnected"
|
||||
primaryConnection = state.primaryConnection || ""
|
||||
|
||||
@@ -251,6 +267,12 @@ Singleton {
|
||||
networksUpdated()
|
||||
}
|
||||
|
||||
if (state.vpnProfiles) {
|
||||
vpnProfiles = state.vpnProfiles
|
||||
}
|
||||
|
||||
vpnActive = state.vpnActive || []
|
||||
|
||||
userPreference = state.preference || "auto"
|
||||
isConnecting = state.isConnecting || false
|
||||
connectingSSID = state.connectingSSID || ""
|
||||
@@ -259,10 +281,8 @@ Singleton {
|
||||
|
||||
if (pendingConnectionSSID) {
|
||||
if (wifiConnected && currentWifiSSID === pendingConnectionSSID && wifiIP) {
|
||||
if (DMSService.verboseLogs) {
|
||||
const elapsed = Date.now() - pendingConnectionStartTime
|
||||
console.log("NetworkManagerService: Successfully connected to", pendingConnectionSSID, "in", elapsed, "ms")
|
||||
}
|
||||
const elapsed = Date.now() - pendingConnectionStartTime
|
||||
console.info("DMSNetworkService: Successfully connected to", pendingConnectionSSID, "in", elapsed, "ms")
|
||||
ToastService.showInfo(`Connected to ${pendingConnectionSSID}`)
|
||||
|
||||
if (userPreference === "wifi" || userPreference === "auto") {
|
||||
@@ -272,20 +292,17 @@ Singleton {
|
||||
pendingConnectionSSID = ""
|
||||
connectionStatus = "connected"
|
||||
} else if (previousConnecting && !isConnecting && !wifiConnected) {
|
||||
const elapsed = Date.now() - pendingConnectionStartTime
|
||||
const isCancellationError = connectionError === "user-canceled"
|
||||
const isBadCredentials = connectionError === "bad-credentials"
|
||||
|
||||
if (elapsed < 5000) {
|
||||
if (DMSService.verboseLogs) {
|
||||
console.log("NetworkManagerService: Quick connection failure, likely authentication error")
|
||||
}
|
||||
if (isCancellationError) {
|
||||
connectionStatus = "cancelled"
|
||||
pendingConnectionSSID = ""
|
||||
} else if (isBadCredentials) {
|
||||
connectionStatus = "invalid_password"
|
||||
pendingConnectionSSID = ""
|
||||
} else {
|
||||
if (DMSService.verboseLogs) {
|
||||
console.log("NetworkManagerService: Connection failed for", pendingConnectionSSID)
|
||||
}
|
||||
if (connectionError === "connection-failed") {
|
||||
ToastService.showError(I18n.tr("Connection failed. Check password and try again."))
|
||||
} else if (connectionError) {
|
||||
if (connectionError) {
|
||||
ToastService.showError(I18n.tr("Failed to connect to ") + pendingConnectionSSID)
|
||||
}
|
||||
connectionStatus = "failed"
|
||||
@@ -327,18 +344,12 @@ Singleton {
|
||||
function scanWifi() {
|
||||
if (!networkAvailable || isScanning || !wifiEnabled) return
|
||||
|
||||
if (DMSService.verboseLogs) {
|
||||
console.log("NetworkManagerService: Starting WiFi scan...")
|
||||
}
|
||||
isScanning = true
|
||||
DMSService.sendRequest("network.wifi.scan", null, response => {
|
||||
isScanning = false
|
||||
if (response.error) {
|
||||
console.warn("NetworkManagerService: WiFi scan failed:", response.error)
|
||||
console.warn("DMSNetworkService: WiFi scan failed:", response.error)
|
||||
} else {
|
||||
if (DMSService.verboseLogs) {
|
||||
console.log("NetworkManagerService: Scan completed")
|
||||
}
|
||||
Qt.callLater(() => getState())
|
||||
}
|
||||
})
|
||||
@@ -378,8 +389,8 @@ Singleton {
|
||||
|
||||
DMSService.sendRequest("network.wifi.connect", params, response => {
|
||||
if (response.error) {
|
||||
if (DMSService.verboseLogs) {
|
||||
console.log("NetworkManagerService: Connection request failed:", response.error)
|
||||
if (connectionStatus === "cancelled") {
|
||||
return
|
||||
}
|
||||
|
||||
connectionError = response.error
|
||||
@@ -387,10 +398,6 @@ Singleton {
|
||||
pendingConnectionSSID = ""
|
||||
connectionStatus = "failed"
|
||||
ToastService.showError(I18n.tr("Failed to start connection to ") + ssid)
|
||||
} else {
|
||||
if (DMSService.verboseLogs) {
|
||||
console.log("NetworkManagerService: Connection request sent for", ssid)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -410,7 +417,12 @@ Singleton {
|
||||
}
|
||||
|
||||
function submitCredentials(token, secrets, save) {
|
||||
if (!networkAvailable || DMSService.apiVersion < 7) return
|
||||
console.log("submitCredentials: networkAvailable=" + networkAvailable + " apiVersion=" + DMSService.apiVersion)
|
||||
|
||||
if (!networkAvailable || DMSService.apiVersion < 7) {
|
||||
console.warn("submitCredentials: Aborting - networkAvailable=" + networkAvailable + " apiVersion=" + DMSService.apiVersion)
|
||||
return
|
||||
}
|
||||
|
||||
const params = {
|
||||
token: token,
|
||||
@@ -418,15 +430,11 @@ Singleton {
|
||||
save: save || false
|
||||
}
|
||||
|
||||
if (DMSService.verboseLogs) {
|
||||
console.log("NetworkManagerService: Submitting credentials for token", token)
|
||||
}
|
||||
|
||||
credentialsRequested = false
|
||||
|
||||
DMSService.sendRequest("network.credentials.submit", params, response => {
|
||||
if (response.error) {
|
||||
console.warn("NetworkManagerService: Failed to submit credentials:", response.error)
|
||||
console.warn("DMSNetworkService: Failed to submit credentials:", response.error)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -435,21 +443,16 @@ Singleton {
|
||||
if (!networkAvailable || DMSService.apiVersion < 7) return
|
||||
|
||||
const params = {
|
||||
token: token,
|
||||
cancel: true
|
||||
}
|
||||
|
||||
if (DMSService.verboseLogs) {
|
||||
console.log("NetworkManagerService: Cancelling credentials for token", token)
|
||||
token: token
|
||||
}
|
||||
|
||||
credentialsRequested = false
|
||||
pendingConnectionSSID = ""
|
||||
connectionStatus = "cancelled"
|
||||
|
||||
DMSService.sendRequest("network.credentials.submit", params, response => {
|
||||
DMSService.sendRequest("network.credentials.cancel", params, response => {
|
||||
if (response.error) {
|
||||
console.warn("NetworkManagerService: Failed to cancel credentials:", response.error)
|
||||
console.warn("DMSNetworkService: Failed to cancel credentials:", response.error)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -703,31 +706,125 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
function refreshVpnProfiles() {
|
||||
if (!vpnAvailable) return
|
||||
|
||||
DMSService.sendRequest("network.vpn.profiles", null, response => {
|
||||
if (response.result) {
|
||||
vpnProfiles = response.result
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function refreshVpnActive() {
|
||||
if (!vpnAvailable) return
|
||||
|
||||
DMSService.sendRequest("network.vpn.active", null, response => {
|
||||
if (response.result) {
|
||||
vpnActive = response.result
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function connectVpn(uuidOrName, singleActive = false) {
|
||||
if (!vpnAvailable || vpnIsBusy) return
|
||||
|
||||
vpnIsBusy = true
|
||||
|
||||
const params = {
|
||||
uuidOrName: uuidOrName,
|
||||
singleActive: singleActive
|
||||
}
|
||||
|
||||
DMSService.sendRequest("network.vpn.connect", params, response => {
|
||||
vpnIsBusy = false
|
||||
|
||||
if (response.error) {
|
||||
ToastService.showError(I18n.tr("Failed to connect VPN"))
|
||||
} else {
|
||||
Qt.callLater(() => getState())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function connect(uuidOrName, singleActive = false) {
|
||||
connectVpn(uuidOrName, singleActive)
|
||||
}
|
||||
|
||||
function disconnectVpn(uuidOrName) {
|
||||
if (!vpnAvailable || vpnIsBusy) return
|
||||
|
||||
vpnIsBusy = true
|
||||
|
||||
const params = {
|
||||
uuidOrName: uuidOrName
|
||||
}
|
||||
|
||||
DMSService.sendRequest("network.vpn.disconnect", params, response => {
|
||||
vpnIsBusy = false
|
||||
|
||||
if (response.error) {
|
||||
ToastService.showError(I18n.tr("Failed to disconnect VPN"))
|
||||
} else {
|
||||
Qt.callLater(() => getState())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function disconnect(uuidOrName) {
|
||||
disconnectVpn(uuidOrName)
|
||||
}
|
||||
|
||||
function disconnectAllVpns() {
|
||||
if (!vpnAvailable || vpnIsBusy) return
|
||||
|
||||
vpnIsBusy = true
|
||||
|
||||
DMSService.sendRequest("network.vpn.disconnectAll", null, response => {
|
||||
vpnIsBusy = false
|
||||
|
||||
if (response.error) {
|
||||
ToastService.showError(I18n.tr("Failed to disconnect VPNs"))
|
||||
} else {
|
||||
Qt.callLater(() => getState())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function disconnectAllActive() {
|
||||
disconnectAllVpns()
|
||||
}
|
||||
|
||||
function toggleVpn(uuid) {
|
||||
if (uuid) {
|
||||
if (isActiveVpnUuid(uuid)) {
|
||||
disconnectVpn(uuid)
|
||||
} else {
|
||||
connectVpn(uuid)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (vpnProfiles.length > 0) {
|
||||
connectVpn(vpnProfiles[0].uuid)
|
||||
}
|
||||
}
|
||||
|
||||
function toggle(uuid) {
|
||||
toggleVpn(uuid)
|
||||
}
|
||||
|
||||
function isActiveVpnUuid(uuid) {
|
||||
return activeUuids && activeUuids.indexOf(uuid) !== -1
|
||||
}
|
||||
|
||||
function isActiveUuid(uuid) {
|
||||
return isActiveVpnUuid(uuid)
|
||||
}
|
||||
|
||||
function refreshNetworkState() {
|
||||
if (networkAvailable) {
|
||||
getState()
|
||||
}
|
||||
}
|
||||
|
||||
function splitNmcliFields(line) {
|
||||
const parts = []
|
||||
let cur = ""
|
||||
let escape = false
|
||||
for (var i = 0; i < line.length; i++) {
|
||||
const ch = line[i]
|
||||
if (escape) {
|
||||
cur += ch
|
||||
escape = false
|
||||
} else if (ch === '\\') {
|
||||
escape = true
|
||||
} else if (ch === ':') {
|
||||
parts.push(cur)
|
||||
cur = ""
|
||||
} else {
|
||||
cur += ch
|
||||
}
|
||||
}
|
||||
parts.push(cur)
|
||||
return parts
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ Singleton {
|
||||
property bool subscribeConnected: false
|
||||
|
||||
readonly property string socketPath: Quickshell.env("DMS_SOCKET")
|
||||
readonly property bool verboseLogs: Quickshell.env("DMS_VERBOSE_LOGS") === "1"
|
||||
|
||||
property var pendingRequests: ({})
|
||||
property int requestIdCounter: 0
|
||||
@@ -42,6 +41,7 @@ Singleton {
|
||||
signal loginctlEvent(var event)
|
||||
signal capabilitiesReceived()
|
||||
signal credentialsRequest(var data)
|
||||
signal bluetoothPairingRequest(var data)
|
||||
|
||||
Component.onCompleted: {
|
||||
if (socketPath && socketPath.length > 0) {
|
||||
@@ -169,9 +169,7 @@ Singleton {
|
||||
return
|
||||
}
|
||||
|
||||
if (root.verboseLogs) {
|
||||
console.log("DMSService: Request socket <<", line)
|
||||
}
|
||||
console.log("DMSService: Request socket <<", line)
|
||||
|
||||
try {
|
||||
const response = JSON.parse(line)
|
||||
@@ -201,9 +199,7 @@ Singleton {
|
||||
return
|
||||
}
|
||||
|
||||
if (root.verboseLogs) {
|
||||
console.log("DMSService: Subscribe socket <<", line)
|
||||
}
|
||||
console.log("DMSService: Subscribe socket <<", line)
|
||||
|
||||
try {
|
||||
const response = JSON.parse(line)
|
||||
@@ -220,9 +216,7 @@ Singleton {
|
||||
"method": "subscribe"
|
||||
}
|
||||
|
||||
if (verboseLogs) {
|
||||
console.log("DMSService: Subscribing to all services")
|
||||
}
|
||||
console.log("DMSService: Subscribing to all services")
|
||||
subscribeSocket.send(request)
|
||||
}
|
||||
|
||||
@@ -253,7 +247,7 @@ Singleton {
|
||||
apiVersion = data.apiVersion || 0
|
||||
capabilities = data.capabilities || []
|
||||
|
||||
console.log("DMSService: Connected (API v" + apiVersion + ") -", JSON.stringify(capabilities))
|
||||
console.info("DMSService: Connected (API v" + apiVersion + ") -", JSON.stringify(capabilities))
|
||||
|
||||
if (apiVersion < expectedApiVersion) {
|
||||
ToastService.showError("DMS server is outdated (API v" + apiVersion + ", expected v" + expectedApiVersion + ")")
|
||||
@@ -270,11 +264,14 @@ Singleton {
|
||||
} else {
|
||||
loginctlStateUpdate(data)
|
||||
}
|
||||
} else if (service === "bluetooth.pairing") {
|
||||
bluetoothPairingRequest(data)
|
||||
}
|
||||
}
|
||||
|
||||
function sendRequest(method, params, callback) {
|
||||
if (!isConnected) {
|
||||
console.warn("DMSService.sendRequest: Not connected, method:", method)
|
||||
if (callback) {
|
||||
callback({
|
||||
"error": "not connected to DMS socket"
|
||||
@@ -298,6 +295,7 @@ Singleton {
|
||||
pendingRequests[id] = callback
|
||||
}
|
||||
|
||||
console.log("DMSService.sendRequest: Sending request id=" + id + " method=" + method)
|
||||
requestSocket.send(request)
|
||||
}
|
||||
|
||||
@@ -408,4 +406,48 @@ Singleton {
|
||||
function unlockSession(callback) {
|
||||
sendRequest("loginctl.unlock", null, callback)
|
||||
}
|
||||
|
||||
function bluetoothPair(devicePath, callback) {
|
||||
sendRequest("bluetooth.pair", {
|
||||
"device": devicePath
|
||||
}, callback)
|
||||
}
|
||||
|
||||
function bluetoothConnect(devicePath, callback) {
|
||||
sendRequest("bluetooth.connect", {
|
||||
"device": devicePath
|
||||
}, callback)
|
||||
}
|
||||
|
||||
function bluetoothDisconnect(devicePath, callback) {
|
||||
sendRequest("bluetooth.disconnect", {
|
||||
"device": devicePath
|
||||
}, callback)
|
||||
}
|
||||
|
||||
function bluetoothRemove(devicePath, callback) {
|
||||
sendRequest("bluetooth.remove", {
|
||||
"device": devicePath
|
||||
}, callback)
|
||||
}
|
||||
|
||||
function bluetoothTrust(devicePath, callback) {
|
||||
sendRequest("bluetooth.trust", {
|
||||
"device": devicePath
|
||||
}, callback)
|
||||
}
|
||||
|
||||
function bluetoothSubmitPairing(token, secrets, accept, callback) {
|
||||
sendRequest("bluetooth.pairing.submit", {
|
||||
"token": token,
|
||||
"secrets": secrets,
|
||||
"accept": accept
|
||||
}, callback)
|
||||
}
|
||||
|
||||
function bluetoothCancelPairing(token, callback) {
|
||||
sendRequest("bluetooth.pairing.cancel", {
|
||||
"token": token
|
||||
}, callback)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,7 +682,7 @@ Singleton {
|
||||
// Prefer PRETTY_NAME, fallback to NAME
|
||||
const distroName = prettyName || name || "Linux"
|
||||
distribution = distroName
|
||||
console.log("Detected distribution:", distroName)
|
||||
console.info("Detected distribution:", distroName)
|
||||
} catch (e) {
|
||||
console.warn("Failed to parse /etc/os-release:", e)
|
||||
distribution = "Linux"
|
||||
|
||||
@@ -66,9 +66,11 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
function setBrightness(percentage, device) {
|
||||
function setBrightness(percentage, device, suppressOsd) {
|
||||
setBrightnessInternal(percentage, device)
|
||||
brightnessChanged()
|
||||
if (!suppressOsd) {
|
||||
brightnessChanged()
|
||||
}
|
||||
}
|
||||
|
||||
function setCurrentDevice(deviceName, saveToSession = false) {
|
||||
@@ -509,7 +511,7 @@ Singleton {
|
||||
if (ddcAvailable) {
|
||||
ddcDisplayDetectionProcess.running = true
|
||||
} else {
|
||||
console.log("DisplayService: ddcutil not available")
|
||||
console.info("DisplayService: ddcutil not available")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -545,7 +547,7 @@ Singleton {
|
||||
}
|
||||
|
||||
ddcDevices = newDdcDevices
|
||||
console.log("DisplayService: Found", ddcDevices.length, "DDC displays")
|
||||
console.info("DisplayService: Found", ddcDevices.length, "DDC displays")
|
||||
|
||||
// Queue initial brightness readings for DDC devices
|
||||
ddcInitQueue = []
|
||||
|
||||
@@ -55,7 +55,7 @@ Singleton {
|
||||
|
||||
function createIdleMonitors() {
|
||||
if (!idleMonitorAvailable) {
|
||||
console.log("IdleService: IdleMonitor not available, skipping creation")
|
||||
console.info("IdleService: IdleMonitor not available, skipping creation")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ Singleton {
|
||||
if (!idleMonitorAvailable) {
|
||||
console.warn("IdleService: IdleMonitor not available - power management disabled. This requires a newer version of Quickshell.")
|
||||
} else {
|
||||
console.log("IdleService: Initialized with idle monitoring support")
|
||||
console.info("IdleService: Initialized with idle monitoring support")
|
||||
createIdleMonitors()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ Singleton {
|
||||
function activate() {
|
||||
if (!isActive) {
|
||||
isActive = true
|
||||
console.log("LegacyNetworkService: Activating...")
|
||||
console.info("LegacyNetworkService: Activating...")
|
||||
initializeDBusMonitors()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ Singleton {
|
||||
id: root
|
||||
|
||||
property bool networkAvailable: activeService !== null
|
||||
property string backend: activeService?.backend ?? ""
|
||||
property string networkStatus: activeService?.networkStatus ?? "disconnected"
|
||||
property string primaryConnection: activeService?.primaryConnection ?? ""
|
||||
|
||||
@@ -79,7 +80,7 @@ Singleton {
|
||||
|
||||
signal networksUpdated
|
||||
signal connectionChanged
|
||||
signal credentialsNeeded(string token, string ssid, string setting, var fields, var hints, string reason)
|
||||
signal credentialsNeeded(string token, string ssid, string setting, var fields, var hints, string reason, string connType, string connName, string vpnService)
|
||||
|
||||
property bool usingLegacy: false
|
||||
property var activeService: null
|
||||
@@ -87,9 +88,9 @@ Singleton {
|
||||
readonly property string socketPath: Quickshell.env("DMS_SOCKET")
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("NetworkService: Initializing...")
|
||||
console.info("NetworkService: Initializing...")
|
||||
if (!socketPath || socketPath.length === 0) {
|
||||
console.log("NetworkService: DMS_SOCKET not set, using LegacyNetworkService")
|
||||
console.info("NetworkService: DMS_SOCKET not set, using LegacyNetworkService")
|
||||
useLegacyService()
|
||||
} else {
|
||||
console.log("NetworkService: DMS_SOCKET found, waiting for capabilities...")
|
||||
@@ -97,17 +98,17 @@ Singleton {
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: NetworkManagerService
|
||||
target: DMSNetworkService
|
||||
|
||||
function onNetworkAvailableChanged() {
|
||||
if (!activeService && NetworkManagerService.networkAvailable) {
|
||||
console.log("NetworkService: Network capability detected, using NetworkManagerService")
|
||||
activeService = NetworkManagerService
|
||||
if (!activeService && DMSNetworkService.networkAvailable) {
|
||||
console.info("NetworkService: Network capability detected, using DMSNetworkService")
|
||||
activeService = DMSNetworkService
|
||||
usingLegacy = false
|
||||
console.log("NetworkService: Switched to NetworkManagerService, networkAvailable:", networkAvailable)
|
||||
console.info("NetworkService: Switched to DMSNetworkService, networkAvailable:", networkAvailable)
|
||||
connectSignals()
|
||||
} else if (!activeService && !NetworkManagerService.networkAvailable && socketPath && socketPath.length > 0) {
|
||||
console.log("NetworkService: Network capability not available in DMS, using LegacyNetworkService")
|
||||
} else if (!activeService && !DMSNetworkService.networkAvailable && socketPath && socketPath.length > 0) {
|
||||
console.info("NetworkService: Network capability not available in DMS, using LegacyNetworkService")
|
||||
useLegacyService()
|
||||
}
|
||||
}
|
||||
@@ -116,7 +117,7 @@ Singleton {
|
||||
function useLegacyService() {
|
||||
activeService = LegacyNetworkService
|
||||
usingLegacy = true
|
||||
console.log("NetworkService: Switched to LegacyNetworkService, networkAvailable:", networkAvailable)
|
||||
console.info("NetworkService: Switched to LegacyNetworkService, networkAvailable:", networkAvailable)
|
||||
if (LegacyNetworkService.activate) {
|
||||
LegacyNetworkService.activate()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pragma Singleton
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
pragma ComponentBehavior
|
||||
|
||||
import QtCore
|
||||
import QtQuick
|
||||
@@ -37,7 +37,7 @@ Singleton {
|
||||
property bool matugenSuppression: false
|
||||
property bool configGenerationPending: false
|
||||
|
||||
signal windowUrgentChanged()
|
||||
signal windowUrgentChanged
|
||||
|
||||
Component.onCompleted: fetchOutputs()
|
||||
|
||||
@@ -89,7 +89,7 @@ Singleton {
|
||||
|
||||
onExited: exitCode => {
|
||||
if (exitCode === 0) {
|
||||
console.log("NiriService: Generated layout config at", configPath)
|
||||
console.info("NiriService: Generated layout config at", configPath)
|
||||
return
|
||||
}
|
||||
console.warn("NiriService: Failed to write layout config, exit code:", exitCode)
|
||||
@@ -102,7 +102,7 @@ Singleton {
|
||||
|
||||
onExited: exitCode => {
|
||||
if (exitCode === 0) {
|
||||
console.log("NiriService: Generated binds config at", bindsPath)
|
||||
console.info("NiriService: Generated binds config at", bindsPath)
|
||||
return
|
||||
}
|
||||
console.warn("NiriService: Failed to write binds config, exit code:", exitCode)
|
||||
@@ -140,28 +140,30 @@ Singleton {
|
||||
}
|
||||
|
||||
function fetchOutputs() {
|
||||
if (!CompositorService.isNiri) return
|
||||
if (!CompositorService.isNiri)
|
||||
return
|
||||
Proc.runCommand("niri-fetch-outputs", ["niri", "msg", "-j", "outputs"], (output, exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("NiriService: Failed to fetch outputs, exit code:", exitCode)
|
||||
return
|
||||
}
|
||||
try {
|
||||
const outputsData = JSON.parse(output)
|
||||
outputs = outputsData
|
||||
console.log("NiriService: Loaded", Object.keys(outputsData).length, "outputs")
|
||||
updateDisplayScales()
|
||||
if (windows.length > 0) {
|
||||
windows = sortWindowsByLayout(windows)
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("NiriService: Failed to parse outputs:", e)
|
||||
}
|
||||
})
|
||||
if (exitCode !== 0) {
|
||||
console.warn("NiriService: Failed to fetch outputs, exit code:", exitCode)
|
||||
return
|
||||
}
|
||||
try {
|
||||
const outputsData = JSON.parse(output)
|
||||
outputs = outputsData
|
||||
console.info("NiriService: Loaded", Object.keys(outputsData).length, "outputs")
|
||||
updateDisplayScales()
|
||||
if (windows.length > 0) {
|
||||
windows = sortWindowsByLayout(windows)
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("NiriService: Failed to parse outputs:", e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function updateDisplayScales() {
|
||||
if (!outputs || Object.keys(outputs).length === 0) return
|
||||
if (!outputs || Object.keys(outputs).length === 0)
|
||||
return
|
||||
|
||||
const scales = {}
|
||||
for (const outputName in outputs) {
|
||||
@@ -175,49 +177,47 @@ Singleton {
|
||||
}
|
||||
|
||||
function sortWindowsByLayout(windowList) {
|
||||
return [...windowList].sort((a, b) => {
|
||||
const aWorkspace = workspaces[a.workspace_id]
|
||||
const bWorkspace = workspaces[b.workspace_id]
|
||||
|
||||
if (aWorkspace && bWorkspace) {
|
||||
const aOutput = aWorkspace.output
|
||||
const bOutput = bWorkspace.output
|
||||
|
||||
const aOutputInfo = outputs[aOutput]
|
||||
const bOutputInfo = outputs[bOutput]
|
||||
|
||||
if (aOutputInfo && bOutputInfo && aOutputInfo.logical && bOutputInfo.logical) {
|
||||
if (aOutputInfo.logical.x !== bOutputInfo.logical.x) {
|
||||
return aOutputInfo.logical.x - bOutputInfo.logical.x
|
||||
}
|
||||
if (aOutputInfo.logical.y !== bOutputInfo.logical.y) {
|
||||
return aOutputInfo.logical.y - bOutputInfo.logical.y
|
||||
}
|
||||
}
|
||||
|
||||
if (aOutput === bOutput && aWorkspace.idx !== bWorkspace.idx) {
|
||||
return aWorkspace.idx - bWorkspace.idx
|
||||
const enriched = windowList.map(w => {
|
||||
const ws = workspaces[w.workspace_id]
|
||||
if (!ws) {
|
||||
return {
|
||||
window: w,
|
||||
outputX: 999999,
|
||||
outputY: 999999,
|
||||
wsIdx: 999999,
|
||||
col: 999999,
|
||||
row: 999999
|
||||
}
|
||||
}
|
||||
|
||||
if (a.workspace_id === b.workspace_id && a.layout && b.layout) {
|
||||
if (a.layout.pos_in_scrolling_layout && b.layout.pos_in_scrolling_layout) {
|
||||
const aPos = a.layout.pos_in_scrolling_layout
|
||||
const bPos = b.layout.pos_in_scrolling_layout
|
||||
const outputInfo = outputs[ws.output]
|
||||
const outputX = (outputInfo && outputInfo.logical) ? outputInfo.logical.x : 999999
|
||||
const outputY = (outputInfo && outputInfo.logical) ? outputInfo.logical.y : 999999
|
||||
|
||||
if (aPos.length > 1 && bPos.length > 1) {
|
||||
if (aPos[0] !== bPos[0]) {
|
||||
return aPos[0] - bPos[0]
|
||||
}
|
||||
if (aPos[1] !== bPos[1]) {
|
||||
return aPos[1] - bPos[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
const pos = w.layout?.pos_in_scrolling_layout
|
||||
const col = (pos && pos.length >= 2) ? pos[0] : 999999
|
||||
const row = (pos && pos.length >= 2) ? pos[1] : 999999
|
||||
|
||||
return {
|
||||
window: w,
|
||||
outputX: outputX,
|
||||
outputY: outputY,
|
||||
wsIdx: ws.idx,
|
||||
col: col,
|
||||
row: row
|
||||
}
|
||||
|
||||
return a.id - b.id
|
||||
})
|
||||
|
||||
enriched.sort((a, b) => {
|
||||
if (a.outputX !== b.outputX) return a.outputX - b.outputX
|
||||
if (a.outputY !== b.outputY) return a.outputY - b.outputY
|
||||
if (a.wsIdx !== b.wsIdx) return a.wsIdx - b.wsIdx
|
||||
if (a.col !== b.col) return a.col - b.col
|
||||
if (a.row !== b.row) return a.row - b.row
|
||||
return a.window.id - b.window.id
|
||||
})
|
||||
|
||||
return enriched.map(e => e.window)
|
||||
}
|
||||
|
||||
function handleNiriEvent(event) {
|
||||
@@ -233,6 +233,9 @@ Singleton {
|
||||
case 'WorkspaceActiveWindowChanged':
|
||||
handleWorkspaceActiveWindowChanged(event.WorkspaceActiveWindowChanged)
|
||||
break
|
||||
case 'WindowFocusChanged':
|
||||
handleWindowFocusChanged(event.WindowFocusChanged)
|
||||
break
|
||||
case 'WindowsChanged':
|
||||
handleWindowsChanged(event.WindowsChanged)
|
||||
break
|
||||
@@ -267,14 +270,18 @@ Singleton {
|
||||
}
|
||||
|
||||
function handleWorkspacesChanged(data) {
|
||||
const workspaces = {}
|
||||
const newWorkspaces = {}
|
||||
|
||||
for (const ws of data.workspaces) {
|
||||
workspaces[ws.id] = ws
|
||||
const oldWs = root.workspaces[ws.id]
|
||||
newWorkspaces[ws.id] = ws
|
||||
if (oldWs && oldWs.active_window_id !== undefined) {
|
||||
newWorkspaces[ws.id].active_window_id = oldWs.active_window_id
|
||||
}
|
||||
}
|
||||
|
||||
root.workspaces = workspaces
|
||||
allWorkspaces = [...data.workspaces].sort((a, b) => a.idx - b.idx)
|
||||
root.workspaces = newWorkspaces
|
||||
allWorkspaces = Object.values(newWorkspaces).sort((a, b) => a.idx - b.idx)
|
||||
|
||||
focusedWorkspaceIndex = allWorkspaces.findIndex(w => w.is_focused)
|
||||
if (focusedWorkspaceIndex >= 0) {
|
||||
@@ -296,33 +303,101 @@ Singleton {
|
||||
}
|
||||
const output = ws.output
|
||||
|
||||
const updatedWorkspaces = {}
|
||||
|
||||
for (const id in root.workspaces) {
|
||||
const workspace = root.workspaces[id]
|
||||
const got_activated = workspace.id === data.id
|
||||
|
||||
const updatedWs = {}
|
||||
for (let prop in workspace) {
|
||||
updatedWs[prop] = workspace[prop]
|
||||
}
|
||||
|
||||
if (workspace.output === output) {
|
||||
workspace.is_active = got_activated
|
||||
updatedWs.is_active = got_activated
|
||||
}
|
||||
|
||||
if (data.focused) {
|
||||
workspace.is_focused = got_activated
|
||||
updatedWs.is_focused = got_activated
|
||||
}
|
||||
|
||||
updatedWorkspaces[id] = updatedWs
|
||||
}
|
||||
|
||||
root.workspaces = updatedWorkspaces
|
||||
|
||||
focusedWorkspaceId = data.id
|
||||
focusedWorkspaceIndex = allWorkspaces.findIndex(w => w.id === data.id)
|
||||
focusedWorkspaceIndex = Object.values(updatedWorkspaces).findIndex(w => w.id === data.id)
|
||||
|
||||
if (focusedWorkspaceIndex >= 0) {
|
||||
currentOutput = allWorkspaces[focusedWorkspaceIndex].output || ""
|
||||
const ws = Object.values(updatedWorkspaces)[focusedWorkspaceIndex]
|
||||
currentOutput = ws.output || ""
|
||||
}
|
||||
|
||||
allWorkspaces = Object.values(root.workspaces).sort((a, b) => a.idx - b.idx)
|
||||
allWorkspaces = Object.values(updatedWorkspaces).sort((a, b) => a.idx - b.idx)
|
||||
|
||||
updateCurrentOutputWorkspaces()
|
||||
workspacesChanged()
|
||||
}
|
||||
|
||||
function handleWindowFocusChanged(data) {
|
||||
const focusedWindowId = data.id
|
||||
|
||||
let focusedWindow = null
|
||||
const updatedWindows = []
|
||||
|
||||
for (var i = 0; i < windows.length; i++) {
|
||||
const w = windows[i]
|
||||
const updatedWindow = {}
|
||||
|
||||
for (let prop in w) {
|
||||
updatedWindow[prop] = w[prop]
|
||||
}
|
||||
|
||||
updatedWindow.is_focused = (w.id === focusedWindowId)
|
||||
if (updatedWindow.is_focused) {
|
||||
focusedWindow = updatedWindow
|
||||
}
|
||||
|
||||
updatedWindows.push(updatedWindow)
|
||||
}
|
||||
|
||||
windows = updatedWindows
|
||||
|
||||
if (focusedWindow) {
|
||||
const ws = root.workspaces[focusedWindow.workspace_id]
|
||||
if (ws && ws.active_window_id !== focusedWindowId) {
|
||||
const updatedWs = {}
|
||||
for (let prop in ws) {
|
||||
updatedWs[prop] = ws[prop]
|
||||
}
|
||||
updatedWs.active_window_id = focusedWindowId
|
||||
|
||||
const updatedWorkspaces = {}
|
||||
for (const id in root.workspaces) {
|
||||
updatedWorkspaces[id] = id === focusedWindow.workspace_id ? updatedWs : root.workspaces[id]
|
||||
}
|
||||
root.workspaces = updatedWorkspaces
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleWorkspaceActiveWindowChanged(data) {
|
||||
const ws = root.workspaces[data.workspace_id]
|
||||
if (ws) {
|
||||
const updatedWs = {}
|
||||
for (let prop in ws) {
|
||||
updatedWs[prop] = ws[prop]
|
||||
}
|
||||
updatedWs.active_window_id = data.active_window_id
|
||||
|
||||
const updatedWorkspaces = {}
|
||||
for (const id in root.workspaces) {
|
||||
updatedWorkspaces[id] = id === data.workspace_id ? updatedWs : root.workspaces[id]
|
||||
}
|
||||
root.workspaces = updatedWorkspaces
|
||||
}
|
||||
|
||||
const updatedWindows = []
|
||||
|
||||
for (var i = 0; i < windows.length; i++) {
|
||||
@@ -354,7 +429,8 @@ Singleton {
|
||||
}
|
||||
|
||||
function handleWindowOpenedOrChanged(data) {
|
||||
if (!data.window) return
|
||||
if (!data.window)
|
||||
return
|
||||
|
||||
const window = data.window
|
||||
const existingIndex = windows.findIndex(w => w.id === window.id)
|
||||
@@ -363,14 +439,14 @@ Singleton {
|
||||
const updatedWindows = [...windows]
|
||||
updatedWindows[existingIndex] = window
|
||||
windows = sortWindowsByLayout(updatedWindows)
|
||||
return
|
||||
} else {
|
||||
windows = sortWindowsByLayout([...windows, window])
|
||||
}
|
||||
|
||||
windows = sortWindowsByLayout([...windows, window])
|
||||
}
|
||||
|
||||
function handleWindowLayoutsChanged(data) {
|
||||
if (!data.changes) return
|
||||
if (!data.changes)
|
||||
return
|
||||
|
||||
const updatedWindows = [...windows]
|
||||
let hasChanges = false
|
||||
@@ -380,7 +456,8 @@ Singleton {
|
||||
const layoutData = change[1]
|
||||
|
||||
const windowIndex = updatedWindows.findIndex(w => w.id === windowId)
|
||||
if (windowIndex < 0) continue
|
||||
if (windowIndex < 0)
|
||||
continue
|
||||
|
||||
const updatedWindow = {}
|
||||
for (var prop in updatedWindows[windowIndex]) {
|
||||
@@ -391,14 +468,15 @@ Singleton {
|
||||
hasChanges = true
|
||||
}
|
||||
|
||||
if (!hasChanges) return
|
||||
if (!hasChanges)
|
||||
return
|
||||
|
||||
windows = sortWindowsByLayout(updatedWindows)
|
||||
windowsChanged()
|
||||
}
|
||||
|
||||
function handleOutputsChanged(data) {
|
||||
if (!data.outputs) return
|
||||
if (!data.outputs)
|
||||
return
|
||||
outputs = data.outputs
|
||||
updateDisplayScales()
|
||||
windows = sortWindowsByLayout(windows)
|
||||
@@ -442,14 +520,22 @@ Singleton {
|
||||
|
||||
function handleWorkspaceUrgencyChanged(data) {
|
||||
const ws = root.workspaces[data.id]
|
||||
if (!ws) return
|
||||
if (!ws)
|
||||
return
|
||||
|
||||
ws.is_urgent = data.urgent
|
||||
|
||||
const idx = allWorkspaces.findIndex(w => w.id === data.id)
|
||||
if (idx >= 0) {
|
||||
allWorkspaces[idx].is_urgent = data.urgent
|
||||
const updatedWs = {}
|
||||
for (let prop in ws) {
|
||||
updatedWs[prop] = ws[prop]
|
||||
}
|
||||
updatedWs.is_urgent = data.urgent
|
||||
|
||||
const updatedWorkspaces = {}
|
||||
for (const id in root.workspaces) {
|
||||
updatedWorkspaces[id] = id === data.id ? updatedWs : root.workspaces[id]
|
||||
}
|
||||
root.workspaces = updatedWorkspaces
|
||||
|
||||
allWorkspaces = Object.values(updatedWorkspaces).sort((a, b) => a.idx - b.idx)
|
||||
|
||||
windowUrgentChanged()
|
||||
}
|
||||
@@ -465,41 +551,86 @@ Singleton {
|
||||
}
|
||||
|
||||
function send(request) {
|
||||
if (!CompositorService.isNiri || !requestSocket.connected) return false
|
||||
if (!CompositorService.isNiri || !requestSocket.connected)
|
||||
return false
|
||||
requestSocket.send(request)
|
||||
return true
|
||||
}
|
||||
|
||||
function doScreenTransition() {
|
||||
return send({"Action": {"DoScreenTransition": {"delay_ms": 0}}})
|
||||
return send({
|
||||
"Action": {
|
||||
"DoScreenTransition": {
|
||||
"delay_ms": 0
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function toggleOverview() {
|
||||
return send({"Action": {"ToggleOverview": {}}})
|
||||
return send({
|
||||
"Action": {
|
||||
"ToggleOverview": {}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function switchToWorkspace(workspaceIndex) {
|
||||
return send({"Action": {"FocusWorkspace": {"reference": {"Index": workspaceIndex}}}})
|
||||
return send({
|
||||
"Action": {
|
||||
"FocusWorkspace": {
|
||||
"reference": {
|
||||
"Index": workspaceIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function focusWindow(windowId) {
|
||||
return send({"Action": {"FocusWindow": {"id": windowId}}})
|
||||
return send({
|
||||
"Action": {
|
||||
"FocusWindow": {
|
||||
"id": windowId
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function powerOffMonitors() {
|
||||
return send({"Action": {"PowerOffMonitors": {}}})
|
||||
return send({
|
||||
"Action": {
|
||||
"PowerOffMonitors": {}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function powerOnMonitors() {
|
||||
return send({"Action": {"PowerOnMonitors": {}}})
|
||||
return send({
|
||||
"Action": {
|
||||
"PowerOnMonitors": {}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function cycleKeyboardLayout() {
|
||||
return send({"Action": {"SwitchLayout": {"layout": "Next"}}})
|
||||
return send({
|
||||
"Action": {
|
||||
"SwitchLayout": {
|
||||
"layout": "Next"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function quit() {
|
||||
return send({"Action": {"Quit": {"skip_confirmation": true}}})
|
||||
return send({
|
||||
"Action": {
|
||||
"Quit": {
|
||||
"skip_confirmation": true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getCurrentOutputWorkspaceNumbers() {
|
||||
@@ -526,13 +657,17 @@ Singleton {
|
||||
}
|
||||
|
||||
function findNiriWindow(toplevel) {
|
||||
if (!toplevel.appId) return null
|
||||
if (!toplevel.appId)
|
||||
return null
|
||||
|
||||
for (var j = 0; j < windows.length; j++) {
|
||||
const niriWindow = windows[j]
|
||||
if (niriWindow.app_id === toplevel.appId) {
|
||||
if (!niriWindow.title || niriWindow.title === toplevel.title) {
|
||||
return {"niriIndex": j, "niriWindow": niriWindow}
|
||||
return {
|
||||
"niriIndex": j,
|
||||
"niriWindow": niriWindow
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -549,35 +684,50 @@ Singleton {
|
||||
|
||||
for (const niriWindow of sortWindowsByLayout(windows)) {
|
||||
let bestMatch = null
|
||||
let bestScore = -1
|
||||
|
||||
for (const toplevel of toplevels) {
|
||||
if (usedToplevels.has(toplevel)) continue
|
||||
if (usedToplevels.has(toplevel))
|
||||
continue
|
||||
|
||||
if (toplevel.appId === niriWindow.app_id) {
|
||||
if (niriWindow.title && toplevel.title === niriWindow.title) {
|
||||
bestMatch = toplevel
|
||||
break
|
||||
let score = 1
|
||||
|
||||
if (niriWindow.title && toplevel.title) {
|
||||
if (toplevel.title === niriWindow.title) {
|
||||
score = 3
|
||||
} else if (toplevel.title.includes(niriWindow.title) || niriWindow.title.includes(toplevel.title)) {
|
||||
score = 2
|
||||
}
|
||||
}
|
||||
if (!niriWindow.title && !bestMatch) {
|
||||
|
||||
if (score > bestScore) {
|
||||
bestScore = score
|
||||
bestMatch = toplevel
|
||||
if (score === 3)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!bestMatch) continue
|
||||
if (!bestMatch)
|
||||
continue
|
||||
|
||||
usedToplevels.add(bestMatch)
|
||||
|
||||
const workspace = workspaces[niriWindow.workspace_id]
|
||||
const isFocused = niriWindow.is_focused ?? (workspace && workspace.active_window_id === niriWindow.id) ?? false
|
||||
|
||||
const enrichedToplevel = {
|
||||
appId: bestMatch.appId,
|
||||
title: bestMatch.title,
|
||||
activated: bestMatch.activated,
|
||||
niriWindowId: niriWindow.id,
|
||||
niriWorkspaceId: niriWindow.workspace_id,
|
||||
activate: function() {
|
||||
"appId": bestMatch.appId,
|
||||
"title": bestMatch.title,
|
||||
"activated": isFocused,
|
||||
"niriWindowId": niriWindow.id,
|
||||
"niriWorkspaceId": niriWindow.workspace_id,
|
||||
"activate": function () {
|
||||
return NiriService.focusWindow(niriWindow.id)
|
||||
},
|
||||
close: function() {
|
||||
"close": function () {
|
||||
if (bestMatch.close) {
|
||||
return bestMatch.close()
|
||||
}
|
||||
@@ -614,7 +764,8 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
if (currentWorkspaceId === null) return toplevels
|
||||
if (currentWorkspaceId === null)
|
||||
return toplevels
|
||||
|
||||
const workspaceWindows = windows.filter(niriWindow => niriWindow.workspace_id === currentWorkspaceId)
|
||||
const usedToplevels = new Set()
|
||||
@@ -622,35 +773,50 @@ Singleton {
|
||||
|
||||
for (const niriWindow of workspaceWindows) {
|
||||
let bestMatch = null
|
||||
let bestScore = -1
|
||||
|
||||
for (const toplevel of toplevels) {
|
||||
if (usedToplevels.has(toplevel)) continue
|
||||
if (usedToplevels.has(toplevel))
|
||||
continue
|
||||
|
||||
if (toplevel.appId === niriWindow.app_id) {
|
||||
if (niriWindow.title && toplevel.title === niriWindow.title) {
|
||||
bestMatch = toplevel
|
||||
break
|
||||
let score = 1
|
||||
|
||||
if (niriWindow.title && toplevel.title) {
|
||||
if (toplevel.title === niriWindow.title) {
|
||||
score = 3
|
||||
} else if (toplevel.title.includes(niriWindow.title) || niriWindow.title.includes(toplevel.title)) {
|
||||
score = 2
|
||||
}
|
||||
}
|
||||
if (!niriWindow.title && !bestMatch) {
|
||||
|
||||
if (score > bestScore) {
|
||||
bestScore = score
|
||||
bestMatch = toplevel
|
||||
if (score === 3)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!bestMatch) continue
|
||||
if (!bestMatch)
|
||||
continue
|
||||
|
||||
usedToplevels.add(bestMatch)
|
||||
|
||||
const workspace = workspaces[niriWindow.workspace_id]
|
||||
const isFocused = niriWindow.is_focused ?? (workspace && workspace.active_window_id === niriWindow.id) ?? false
|
||||
|
||||
const enrichedToplevel = {
|
||||
appId: bestMatch.appId,
|
||||
title: bestMatch.title,
|
||||
activated: bestMatch.activated,
|
||||
niriWindowId: niriWindow.id,
|
||||
niriWorkspaceId: niriWindow.workspace_id,
|
||||
activate: function() {
|
||||
"appId": bestMatch.appId,
|
||||
"title": bestMatch.title,
|
||||
"activated": isFocused,
|
||||
"niriWindowId": niriWindow.id,
|
||||
"niriWorkspaceId": niriWindow.workspace_id,
|
||||
"activate": function () {
|
||||
return NiriService.focusWindow(niriWindow.id)
|
||||
},
|
||||
close: function() {
|
||||
"close": function () {
|
||||
if (bestMatch.close) {
|
||||
return bestMatch.close()
|
||||
}
|
||||
@@ -671,10 +837,10 @@ Singleton {
|
||||
}
|
||||
|
||||
function generateNiriLayoutConfig() {
|
||||
const niriSocket = Quickshell.env("NIRI_SOCKET")
|
||||
if (!niriSocket || niriSocket.length === 0) return
|
||||
if (configGenerationPending) return
|
||||
if (!CompositorService.isNiri || configGenerationPending)
|
||||
return
|
||||
|
||||
suppressNextToast()
|
||||
configGenerationPending = true
|
||||
configGenerationDebounce.restart()
|
||||
}
|
||||
@@ -696,7 +862,6 @@ Singleton {
|
||||
width 2
|
||||
}
|
||||
}
|
||||
|
||||
window-rule {
|
||||
geometry-corner-radius ${cornerRadius}
|
||||
clip-to-geometry true
|
||||
@@ -727,4 +892,4 @@ window-rule {
|
||||
writeBindsProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && cp --no-preserve=mode "${sourceBindsPath}" "${bindsPath}"`]
|
||||
writeBindsProcess.running = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pragma Singleton
|
||||
|
||||
pragma ComponentBehavior
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
@@ -174,7 +174,7 @@ Singleton {
|
||||
if (socketPath && socketPath.length > 0) {
|
||||
checkDMSCapabilities()
|
||||
} else {
|
||||
console.log("PortalService: DMS_SOCKET not set")
|
||||
console.info("PortalService: DMS_SOCKET not set")
|
||||
}
|
||||
colorSchemeDetector.running = true
|
||||
}
|
||||
@@ -212,7 +212,7 @@ Singleton {
|
||||
checkAccountsService()
|
||||
checkSettingsPortal()
|
||||
} else {
|
||||
console.log("PortalService: freedesktop capability not available in DMS")
|
||||
console.info("PortalService: freedesktop capability not available in DMS")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ Singleton {
|
||||
|
||||
if ((node.type & PwNodeType.AudioInStream) === PwNodeType.AudioInStream) {
|
||||
if (!looksLikeSystemVirtualMic(node)) {
|
||||
console.log(node.audio)
|
||||
if (node.audio && node.audio.muted) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ Singleton {
|
||||
detectElogindProcess.running = true
|
||||
detectHibernateProcess.running = true
|
||||
detectPrimeRunProcess.running = true
|
||||
console.log("SessionService: Native inhibitor available:", nativeInhibitorAvailable)
|
||||
console.info("SessionService: Native inhibitor available:", nativeInhibitorAvailable)
|
||||
if (!SettingsData.loginctlLockIntegration) {
|
||||
console.log("SessionService: loginctl lock integration disabled by user")
|
||||
return
|
||||
|
||||
@@ -263,4 +263,22 @@ Singleton {
|
||||
running: refCount > 0 && distributionSupported && (pkgManager || updChecker)
|
||||
onTriggered: checkForUpdates()
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "systemupdater"
|
||||
|
||||
function updatestatus(): string {
|
||||
if (root.isChecking) {
|
||||
return "ERROR: already checking"
|
||||
}
|
||||
if (!distributionSupported) {
|
||||
return "ERROR: distribution not supported"
|
||||
}
|
||||
if (!pkgManager && !updChecker) {
|
||||
return "ERROR: update checker not available"
|
||||
}
|
||||
root.checkForUpdates()
|
||||
return "SUCCESS: Now checking..."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,246 +0,0 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
// Minimal VPN controller backed by NetworkManager (nmcli + D-Bus monitor)
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property int refCount: 0
|
||||
|
||||
onRefCountChanged: {
|
||||
console.log("VpnService: refCount changed to", refCount)
|
||||
if (refCount > 0 && !nmMonitor.running) {
|
||||
console.log("VpnService: Starting nmMonitor")
|
||||
nmMonitor.running = true
|
||||
refreshAll()
|
||||
} else if (refCount === 0 && nmMonitor.running) {
|
||||
console.log("VpnService: Stopping nmMonitor")
|
||||
nmMonitor.running = false
|
||||
}
|
||||
}
|
||||
|
||||
// State
|
||||
property bool available: true
|
||||
property bool isBusy: false
|
||||
property string errorMessage: ""
|
||||
|
||||
// Profiles discovered on the system
|
||||
// [{ name, uuid, type }]
|
||||
property var profiles: []
|
||||
|
||||
// Allow multiple active VPNs (set true to allow concurrent connections)
|
||||
// Default: allow multiple, to align with NetworkManager capability
|
||||
property bool singleActive: false
|
||||
|
||||
// Active VPN connections (may be multiple)
|
||||
// Full list and convenience projections
|
||||
property var activeConnections: [] // [{ name, uuid, device, state }]
|
||||
property var activeUuids: []
|
||||
property var activeNames: []
|
||||
// Back-compat single values (first active if present)
|
||||
property string activeUuid: activeUuids.length > 0 ? activeUuids[0] : ""
|
||||
property string activeName: activeNames.length > 0 ? activeNames[0] : ""
|
||||
property string activeDevice: activeConnections.length > 0 ? (activeConnections[0].device || "") : ""
|
||||
property string activeState: activeConnections.length > 0 ? (activeConnections[0].state || "") : ""
|
||||
property bool connected: activeUuids.length > 0
|
||||
|
||||
// Use implicit property notify signals (profilesChanged, activeUuidChanged, etc.)
|
||||
|
||||
function refreshAll() {
|
||||
listProfiles()
|
||||
refreshActive()
|
||||
}
|
||||
|
||||
// Monitor NetworkManager changes and refresh on activity
|
||||
Process {
|
||||
id: nmMonitor
|
||||
command: ["gdbus", "monitor", "--system", "--dest", "org.freedesktop.NetworkManager"]
|
||||
running: false
|
||||
|
||||
stdout: SplitParser {
|
||||
splitMarker: "\n"
|
||||
onRead: line => {
|
||||
if (line.includes("ActiveConnection") || line.includes("PropertiesChanged") || line.includes("StateChanged")) {
|
||||
refreshAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Query all VPN profiles
|
||||
function listProfiles() {
|
||||
getProfiles.running = true
|
||||
}
|
||||
|
||||
Process {
|
||||
id: getProfiles
|
||||
command: ["bash", "-lc", "nmcli -t -f NAME,UUID,TYPE connection show | while IFS=: read -r name uuid type; do case \"$type\" in vpn) svc=$(nmcli -g vpn.service-type connection show uuid \"$uuid\" 2>/dev/null); echo \"$name:$uuid:$type:$svc\" ;; wireguard) echo \"$name:$uuid:$type:\" ;; *) : ;; esac; done"]
|
||||
running: false
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const lines = text.trim().length ? text.trim().split('\n') : []
|
||||
const out = []
|
||||
for (const line of lines) {
|
||||
const parts = line.split(':')
|
||||
if (parts.length >= 3 && (parts[2] === "vpn" || parts[2] === "wireguard")) {
|
||||
const svc = parts.length >= 4 ? parts[3] : ""
|
||||
out.push({ name: parts[0], uuid: parts[1], type: parts[2], serviceType: svc })
|
||||
}
|
||||
}
|
||||
root.profiles = out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Query active VPN connection
|
||||
function refreshActive() {
|
||||
getActive.running = true
|
||||
}
|
||||
|
||||
Process {
|
||||
id: getActive
|
||||
command: ["nmcli", "-t", "-f", "NAME,UUID,TYPE,DEVICE,STATE", "connection", "show", "--active"]
|
||||
running: false
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const lines = text.trim().length ? text.trim().split('\n') : []
|
||||
let act = []
|
||||
for (const line of lines) {
|
||||
const parts = line.split(':')
|
||||
if (parts.length >= 5 && (parts[2] === "vpn" || parts[2] === "wireguard")) {
|
||||
act.push({ name: parts[0], uuid: parts[1], device: parts[3], state: parts[4] })
|
||||
}
|
||||
}
|
||||
root.activeConnections = act
|
||||
root.activeUuids = act.map(a => a.uuid).filter(u => !!u)
|
||||
root.activeNames = act.map(a => a.name).filter(n => !!n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isActiveUuid(uuid) {
|
||||
return root.activeUuids && root.activeUuids.indexOf(uuid) !== -1
|
||||
}
|
||||
|
||||
function _looksLikeUuid(s) {
|
||||
// Very loose check for UUID pattern
|
||||
return s && s.indexOf('-') !== -1 && s.length >= 8
|
||||
}
|
||||
|
||||
function connect(uuidOrName) {
|
||||
if (root.isBusy) return
|
||||
root.isBusy = true
|
||||
root.errorMessage = ""
|
||||
if (root.singleActive) {
|
||||
// Bring down all active VPNs, then bring up the requested one
|
||||
const isUuid = _looksLikeUuid(uuidOrName)
|
||||
const escaped = ('' + uuidOrName).replace(/'/g, "'\\''")
|
||||
const upCmd = isUuid ? `nmcli connection up uuid '${escaped}'` : `nmcli connection up id '${escaped}'`
|
||||
const script = `set -e\n` +
|
||||
`nmcli -t -f UUID,TYPE connection show --active | awk -F: '$2 ~ /^(vpn|wireguard)$/ {print $1}' | while read u; do [ -n \"$u\" ] && nmcli connection down uuid \"$u\" || true; done\n` +
|
||||
upCmd + `\n`
|
||||
vpnSwitch.command = ["bash", "-lc", script]
|
||||
vpnSwitch.running = true
|
||||
} else {
|
||||
if (_looksLikeUuid(uuidOrName)) {
|
||||
vpnUp.command = ["nmcli", "connection", "up", "uuid", uuidOrName]
|
||||
} else {
|
||||
vpnUp.command = ["nmcli", "connection", "up", "id", uuidOrName]
|
||||
}
|
||||
vpnUp.running = true
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect(uuidOrName) {
|
||||
if (root.isBusy) return
|
||||
root.isBusy = true
|
||||
root.errorMessage = ""
|
||||
if (_looksLikeUuid(uuidOrName)) {
|
||||
vpnDown.command = ["nmcli", "connection", "down", "uuid", uuidOrName]
|
||||
} else {
|
||||
vpnDown.command = ["nmcli", "connection", "down", "id", uuidOrName]
|
||||
}
|
||||
vpnDown.running = true
|
||||
}
|
||||
|
||||
function toggle(uuid) {
|
||||
if (uuid) {
|
||||
if (isActiveUuid(uuid)) disconnect(uuid)
|
||||
else connect(uuid)
|
||||
return
|
||||
}
|
||||
if (root.profiles.length > 0) {
|
||||
connect(root.profiles[0].uuid)
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: vpnUp
|
||||
running: false
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
root.isBusy = false
|
||||
if (!text.toLowerCase().includes("successfully")) {
|
||||
root.errorMessage = text.trim()
|
||||
}
|
||||
refreshAll()
|
||||
}
|
||||
}
|
||||
onExited: exitCode => {
|
||||
root.isBusy = false
|
||||
if (exitCode !== 0 && root.errorMessage === "") {
|
||||
root.errorMessage = "Failed to connect VPN"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: vpnDown
|
||||
running: false
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
root.isBusy = false
|
||||
if (!text.toLowerCase().includes("deactivated") && !text.toLowerCase().includes("successfully")) {
|
||||
root.errorMessage = text.trim()
|
||||
}
|
||||
refreshAll()
|
||||
}
|
||||
}
|
||||
onExited: exitCode => {
|
||||
root.isBusy = false
|
||||
if (exitCode !== 0 && root.errorMessage === "") {
|
||||
root.errorMessage = "Failed to disconnect VPN"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function disconnectAllActive() {
|
||||
if (root.isBusy) return
|
||||
root.isBusy = true
|
||||
const script = `nmcli -t -f UUID,TYPE connection show --active | awk -F: '$2 ~ /^(vpn|wireguard)$/ {print $1}' | while read u; do [ -n \"$u\" ] && nmcli connection down uuid \"$u\" || true; done`
|
||||
vpnSwitch.command = ["bash", "-lc", script]
|
||||
vpnSwitch.running = true
|
||||
}
|
||||
|
||||
// Sequenced down/up using a single shell for exclusive switch
|
||||
Process {
|
||||
id: vpnSwitch
|
||||
running: false
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
root.isBusy = false
|
||||
refreshAll()
|
||||
}
|
||||
}
|
||||
onExited: exitCode => {
|
||||
root.isBusy = false
|
||||
if (exitCode !== 0 && root.errorMessage === "") {
|
||||
root.errorMessage = "Failed to switch VPN"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,9 +46,9 @@ authentication, and dynamic theming.
|
||||
# QML-based application
|
||||
|
||||
%install
|
||||
# Install greeter files to XDG config location
|
||||
install -dm755 %{buildroot}%{_sysconfdir}/xdg/quickshell/dms-greeter
|
||||
cp -r * %{buildroot}%{_sysconfdir}/xdg/quickshell/dms-greeter/
|
||||
# Install greeter files to shared data location
|
||||
install -dm755 %{buildroot}%{_datadir}/quickshell/dms-greeter
|
||||
cp -r * %{buildroot}%{_datadir}/quickshell/dms-greeter/
|
||||
|
||||
# Install launcher script
|
||||
install -Dm755 Modules/Greetd/assets/dms-greeter %{buildroot}%{_bindir}/dms-greeter
|
||||
@@ -123,7 +123,7 @@ echo "Creating symlinks to sync theme..."
|
||||
declare -A links=(
|
||||
["$HOME/.config/DankMaterialShell/settings.json"]="$CACHE_DIR/settings.json"
|
||||
["$HOME/.local/state/DankMaterialShell/session.json"]="$CACHE_DIR/session.json"
|
||||
["$HOME/.cache/quickshell/dankshell/dms-colors.json"]="$CACHE_DIR/colors.json"
|
||||
["$HOME/.cache/DankMaterialShell/dms-colors.json"]="$CACHE_DIR/colors.json"
|
||||
)
|
||||
|
||||
for source in "${!links[@]}"; do
|
||||
@@ -160,19 +160,28 @@ install -dm755 %{buildroot}%{_sharedstatedir}/greeter
|
||||
# Instead, we verify/fix it in %post if needed
|
||||
|
||||
# Remove build and development files
|
||||
rm -rf %{buildroot}%{_sysconfdir}/xdg/quickshell/dms-greeter/.git*
|
||||
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms-greeter/.gitignore
|
||||
rm -rf %{buildroot}%{_sysconfdir}/xdg/quickshell/dms-greeter/.github
|
||||
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms-greeter/*.spec
|
||||
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms-greeter/dms.spec
|
||||
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms-greeter/dms-greeter.spec
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms-greeter/.git*
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms-greeter/.gitignore
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms-greeter/.github
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms-greeter/*.spec
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms-greeter/dms.spec
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms-greeter/dms-greeter.spec
|
||||
|
||||
%posttrans
|
||||
# Clean up old installation path from previous versions (only if empty)
|
||||
if [ -d "%{_sysconfdir}/xdg/quickshell/dms-greeter" ]; then
|
||||
# Remove directories only if empty (preserves any user-added files)
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell/dms-greeter" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
%files
|
||||
%license LICENSE
|
||||
%doc %{_docdir}/dms-greeter/README.md
|
||||
%{_bindir}/dms-greeter
|
||||
%{_bindir}/dms-greeter-sync
|
||||
%{_sysconfdir}/xdg/quickshell/dms-greeter/
|
||||
%{_datadir}/quickshell/dms-greeter/
|
||||
%dir %attr(0750,greeter,greeter) %{_localstatedir}/cache/dms-greeter
|
||||
%dir %attr(0755,greeter,greeter) %{_sharedstatedir}/greeter
|
||||
|
||||
@@ -200,9 +209,9 @@ if [ -x /usr/sbin/semanage ] && [ -x /usr/sbin/restorecon ]; then
|
||||
semanage fcontext -a -t cache_home_t '%{_localstatedir}/cache/dms-greeter(/.*)?' >/dev/null 2>&1 || true
|
||||
restorecon -R %{_localstatedir}/cache/dms-greeter >/dev/null 2>&1 || true
|
||||
|
||||
# Config directory
|
||||
semanage fcontext -a -t etc_t '%{_sysconfdir}/xdg/quickshell/dms-greeter(/.*)?' >/dev/null 2>&1 || true
|
||||
restorecon -R %{_sysconfdir}/xdg/quickshell/dms-greeter >/dev/null 2>&1 || true
|
||||
# Shared data directory
|
||||
semanage fcontext -a -t usr_t '%{_datadir}/quickshell/dms-greeter(/.*)?' >/dev/null 2>&1 || true
|
||||
restorecon -R %{_datadir}/quickshell/dms-greeter >/dev/null 2>&1 || true
|
||||
|
||||
# PAM configuration
|
||||
restorecon %{_sysconfdir}/pam.d/greetd >/dev/null 2>&1 || true
|
||||
@@ -300,17 +309,19 @@ fi
|
||||
|
||||
# Only show banner on initial install
|
||||
if [ "$1" -eq 1 ]; then
|
||||
cat << EOF
|
||||
cat << 'EOF'
|
||||
|
||||
===============================================================================
|
||||
DMS Greeter Installation Complete!
|
||||
===============================================================================
|
||||
=========================================================================
|
||||
DMS Greeter Installation Complete!
|
||||
=========================================================================
|
||||
|
||||
Status:
|
||||
- Greeter user: Created ✓
|
||||
- Greeter directories: /var/cache/dms-greeter, /var/lib/greeter ✓
|
||||
- SELinux contexts: Applied ✓
|
||||
- Greetd config: $CONFIG_STATUS
|
||||
✓ Greeter user: Created
|
||||
✓ Greeter directories: /var/cache/dms-greeter, /var/lib/greeter
|
||||
✓ SELinux contexts: Applied
|
||||
EOF
|
||||
echo " ✓ Greetd config: $CONFIG_STATUS"
|
||||
cat << 'EOF'
|
||||
|
||||
Next steps:
|
||||
|
||||
@@ -327,7 +338,7 @@ Next steps:
|
||||
|
||||
Ready to test? Reboot or run: sudo systemctl start greetd
|
||||
Documentation: /usr/share/doc/dms-greeter/README.md
|
||||
===============================================================================
|
||||
=========================================================================
|
||||
|
||||
EOF
|
||||
fi
|
||||
@@ -338,7 +349,7 @@ if [ "$1" -eq 0 ] && [ -x /usr/sbin/semanage ]; then
|
||||
semanage fcontext -d '%{_bindir}/dms-greeter' 2>/dev/null || true
|
||||
semanage fcontext -d '%{_sharedstatedir}/greeter(/.*)?' 2>/dev/null || true
|
||||
semanage fcontext -d '%{_localstatedir}/cache/dms-greeter(/.*)?' 2>/dev/null || true
|
||||
semanage fcontext -d '%{_sysconfdir}/xdg/quickshell/dms-greeter(/.*)?' 2>/dev/null || true
|
||||
semanage fcontext -d '%{_datadir}/quickshell/dms-greeter(/.*)?' 2>/dev/null || true
|
||||
fi
|
||||
|
||||
%changelog
|
||||
|
||||
58
dms.spec
58
dms.spec
@@ -27,6 +27,7 @@ BuildRequires: wget
|
||||
|
||||
# Core requirements
|
||||
Requires: (quickshell-git or quickshell)
|
||||
Requires: accountsservice
|
||||
Requires: dms-cli
|
||||
Requires: dgop
|
||||
Requires: fira-code-fonts
|
||||
@@ -128,20 +129,61 @@ install -Dm755 %{_builddir}/danklinux-master/bin/${DMS_BINARY} %{buildroot}%{_bi
|
||||
# Install dgop binary
|
||||
install -Dm755 %{_builddir}/dgop %{buildroot}%{_bindir}/dgop
|
||||
|
||||
# Install shell files to XDG config location
|
||||
install -dm755 %{buildroot}%{_sysconfdir}/xdg/quickshell/dms
|
||||
cp -r * %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/
|
||||
# Install shell files to shared data location
|
||||
install -dm755 %{buildroot}%{_datadir}/quickshell/dms
|
||||
cp -r * %{buildroot}%{_datadir}/quickshell/dms/
|
||||
|
||||
# Remove build files
|
||||
rm -rf %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/.git*
|
||||
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/.gitignore
|
||||
rm -rf %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/.github
|
||||
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/*.spec
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.git*
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms/*.spec
|
||||
|
||||
%posttrans
|
||||
# Clean up old installation path from previous versions (only if empty)
|
||||
if [ -d "%{_sysconfdir}/xdg/quickshell/dms" ]; then
|
||||
# Remove directories only if empty (preserves any user-added files)
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell/dms" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Restart DMS for active users after upgrade
|
||||
if [ "$1" -ge 2 ]; then
|
||||
# Find all quickshell DMS processes (PID and username)
|
||||
while read pid cmd; do
|
||||
username=$(ps -o user= -p "$pid" 2>/dev/null)
|
||||
|
||||
[ "$username" = "root" ] && continue
|
||||
[ -z "$username" ] && continue
|
||||
|
||||
# Get user's UID and validate session
|
||||
user_uid=$(id -u "$username" 2>/dev/null)
|
||||
[ -z "$user_uid" ] && continue
|
||||
[ ! -d "/run/user/$user_uid" ] && continue
|
||||
|
||||
wayland_display=$(tr '\0' '\n' < /proc/$pid/environ 2>/dev/null | grep '^WAYLAND_DISPLAY=' | cut -d= -f2)
|
||||
[ -z "$wayland_display" ] && continue
|
||||
|
||||
echo "Restarting DMS for user: $username"
|
||||
|
||||
# Run as user with full Wayland session environment
|
||||
runuser -u "$username" -- /bin/sh -c "
|
||||
export XDG_RUNTIME_DIR=/run/user/$user_uid
|
||||
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$user_uid/bus
|
||||
export WAYLAND_DISPLAY=$wayland_display
|
||||
export PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:\$PATH
|
||||
dms restart >/dev/null 2>&1
|
||||
" 2>/dev/null || true
|
||||
|
||||
break
|
||||
done < <(pgrep -a -f 'quickshell.*dms' 2>/dev/null)
|
||||
fi
|
||||
|
||||
%files
|
||||
%license LICENSE
|
||||
%doc README.md CONTRIBUTING.md
|
||||
%{_sysconfdir}/xdg/quickshell/dms/
|
||||
%{_datadir}/quickshell/dms/
|
||||
|
||||
%files -n dms-cli
|
||||
%{_bindir}/dms
|
||||
|
||||
15
docs/IPC.md
15
docs/IPC.md
@@ -407,6 +407,21 @@ dms ipc call bar hide
|
||||
dms ipc call bar status
|
||||
```
|
||||
|
||||
## Target: `systemupdater`
|
||||
|
||||
System updater external check request.
|
||||
|
||||
### Functions
|
||||
|
||||
**`updatestatus`**
|
||||
- Trigger a system update check
|
||||
- Returns: Success confirmation
|
||||
|
||||
### Examples
|
||||
```bash
|
||||
dms ipc call systemupdater updatestatus
|
||||
```
|
||||
|
||||
## Modal Controls
|
||||
|
||||
These targets control various modal windows and overlays.
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
[config]
|
||||
|
||||
[templates.dmsgtk3]
|
||||
input_path = './templates/gtk-colors.css'
|
||||
output_path = '~/.config/gtk-3.0/dank-colors.css'
|
||||
|
||||
[templates.dmsgtk4]
|
||||
input_path = './templates/gtk-colors.css'
|
||||
output_path = '~/.config/gtk-4.0/dank-colors.css'
|
||||
|
||||
[templates.dmsqt5ct]
|
||||
input_path = './templates/qtct-colors.conf'
|
||||
output_path = '~/.config/qt5ct/colors/matugen.conf'
|
||||
|
||||
[templates.dmsqt6ct]
|
||||
input_path = './templates/qtct-colors.conf'
|
||||
output_path = '~/.config/qt6ct/colors/matugen.conf'
|
||||
|
||||
[templates.dmskcolorscheme]
|
||||
input_path = './templates/matugen-kcolorscheme.colors'
|
||||
output_path = '~/.local/share/color-schemes/DankMatugen.colors'
|
||||
|
||||
[templates.dmsdgop]
|
||||
input_path = './templates/dgop.json'
|
||||
output_path = '~/.config/dgop/colors.json'
|
||||
|
||||
[templates.dmsniri]
|
||||
input_path = './templates/niri-colors.kdl'
|
||||
output_path = '~/.config/niri/dankshell-colors.kdl'
|
||||
|
||||
[templates.dmsghostty]
|
||||
input_path = './templates/ghostty-colors.conf'
|
||||
output_path = '~/.config/ghostty/config-dankcolors'
|
||||
@@ -5,5 +5,13 @@ input_path = './matugen/templates/gtk-colors.css'
|
||||
output_path = '~/.config/gtk-4.0/dank-colors.css'
|
||||
|
||||
[templates.dmskcolorscheme]
|
||||
input_path = './matugen/templates/matugen-kcolorscheme.colors'
|
||||
output_path = '~/.local/share/color-schemes/DankMatugen.colors'
|
||||
input_path = './matugen/templates/kcolorscheme.colors'
|
||||
output_path = '~/.local/share/color-schemes/DankMatugen.colors'
|
||||
|
||||
[templates.dmslightkcolorscheme]
|
||||
input_path = './matugen/templates/light-kcolorscheme.colors'
|
||||
output_path = '~/.local/share/color-schemes/DankMatugenLight.colors'
|
||||
|
||||
[templates.dmsdarkkcolorscheme]
|
||||
input_path = './matugen/templates/dark-kcolorscheme.colors'
|
||||
output_path = '~/.local/share/color-schemes/DankMatugenDark.colors'
|
||||
146
matugen/templates/dark-kcolorscheme.colors
Normal file
146
matugen/templates/dark-kcolorscheme.colors
Normal file
@@ -0,0 +1,146 @@
|
||||
[KDE]
|
||||
contrast=4
|
||||
|
||||
[General]
|
||||
ColorScheme=DankMatugenDark
|
||||
Name=Dank Shell (matugen-dark)
|
||||
|
||||
[ColorEffects:Disabled]
|
||||
Color={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ColorAmount=0
|
||||
ColorEffect=0
|
||||
ContrastAmount=0.65
|
||||
ContrastEffect=1
|
||||
IntensityAmount=0.1
|
||||
IntensityEffect=2
|
||||
|
||||
[ColorEffects:Inactive]
|
||||
ChangeSelectionColor=true
|
||||
Color={{colors.outline.dark.red}},{{colors.outline.dark.green}},{{colors.outline.dark.blue}}
|
||||
ColorAmount=0.025
|
||||
ColorEffect=2
|
||||
ContrastAmount=0.1
|
||||
ContrastEffect=2
|
||||
Enable=false
|
||||
IntensityAmount=0
|
||||
IntensityEffect=0
|
||||
|
||||
[Colors:Button]
|
||||
BackgroundAlternate={{colors.surface_container_high.dark.red}},{{colors.surface_container_high.dark.green}},{{colors.surface_container_high.dark.blue}}
|
||||
BackgroundNormal={{colors.surface_container.dark.red}},{{colors.surface_container.dark.green}},{{colors.surface_container.dark.blue}}
|
||||
DecorationFocus={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationHover={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundActive={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ForegroundLink={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundNegative={{colors.error.dark.red}},{{colors.error.dark.green}},{{colors.error.dark.blue}}
|
||||
ForegroundNeutral={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
ForegroundNormal={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
ForegroundPositive={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundVisited={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
|
||||
[Colors:Complementary]
|
||||
BackgroundAlternate={{colors.surface_container_high.dark.red}},{{colors.surface_container_high.dark.green}},{{colors.surface_container_high.dark.blue}}
|
||||
BackgroundNormal={{colors.surface.dark.red}},{{colors.surface.dark.green}},{{colors.surface.dark.blue}}
|
||||
DecorationFocus={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationHover={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundActive={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ForegroundLink={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundNegative={{colors.error.dark.red}},{{colors.error.dark.green}},{{colors.error.dark.blue}}
|
||||
ForegroundNeutral={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
ForegroundNormal={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
ForegroundPositive={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundVisited={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
|
||||
[Colors:Header]
|
||||
BackgroundAlternate={{colors.surface.dark.red}},{{colors.surface.dark.green}},{{colors.surface.dark.blue}}
|
||||
BackgroundNormal={{colors.surface_container.dark.red}},{{colors.surface_container.dark.green}},{{colors.surface_container.dark.blue}}
|
||||
DecorationFocus={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationHover={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundActive={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ForegroundLink={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundNegative={{colors.error.dark.red}},{{colors.error.dark.green}},{{colors.error.dark.blue}}
|
||||
ForegroundNeutral={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
ForegroundNormal={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
ForegroundPositive={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundVisited={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
|
||||
[Colors:Header][Inactive]
|
||||
BackgroundAlternate={{colors.surface_container.dark.red}},{{colors.surface_container.dark.green}},{{colors.surface_container.dark.blue}}
|
||||
BackgroundNormal={{colors.surface.dark.red}},{{colors.surface.dark.green}},{{colors.surface.dark.blue}}
|
||||
DecorationFocus={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationHover={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundActive={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ForegroundLink={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundNegative={{colors.error.dark.red}},{{colors.error.dark.green}},{{colors.error.dark.blue}}
|
||||
ForegroundNeutral={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
ForegroundNormal={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
ForegroundPositive={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundVisited={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
|
||||
[Colors:Selection]
|
||||
BackgroundAlternate={{colors.primary_container.dark.red}},{{colors.primary_container.dark.green}},{{colors.primary_container.dark.blue}}
|
||||
BackgroundNormal={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationFocus={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationHover={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundActive={{colors.on_primary.dark.red}},{{colors.on_primary.dark.green}},{{colors.on_primary.dark.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ForegroundLink={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundNegative={{colors.error.dark.red}},{{colors.error.dark.green}},{{colors.error.dark.blue}}
|
||||
ForegroundNeutral={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
ForegroundNormal={{colors.on_primary.dark.red}},{{colors.on_primary.dark.green}},{{colors.on_primary.dark.blue}}
|
||||
ForegroundPositive={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundVisited={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
|
||||
[Colors:Tooltip]
|
||||
BackgroundAlternate={{colors.surface.dark.red}},{{colors.surface.dark.green}},{{colors.surface.dark.blue}}
|
||||
BackgroundNormal={{colors.surface_container.dark.red}},{{colors.surface_container.dark.green}},{{colors.surface_container.dark.blue}}
|
||||
DecorationFocus={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationHover={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundActive={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ForegroundLink={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundNegative={{colors.error.dark.red}},{{colors.error.dark.green}},{{colors.error.dark.blue}}
|
||||
ForegroundNeutral={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
ForegroundNormal={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
ForegroundPositive={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundVisited={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
|
||||
[Colors:View]
|
||||
BackgroundAlternate={{colors.surface_container_low.dark.red}},{{colors.surface_container_low.dark.green}},{{colors.surface_container_low.dark.blue}}
|
||||
BackgroundNormal={{colors.background.dark.red}},{{colors.background.dark.green}},{{colors.background.dark.blue}}
|
||||
DecorationFocus={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationHover={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundActive={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ForegroundLink={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundNegative={{colors.error.dark.red}},{{colors.error.dark.green}},{{colors.error.dark.blue}}
|
||||
ForegroundNeutral={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
ForegroundNormal={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
ForegroundPositive={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundVisited={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
|
||||
[Colors:Window]
|
||||
BackgroundAlternate={{colors.surface_container.dark.red}},{{colors.surface_container.dark.green}},{{colors.surface_container.dark.blue}}
|
||||
BackgroundNormal={{colors.surface.dark.red}},{{colors.surface.dark.green}},{{colors.surface.dark.blue}}
|
||||
DecorationFocus={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationHover={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundActive={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ForegroundLink={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundNegative={{colors.error.dark.red}},{{colors.error.dark.green}},{{colors.error.dark.blue}}
|
||||
ForegroundNeutral={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
ForegroundNormal={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
ForegroundPositive={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundVisited={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
|
||||
[WM]
|
||||
activeBackground={{colors.surface_container.dark.red}},{{colors.surface_container.dark.green}},{{colors.surface_container.dark.blue}}
|
||||
activeBlend={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
activeForeground={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
inactiveBackground={{colors.surface.dark.red}},{{colors.surface.dark.green}},{{colors.surface.dark.blue}}
|
||||
inactiveBlend={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
inactiveForeground={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
146
matugen/templates/light-kcolorscheme.colors
Normal file
146
matugen/templates/light-kcolorscheme.colors
Normal file
@@ -0,0 +1,146 @@
|
||||
[KDE]
|
||||
contrast=4
|
||||
|
||||
[General]
|
||||
ColorScheme=DankMatugenLight
|
||||
Name=Dank Shell (matugen-light)
|
||||
|
||||
[ColorEffects:Disabled]
|
||||
Color={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ColorAmount=0
|
||||
ColorEffect=0
|
||||
ContrastAmount=0.65
|
||||
ContrastEffect=1
|
||||
IntensityAmount=0.1
|
||||
IntensityEffect=2
|
||||
|
||||
[ColorEffects:Inactive]
|
||||
ChangeSelectionColor=true
|
||||
Color={{colors.outline.light.red}},{{colors.outline.light.green}},{{colors.outline.light.blue}}
|
||||
ColorAmount=0.025
|
||||
ColorEffect=2
|
||||
ContrastAmount=0.1
|
||||
ContrastEffect=2
|
||||
Enable=false
|
||||
IntensityAmount=0
|
||||
IntensityEffect=0
|
||||
|
||||
[Colors:Button]
|
||||
BackgroundAlternate={{colors.surface_container_high.light.red}},{{colors.surface_container_high.light.green}},{{colors.surface_container_high.light.blue}}
|
||||
BackgroundNormal={{colors.surface_container.light.red}},{{colors.surface_container.light.green}},{{colors.surface_container.light.blue}}
|
||||
DecorationFocus={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationHover={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundActive={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ForegroundLink={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundNegative={{colors.error.light.red}},{{colors.error.light.green}},{{colors.error.light.blue}}
|
||||
ForegroundNeutral={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
ForegroundNormal={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
ForegroundPositive={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundVisited={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
|
||||
[Colors:Complementary]
|
||||
BackgroundAlternate={{colors.surface_container_high.light.red}},{{colors.surface_container_high.light.green}},{{colors.surface_container_high.light.blue}}
|
||||
BackgroundNormal={{colors.surface.light.red}},{{colors.surface.light.green}},{{colors.surface.light.blue}}
|
||||
DecorationFocus={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationHover={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundActive={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ForegroundLink={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundNegative={{colors.error.light.red}},{{colors.error.light.green}},{{colors.error.light.blue}}
|
||||
ForegroundNeutral={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
ForegroundNormal={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
ForegroundPositive={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundVisited={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
|
||||
[Colors:Header]
|
||||
BackgroundAlternate={{colors.surface.light.red}},{{colors.surface.light.green}},{{colors.surface.light.blue}}
|
||||
BackgroundNormal={{colors.surface_container.light.red}},{{colors.surface_container.light.green}},{{colors.surface_container.light.blue}}
|
||||
DecorationFocus={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationHover={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundActive={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ForegroundLink={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundNegative={{colors.error.light.red}},{{colors.error.light.green}},{{colors.error.light.blue}}
|
||||
ForegroundNeutral={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
ForegroundNormal={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
ForegroundPositive={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundVisited={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
|
||||
[Colors:Header][Inactive]
|
||||
BackgroundAlternate={{colors.surface_container.light.red}},{{colors.surface_container.light.green}},{{colors.surface_container.light.blue}}
|
||||
BackgroundNormal={{colors.surface.light.red}},{{colors.surface.light.green}},{{colors.surface.light.blue}}
|
||||
DecorationFocus={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationHover={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundActive={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ForegroundLink={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundNegative={{colors.error.light.red}},{{colors.error.light.green}},{{colors.error.light.blue}}
|
||||
ForegroundNeutral={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
ForegroundNormal={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
ForegroundPositive={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundVisited={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
|
||||
[Colors:Selection]
|
||||
BackgroundAlternate={{colors.primary_container.light.red}},{{colors.primary_container.light.green}},{{colors.primary_container.light.blue}}
|
||||
BackgroundNormal={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationFocus={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationHover={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundActive={{colors.on_primary.light.red}},{{colors.on_primary.light.green}},{{colors.on_primary.light.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ForegroundLink={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundNegative={{colors.error.light.red}},{{colors.error.light.green}},{{colors.error.light.blue}}
|
||||
ForegroundNeutral={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
ForegroundNormal={{colors.on_primary.light.red}},{{colors.on_primary.light.green}},{{colors.on_primary.light.blue}}
|
||||
ForegroundPositive={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundVisited={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
|
||||
[Colors:Tooltip]
|
||||
BackgroundAlternate={{colors.surface.light.red}},{{colors.surface.light.green}},{{colors.surface.light.blue}}
|
||||
BackgroundNormal={{colors.surface_container.light.red}},{{colors.surface_container.light.green}},{{colors.surface_container.light.blue}}
|
||||
DecorationFocus={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationHover={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundActive={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ForegroundLink={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundNegative={{colors.error.light.red}},{{colors.error.light.green}},{{colors.error.light.blue}}
|
||||
ForegroundNeutral={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
ForegroundNormal={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
ForegroundPositive={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundVisited={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
|
||||
[Colors:View]
|
||||
BackgroundAlternate={{colors.surface_container_low.light.red}},{{colors.surface_container_low.light.green}},{{colors.surface_container_low.light.blue}}
|
||||
BackgroundNormal={{colors.background.light.red}},{{colors.background.light.green}},{{colors.background.light.blue}}
|
||||
DecorationFocus={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationHover={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundActive={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ForegroundLink={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundNegative={{colors.error.light.red}},{{colors.error.light.green}},{{colors.error.light.blue}}
|
||||
ForegroundNeutral={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
ForegroundNormal={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
ForegroundPositive={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundVisited={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
|
||||
[Colors:Window]
|
||||
BackgroundAlternate={{colors.surface_container.light.red}},{{colors.surface_container.light.green}},{{colors.surface_container.light.blue}}
|
||||
BackgroundNormal={{colors.surface.light.red}},{{colors.surface.light.green}},{{colors.surface.light.blue}}
|
||||
DecorationFocus={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationHover={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundActive={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ForegroundLink={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundNegative={{colors.error.light.red}},{{colors.error.light.green}},{{colors.error.light.blue}}
|
||||
ForegroundNeutral={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
ForegroundNormal={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
ForegroundPositive={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundVisited={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
|
||||
[WM]
|
||||
activeBackground={{colors.surface_container.light.red}},{{colors.surface_container.light.green}},{{colors.surface_container.light.blue}}
|
||||
activeBlend={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
activeForeground={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
inactiveBackground={{colors.surface.light.red}},{{colors.surface.light.green}},{{colors.surface.light.blue}}
|
||||
inactiveBlend={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
inactiveForeground={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
@@ -8,6 +8,9 @@
|
||||
cfg = config.programs.dankMaterialShell;
|
||||
jsonFormat = pkgs.formats.json { };
|
||||
in {
|
||||
imports = [
|
||||
(lib.mkRemovedOptionModule ["programs" "dankMaterialShell" "enableNightMode"] "Night mode is now always available.")
|
||||
];
|
||||
options.programs.dankMaterialShell = with lib.types; {
|
||||
enable = lib.mkEnableOption "DankMaterialShell";
|
||||
|
||||
|
||||
12
nix/niri.nix
12
nix/niri.nix
@@ -90,9 +90,15 @@ in {
|
||||
})
|
||||
|
||||
(lib.mkIf cfg.niri.enableSpawn {
|
||||
spawn-at-startup = [
|
||||
{command = ["dms" "run"];}
|
||||
];
|
||||
spawn-at-startup =
|
||||
[
|
||||
{command = ["dms" "run"];}
|
||||
]
|
||||
++ lib.optionals cfg.enableClipboard [
|
||||
{
|
||||
command = ["wl-paste" "--watch" "cliphist" "store"];
|
||||
}
|
||||
];
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -137,6 +137,15 @@
|
||||
"Audio Output Devices (": {
|
||||
"Audio Output Devices (": "オーディオ出力デバイス("
|
||||
},
|
||||
"Authorize": {
|
||||
"Authorize": "許可"
|
||||
},
|
||||
"Authorize pairing with ": {
|
||||
"Authorize pairing with ": "ペアリングを許可 "
|
||||
},
|
||||
"Authorize service for ": {
|
||||
"Authorize service for ": "サービスを許可 "
|
||||
},
|
||||
"Auto Location": {
|
||||
"Auto Location": "自動位置検出"
|
||||
},
|
||||
@@ -218,6 +227,15 @@
|
||||
"Bluetooth Settings": {
|
||||
"Bluetooth Settings": "Bluetooth設定"
|
||||
},
|
||||
"Blur Layer": {
|
||||
"Blur Layer": "ぼかしレイヤー"
|
||||
},
|
||||
"Blur on Overview": {
|
||||
"Blur on Overview": "概要でぼかす"
|
||||
},
|
||||
"Blur wallpaper when niri overview is open": {
|
||||
"Blur wallpaper when niri overview is open": "Niri概要が開いているときに壁紙をぼかす"
|
||||
},
|
||||
"Border": {
|
||||
"Border": "ボーダー"
|
||||
},
|
||||
@@ -362,9 +380,18 @@
|
||||
"Configure which displays show shell components": {
|
||||
"Configure which displays show shell components": "シェルコンポーネントを表示するディスプレイを配置"
|
||||
},
|
||||
"Confirm": {
|
||||
"Confirm": "確認"
|
||||
},
|
||||
"Confirm passkey for ": {
|
||||
"Confirm passkey for ": "パスキーを確認 "
|
||||
},
|
||||
"Connect": {
|
||||
"Connect": "接続"
|
||||
},
|
||||
"Connect to VPN": {
|
||||
"Connect to VPN": ""
|
||||
},
|
||||
"Connect to Wi-Fi": {
|
||||
"Connect to Wi-Fi": "Wi-Fiに接続"
|
||||
},
|
||||
@@ -491,6 +518,9 @@
|
||||
"Device": {
|
||||
"Device": "デバイス"
|
||||
},
|
||||
"Device paired": {
|
||||
"Device paired": "デバイスがペアリングされました"
|
||||
},
|
||||
"Disconnect": {
|
||||
"Disconnect": "切断"
|
||||
},
|
||||
@@ -545,6 +575,9 @@
|
||||
"Domain (optional)": {
|
||||
"Domain (optional)": "ドメイン (オプション)"
|
||||
},
|
||||
"Donate on Ko-fi": {
|
||||
"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で完全に削除できます。"
|
||||
},
|
||||
@@ -575,6 +608,9 @@
|
||||
"Enable WiFi": {
|
||||
"Enable WiFi": "WiFiを有効にする"
|
||||
},
|
||||
"Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.": {
|
||||
"Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.": "コンポジターをターゲットに設定できるぼかしレイヤー(名前空間:dms:blurwallpaper)を有効にします。Niri を手動で設定する必要があります。"
|
||||
},
|
||||
"Enable fingerprint authentication": {
|
||||
"Enable fingerprint authentication": "指紋認証を有効に"
|
||||
},
|
||||
@@ -584,6 +620,15 @@
|
||||
"End": {
|
||||
"End": "終わり"
|
||||
},
|
||||
"Enter 6-digit passkey": {
|
||||
"Enter 6-digit passkey": "6桁のパスキーを入力してください"
|
||||
},
|
||||
"Enter PIN": {
|
||||
"Enter PIN": "PINを入力してください"
|
||||
},
|
||||
"Enter PIN for ": {
|
||||
"Enter PIN for ": "PINを入力してください "
|
||||
},
|
||||
"Enter credentials for ": {
|
||||
"Enter credentials for ": "資格情報を入力"
|
||||
},
|
||||
@@ -596,6 +641,9 @@
|
||||
"Enter filename...": {
|
||||
"Enter filename...": "ファイル名を入力してください..."
|
||||
},
|
||||
"Enter passkey for ": {
|
||||
"Enter passkey for ": "パスキーを入力してください "
|
||||
},
|
||||
"Enter password for ": {
|
||||
"Enter password for ": "パスワードを入力"
|
||||
},
|
||||
@@ -611,15 +659,27 @@
|
||||
"Failed to activate configuration": {
|
||||
"Failed to activate configuration": "設定が適用できませんでした"
|
||||
},
|
||||
"Failed to connect VPN": {
|
||||
"Failed to connect VPN": ""
|
||||
},
|
||||
"Failed to connect to ": {
|
||||
"Failed to connect to ": "接続ができませんでした "
|
||||
},
|
||||
"Failed to disconnect VPN": {
|
||||
"Failed to disconnect VPN": ""
|
||||
},
|
||||
"Failed to disconnect VPNs": {
|
||||
"Failed to disconnect VPNs": ""
|
||||
},
|
||||
"Failed to disconnect WiFi": {
|
||||
"Failed to disconnect WiFi": "WiFiの切断ができませんでした"
|
||||
},
|
||||
"Failed to enable WiFi": {
|
||||
"Failed to enable WiFi": "WiFiを有効化にできませんでした"
|
||||
},
|
||||
"Failed to remove device": {
|
||||
"Failed to remove device": "デバイスの削除に失敗しました"
|
||||
},
|
||||
"Failed to set profile image": {
|
||||
"Failed to set profile image": "プロフィール画像の設定に失敗しました"
|
||||
},
|
||||
@@ -782,6 +842,9 @@
|
||||
"Incorrect password": {
|
||||
"Incorrect password": "パスワードが間違っています"
|
||||
},
|
||||
"Indicator Style": {
|
||||
"Indicator Style": "インジケータースタイル"
|
||||
},
|
||||
"Individual Batteries": {
|
||||
"Individual Batteries": "バッテリーごと"
|
||||
},
|
||||
@@ -1100,6 +1163,18 @@
|
||||
"Padding": {
|
||||
"Padding": "パディング"
|
||||
},
|
||||
"Pair": {
|
||||
"Pair": "ペアリング"
|
||||
},
|
||||
"Pair Bluetooth Device": {
|
||||
"Pair Bluetooth Device": "Bluetoothデバイスのペアリング"
|
||||
},
|
||||
"Pairing failed": {
|
||||
"Pairing failed": "ペアリングが失敗しました"
|
||||
},
|
||||
"Passkey:": {
|
||||
"Passkey:": "パスキー:"
|
||||
},
|
||||
"Password": {
|
||||
"Password": "パスワード"
|
||||
},
|
||||
@@ -1157,6 +1232,9 @@
|
||||
"Plugins": {
|
||||
"Plugins": "プラグイン"
|
||||
},
|
||||
"Plugins:": {
|
||||
"Plugins:": "プラグイン:"
|
||||
},
|
||||
"Popup Position": {
|
||||
"Popup Position": "ポップアップの位置"
|
||||
},
|
||||
@@ -1233,7 +1311,7 @@
|
||||
"Refresh": "リフレッシュ"
|
||||
},
|
||||
"Reload Plugin": {
|
||||
"Reload Plugin": ""
|
||||
"Reload Plugin": "プラグインをリロード"
|
||||
},
|
||||
"Remove": {
|
||||
"Remove": "削除"
|
||||
@@ -1244,6 +1322,9 @@
|
||||
"Reset": {
|
||||
"Reset": "リセット"
|
||||
},
|
||||
"Resources": {
|
||||
"Resources": "リソース"
|
||||
},
|
||||
"Right": {
|
||||
"Right": "右"
|
||||
},
|
||||
@@ -1364,6 +1445,9 @@
|
||||
"Show Workspace Apps": {
|
||||
"Show Workspace Apps": "ワークスペースアプリを表示"
|
||||
},
|
||||
"Show on Last Display": {
|
||||
"Show on Last Display": "最後のディスプレイに表示"
|
||||
},
|
||||
"Show on Overview": {
|
||||
"Show on Overview": "概要に表示"
|
||||
},
|
||||
@@ -1436,6 +1520,9 @@
|
||||
"Storage & Disks": {
|
||||
"Storage & Disks": "ストレージとディスク"
|
||||
},
|
||||
"Support Development": {
|
||||
"Support Development": "開発をサポート"
|
||||
},
|
||||
"Surface": {
|
||||
"Surface": "表面"
|
||||
},
|
||||
@@ -1575,7 +1662,7 @@
|
||||
"Turn off monitors after": "後にモニターの電源を切る"
|
||||
},
|
||||
"Uninstall Plugin": {
|
||||
"Uninstall Plugin": ""
|
||||
"Uninstall Plugin": "プラグインをアンインストール"
|
||||
},
|
||||
"Unpin from Dock": {
|
||||
"Unpin from Dock": "ドックから固定を解除"
|
||||
@@ -1596,7 +1683,7 @@
|
||||
"Update All": "すべて更新"
|
||||
},
|
||||
"Update Plugin": {
|
||||
"Update Plugin": ""
|
||||
"Update Plugin": "プラグインを更新"
|
||||
},
|
||||
"Use 24-hour time format instead of 12-hour AM/PM": {
|
||||
"Use 24-hour time format instead of 12-hour AM/PM": "12時間制のAM/PMではなく、24時間表記を使用"
|
||||
@@ -1694,6 +1781,9 @@
|
||||
"Weather Widget": {
|
||||
"Weather Widget": "天気ウィジェット"
|
||||
},
|
||||
"Website:": {
|
||||
"Website:": "ウェブサイト:"
|
||||
},
|
||||
"When enabled, apps are sorted alphabetically. When disabled, apps are sorted by usage frequency.": {
|
||||
"When enabled, apps are sorted alphabetically. When disabled, apps are sorted by usage frequency.": "有効にすると、アプリはアルファベット順に並べ替えられます。無効にすると、アプリは使用頻度で並べ替えられます。"
|
||||
},
|
||||
|
||||
@@ -137,6 +137,15 @@
|
||||
"Audio Output Devices (": {
|
||||
"Audio Output Devices (": "Dispositivos de Saída de Áudio ("
|
||||
},
|
||||
"Authorize": {
|
||||
"Authorize": ""
|
||||
},
|
||||
"Authorize pairing with ": {
|
||||
"Authorize pairing with ": ""
|
||||
},
|
||||
"Authorize service for ": {
|
||||
"Authorize service for ": ""
|
||||
},
|
||||
"Auto Location": {
|
||||
"Auto Location": "Localização Automática"
|
||||
},
|
||||
@@ -218,6 +227,15 @@
|
||||
"Bluetooth Settings": {
|
||||
"Bluetooth Settings": "Configurações do Bluetooth"
|
||||
},
|
||||
"Blur Layer": {
|
||||
"Blur Layer": ""
|
||||
},
|
||||
"Blur on Overview": {
|
||||
"Blur on Overview": ""
|
||||
},
|
||||
"Blur wallpaper when niri overview is open": {
|
||||
"Blur wallpaper when niri overview is open": ""
|
||||
},
|
||||
"Border": {
|
||||
"Border": "Borda"
|
||||
},
|
||||
@@ -362,9 +380,18 @@
|
||||
"Configure which displays show shell components": {
|
||||
"Configure which displays show shell components": "Configurar em quais telas serão mostrados os componentes do shell"
|
||||
},
|
||||
"Confirm": {
|
||||
"Confirm": ""
|
||||
},
|
||||
"Confirm passkey for ": {
|
||||
"Confirm passkey for ": ""
|
||||
},
|
||||
"Connect": {
|
||||
"Connect": "Conectar"
|
||||
},
|
||||
"Connect to VPN": {
|
||||
"Connect to VPN": ""
|
||||
},
|
||||
"Connect to Wi-Fi": {
|
||||
"Connect to Wi-Fi": "Conectar ao Wi-Fi"
|
||||
},
|
||||
@@ -491,6 +518,9 @@
|
||||
"Device": {
|
||||
"Device": "Dispositivo"
|
||||
},
|
||||
"Device paired": {
|
||||
"Device paired": ""
|
||||
},
|
||||
"Disconnect": {
|
||||
"Disconnect": "Desconectar"
|
||||
},
|
||||
@@ -545,6 +575,9 @@
|
||||
"Domain (optional)": {
|
||||
"Domain (optional)": ""
|
||||
},
|
||||
"Donate on Ko-fi": {
|
||||
"Donate on 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.": "Arraste Widgets para reordená-los nas seções. Use o ícone de olho para esconder ou mostrá-los (manter o espaçamento), ou clique no X para removê-los por completo."
|
||||
},
|
||||
@@ -575,6 +608,9 @@
|
||||
"Enable WiFi": {
|
||||
"Enable WiFi": "Ativar Wi-fi "
|
||||
},
|
||||
"Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.": {
|
||||
"Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.": ""
|
||||
},
|
||||
"Enable fingerprint authentication": {
|
||||
"Enable fingerprint authentication": "Habilitar autenticação por impressão digital"
|
||||
},
|
||||
@@ -584,6 +620,15 @@
|
||||
"End": {
|
||||
"End": "Fim"
|
||||
},
|
||||
"Enter 6-digit passkey": {
|
||||
"Enter 6-digit passkey": ""
|
||||
},
|
||||
"Enter PIN": {
|
||||
"Enter PIN": ""
|
||||
},
|
||||
"Enter PIN for ": {
|
||||
"Enter PIN for ": ""
|
||||
},
|
||||
"Enter credentials for ": {
|
||||
"Enter credentials for ": "Insira as credenciais para "
|
||||
},
|
||||
@@ -596,6 +641,9 @@
|
||||
"Enter filename...": {
|
||||
"Enter filename...": "Insira nome do arquivo..."
|
||||
},
|
||||
"Enter passkey for ": {
|
||||
"Enter passkey for ": ""
|
||||
},
|
||||
"Enter password for ": {
|
||||
"Enter password for ": "Insira senha para "
|
||||
},
|
||||
@@ -611,15 +659,27 @@
|
||||
"Failed to activate configuration": {
|
||||
"Failed to activate configuration": ""
|
||||
},
|
||||
"Failed to connect VPN": {
|
||||
"Failed to connect VPN": ""
|
||||
},
|
||||
"Failed to connect to ": {
|
||||
"Failed to connect to ": ""
|
||||
},
|
||||
"Failed to disconnect VPN": {
|
||||
"Failed to disconnect VPN": ""
|
||||
},
|
||||
"Failed to disconnect VPNs": {
|
||||
"Failed to disconnect VPNs": ""
|
||||
},
|
||||
"Failed to disconnect WiFi": {
|
||||
"Failed to disconnect WiFi": ""
|
||||
},
|
||||
"Failed to enable WiFi": {
|
||||
"Failed to enable WiFi": ""
|
||||
},
|
||||
"Failed to remove device": {
|
||||
"Failed to remove device": ""
|
||||
},
|
||||
"Failed to set profile image": {
|
||||
"Failed to set profile image": ""
|
||||
},
|
||||
@@ -782,6 +842,9 @@
|
||||
"Incorrect password": {
|
||||
"Incorrect password": ""
|
||||
},
|
||||
"Indicator Style": {
|
||||
"Indicator Style": ""
|
||||
},
|
||||
"Individual Batteries": {
|
||||
"Individual Batteries": "Baterias Individuais"
|
||||
},
|
||||
@@ -1100,6 +1163,18 @@
|
||||
"Padding": {
|
||||
"Padding": "Preenchimento"
|
||||
},
|
||||
"Pair": {
|
||||
"Pair": ""
|
||||
},
|
||||
"Pair Bluetooth Device": {
|
||||
"Pair Bluetooth Device": ""
|
||||
},
|
||||
"Pairing failed": {
|
||||
"Pairing failed": ""
|
||||
},
|
||||
"Passkey:": {
|
||||
"Passkey:": ""
|
||||
},
|
||||
"Password": {
|
||||
"Password": "Senha"
|
||||
},
|
||||
@@ -1157,6 +1232,9 @@
|
||||
"Plugins": {
|
||||
"Plugins": "Plugins"
|
||||
},
|
||||
"Plugins:": {
|
||||
"Plugins:": ""
|
||||
},
|
||||
"Popup Position": {
|
||||
"Popup Position": "Posição do Popup"
|
||||
},
|
||||
@@ -1244,6 +1322,9 @@
|
||||
"Reset": {
|
||||
"Reset": "Resetar"
|
||||
},
|
||||
"Resources": {
|
||||
"Resources": ""
|
||||
},
|
||||
"Right": {
|
||||
"Right": ""
|
||||
},
|
||||
@@ -1364,6 +1445,9 @@
|
||||
"Show Workspace Apps": {
|
||||
"Show Workspace Apps": "Mostrar Aplicativos da Área de Trabalho Virtual"
|
||||
},
|
||||
"Show on Last Display": {
|
||||
"Show on Last Display": ""
|
||||
},
|
||||
"Show on Overview": {
|
||||
"Show on Overview": "Mostrar na Visão Geral"
|
||||
},
|
||||
@@ -1436,6 +1520,9 @@
|
||||
"Storage & Disks": {
|
||||
"Storage & Disks": "Armazenamento & Discos"
|
||||
},
|
||||
"Support Development": {
|
||||
"Support Development": ""
|
||||
},
|
||||
"Surface": {
|
||||
"Surface": "Superfície"
|
||||
},
|
||||
@@ -1694,6 +1781,9 @@
|
||||
"Weather Widget": {
|
||||
"Weather Widget": "Widget de Clima"
|
||||
},
|
||||
"Website:": {
|
||||
"Website:": ""
|
||||
},
|
||||
"When enabled, apps are sorted alphabetically. When disabled, apps are sorted by usage frequency.": {
|
||||
"When enabled, apps are sorted alphabetically. When disabled, apps are sorted by usage frequency.": "Quando ativado, apps são ordenados alfabeticamente. Quando desativado, apps são ordenados por frequência de uso"
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user