mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-05-14 16:22:46 -04:00
Compare commits
142 Commits
v1.4.3
..
fbc1ff62c7
| Author | SHA1 | Date | |
|---|---|---|---|
| fbc1ff62c7 | |||
| 1fe72e1a66 | |||
| f82d7610e3 | |||
| bd6ad53875 | |||
| 5d09acca4c | |||
| b4e7c4a4cd | |||
| a6269084c0 | |||
| 8271d8423d | |||
| c76e29c457 | |||
| 4750a7553b | |||
| 60786921a9 | |||
| 751bbcc127 | |||
| 58e8dd5456 | |||
| 1586c25847 | |||
| cded5a7948 | |||
| 6238e065f2 | |||
| 72fbbfdd0d | |||
| 2796c1cd4d | |||
| 54c9886627 | |||
| 05713cb389 | |||
| 8bb3ee5f18 | |||
| bc0b4825f1 | |||
| ef7f17abf4 | |||
| 876cd21f0b | |||
| 5c92d49873 | |||
| da47b573be | |||
| 2f04be8778 | |||
| 69178ddfd8 | |||
| a310f6fff0 | |||
| 7474abe286 | |||
| df2ba3a3c6 | |||
| e536456236 | |||
| 8d77122da3 | |||
| fb66effa51 | |||
| 5052e71c31 | |||
| bfc78d16ca | |||
| c425e3562b | |||
| 1f26092aa9 | |||
| 2849bb96f4 | |||
| 7b749f2a4c | |||
| 8803c94ce0 | |||
| f5235c943b | |||
| 59fec889b5 | |||
| f42f04a807 | |||
| 51f6f37925 | |||
| 9651a4ca98 | |||
| 2b7fd36322 | |||
| b8014fd4df | |||
| 07460f6e1f | |||
| f7bf3b2afb | |||
| 056f298cdf | |||
| e83da53162 | |||
| 9f38a47a02 | |||
| 236a4d4a6d | |||
| 0909471510 | |||
| 05eaf59c89 | |||
| 7749613801 | |||
| e3dbaedbb4 | |||
| 9f17ced6de | |||
| de54ef871d | |||
| b0da45d6d0 | |||
| 9b2a46fa92 | |||
| 12099d2db6 | |||
| 84fa75936a | |||
| d78d8121a1 | |||
| a9a3a52872 | |||
| 912e3bdfce | |||
| ee1b25d9e8 | |||
| 20ef5e2c18 | |||
| 6ee419bc52 | |||
| 85b00d3c76 | |||
| bc4ad31d48 | |||
| 71aad8ee32 | |||
| 8bb8231559 | |||
| 3cf9caae89 | |||
| f983c67135 | |||
| f2aef5b93f | |||
| 46d4288969 | |||
| 65516e872f | |||
| 171329246c | |||
| b2bee699e0 | |||
| 95c66b4d67 | |||
| babc8feb2b | |||
| 2f445c546a | |||
| a0283b3e3e | |||
| 61bd156fb0 | |||
| 8ad0cf8e5f | |||
| ecd6d70da6 | |||
| 359617d927 | |||
| 38c286329a | |||
| 401b4095cc | |||
| 06ab1a8ef0 | |||
| 726fb8b015 | |||
| b3b5c7a59f | |||
| d18f934978 | |||
| e67f1f79bc | |||
| e931829411 | |||
| db8ebd606c | |||
| 072a358a94 | |||
| 6ceb1b150c | |||
| a4e03e1877 | |||
| 02b3e4277b | |||
| 37daf801e6 | |||
| 68d9f7eeb2 | |||
| 526e2420ca | |||
| a9cc58fc28 | |||
| 77889ce1c6 | |||
| 549073119e | |||
| 5c5af5795f | |||
| 68e10934e4 | |||
| c67bb1444a | |||
| 07389a152e | |||
| e562e21555 | |||
| 86dfe7dd3f | |||
| ac0a8f3449 | |||
| 8e4a63db67 | |||
| c02c63806f | |||
| 42e5d7f6e9 | |||
| d8cf1af422 | |||
| 9723661c80 | |||
| 81cba7ad97 | |||
| c23f58de40 | |||
| 2cf67ca7da | |||
| 392bd850ea | |||
| 3b2ad9d1bd | |||
| 27b7474180 | |||
| 63948d728e | |||
| d219d3b873 | |||
| 93ab290bc1 | |||
| 7335c5d79a | |||
| 242ead722a | |||
| 8a6d9696a8 | |||
| 896b7ea242 | |||
| 0c7f4c7828 | |||
| 3d35af2a87 | |||
| fed3c36f84 | |||
| 414d81aa40 | |||
| d548803769 | |||
| 1180258394 | |||
| 48a566a24b | |||
| 3bc5d1df81 | |||
| c7222e2e86 |
@@ -9,8 +9,8 @@ on:
|
|||||||
type: choice
|
type: choice
|
||||||
options:
|
options:
|
||||||
- dms
|
- dms
|
||||||
- dms-git
|
|
||||||
- dms-greeter
|
- dms-greeter
|
||||||
|
- dms-git
|
||||||
- all
|
- all
|
||||||
default: "dms"
|
default: "dms"
|
||||||
rebuild_release:
|
rebuild_release:
|
||||||
@@ -119,9 +119,8 @@ jobs:
|
|||||||
echo "🔄 Manual rebuild requested: $PKG (db$REBUILD)"
|
echo "🔄 Manual rebuild requested: $PKG (db$REBUILD)"
|
||||||
|
|
||||||
elif [[ "$PKG" == "all" ]]; then
|
elif [[ "$PKG" == "all" ]]; then
|
||||||
# Check each package and build list of those needing updates
|
# Check each stable package and build list of those needing updates
|
||||||
PACKAGES_TO_UPDATE=()
|
PACKAGES_TO_UPDATE=()
|
||||||
check_dms_git && PACKAGES_TO_UPDATE+=("dms-git")
|
|
||||||
if check_dms_stable; then
|
if check_dms_stable; then
|
||||||
PACKAGES_TO_UPDATE+=("dms")
|
PACKAGES_TO_UPDATE+=("dms")
|
||||||
if [[ -n "$LATEST_TAG" ]]; then
|
if [[ -n "$LATEST_TAG" ]]; then
|
||||||
@@ -140,7 +139,7 @@ jobs:
|
|||||||
else
|
else
|
||||||
echo "packages=" >> $GITHUB_OUTPUT
|
echo "packages=" >> $GITHUB_OUTPUT
|
||||||
echo "has_updates=false" >> $GITHUB_OUTPUT
|
echo "has_updates=false" >> $GITHUB_OUTPUT
|
||||||
echo "✓ All packages up to date"
|
echo "✓ Both packages up to date"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
elif [[ "$PKG" == "dms-git" ]]; then
|
elif [[ "$PKG" == "dms-git" ]]; then
|
||||||
@@ -245,7 +244,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Update dms-git spec version
|
- name: Update dms-git spec version
|
||||||
if: contains(steps.packages.outputs.packages, 'dms-git') || steps.packages.outputs.packages == 'all'
|
if: contains(steps.packages.outputs.packages, 'dms-git')
|
||||||
run: |
|
run: |
|
||||||
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
|
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
|
||||||
COMMIT_COUNT=$(git rev-list --count HEAD)
|
COMMIT_COUNT=$(git rev-list --count HEAD)
|
||||||
@@ -266,7 +265,7 @@ jobs:
|
|||||||
} > distro/opensuse/dms-git.spec
|
} > distro/opensuse/dms-git.spec
|
||||||
|
|
||||||
- name: Update Debian dms-git changelog version
|
- name: Update Debian dms-git changelog version
|
||||||
if: contains(steps.packages.outputs.packages, 'dms-git') || steps.packages.outputs.packages == 'all'
|
if: contains(steps.packages.outputs.packages, 'dms-git')
|
||||||
run: |
|
run: |
|
||||||
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
|
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
|
||||||
COMMIT_COUNT=$(git rev-list --count HEAD)
|
COMMIT_COUNT=$(git rev-list --count HEAD)
|
||||||
@@ -389,7 +388,7 @@ jobs:
|
|||||||
UPLOADED_PACKAGES=()
|
UPLOADED_PACKAGES=()
|
||||||
SKIPPED_PACKAGES=()
|
SKIPPED_PACKAGES=()
|
||||||
|
|
||||||
# PACKAGES can be space-separated list (e.g., "dms-git dms" from "all" check)
|
# PACKAGES can be space-separated list (e.g., "dms dms-greeter" from "all" check)
|
||||||
# Loop through each package and upload
|
# Loop through each package and upload
|
||||||
for PKG in $PACKAGES; do
|
for PKG in $PACKAGES; do
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -4,9 +4,15 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
package:
|
package:
|
||||||
description: "Package to upload (dms, dms-git, dms-greeter, or all)"
|
description: "Package to upload"
|
||||||
required: false
|
required: true
|
||||||
default: "dms-git"
|
type: choice
|
||||||
|
options:
|
||||||
|
- dms
|
||||||
|
- dms-greeter
|
||||||
|
- dms-git
|
||||||
|
- all
|
||||||
|
default: "dms"
|
||||||
rebuild_release:
|
rebuild_release:
|
||||||
description: "Release number for rebuilds (e.g., 2, 3, 4 for ppa2, ppa3, ppa4)"
|
description: "Release number for rebuilds (e.g., 2, 3, 4 for ppa2, ppa3, ppa4)"
|
||||||
required: false
|
required: false
|
||||||
@@ -139,7 +145,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
# Fallback
|
# Fallback
|
||||||
echo "packages=dms-git" >> $GITHUB_OUTPUT
|
echo "packages=dms" >> $GITHUB_OUTPUT
|
||||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
echo "has_updates=true" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -209,7 +215,7 @@ jobs:
|
|||||||
echo "✓ Using rebuild release number: ppa$REBUILD_RELEASE"
|
echo "✓ Using rebuild release number: ppa$REBUILD_RELEASE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# PACKAGES can be space-separated list (e.g., "dms-git dms" from "all" check)
|
# PACKAGES can be space-separated list (e.g., "dms-git dms dms-greeter" from "all" check)
|
||||||
# Loop through each package and upload
|
# Loop through each package and upload
|
||||||
for PKG in $PACKAGES; do
|
for PKG in $PACKAGES; do
|
||||||
# Map package to PPA name
|
# Map package to PPA name
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ require (
|
|||||||
github.com/sblinch/kdl-go v0.0.0-20260121213736-8b7053306ca6
|
github.com/sblinch/kdl-go v0.0.0-20260121213736-8b7053306ca6
|
||||||
github.com/spf13/cobra v1.10.2
|
github.com/spf13/cobra v1.10.2
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
|
github.com/yeqown/go-qrcode/v2 v2.2.5
|
||||||
|
github.com/yeqown/go-qrcode/writer/standard v1.3.0
|
||||||
github.com/yuin/goldmark v1.7.16
|
github.com/yuin/goldmark v1.7.16
|
||||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||||
go.etcd.io/bbolt v1.4.3
|
go.etcd.io/bbolt v1.4.3
|
||||||
@@ -32,15 +34,19 @@ require (
|
|||||||
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
|
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
|
||||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||||
github.com/emirpasic/gods v1.18.1 // indirect
|
github.com/emirpasic/gods v1.18.1 // indirect
|
||||||
|
github.com/fogleman/gg v1.3.0 // indirect
|
||||||
github.com/go-git/gcfg/v2 v2.0.2 // indirect
|
github.com/go-git/gcfg/v2 v2.0.2 // indirect
|
||||||
github.com/go-git/go-billy/v6 v6.0.0-20260209124918-37866f83c2d3 // indirect
|
github.com/go-git/go-billy/v6 v6.0.0-20260209124918-37866f83c2d3 // indirect
|
||||||
github.com/go-logfmt/logfmt v0.6.1 // indirect
|
github.com/go-logfmt/logfmt v0.6.1 // indirect
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.6.0 // indirect
|
github.com/kevinburke/ssh_config v1.6.0 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||||
github.com/pjbgf/sha1cd v0.5.0 // indirect
|
github.com/pjbgf/sha1cd v0.5.0 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/sergi/go-diff v1.4.0 // indirect
|
github.com/sergi/go-diff v1.4.0 // indirect
|
||||||
github.com/stretchr/objx v0.5.3 // indirect
|
github.com/stretchr/objx v0.5.3 // indirect
|
||||||
|
github.com/yeqown/reedsolomon v1.0.0 // indirect
|
||||||
golang.org/x/crypto v0.48.0 // indirect
|
golang.org/x/crypto v0.48.0 // indirect
|
||||||
golang.org/x/net v0.50.0 // indirect
|
golang.org/x/net v0.50.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
+12
@@ -58,6 +58,8 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc
|
|||||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||||
|
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||||
|
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||||
@@ -75,6 +77,8 @@ github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/
|
|||||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ=
|
github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ=
|
||||||
github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
|
github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
@@ -115,6 +119,8 @@ github.com/pilebones/go-udev v0.9.1 h1:uN72M1C1fgzhsVmBGEM8w9RD1JY4iVsPZpr+Z6rb3
|
|||||||
github.com/pilebones/go-udev v0.9.1/go.mod h1:Bgcl07crebF3JSeS4+nuaRvhWFdCeFoBhXXeAp93XNo=
|
github.com/pilebones/go-udev v0.9.1/go.mod h1:Bgcl07crebF3JSeS4+nuaRvhWFdCeFoBhXXeAp93XNo=
|
||||||
github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
|
github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
|
||||||
github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
|
github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
@@ -142,6 +148,12 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
|
|||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||||
|
github.com/yeqown/go-qrcode/v2 v2.2.5 h1:HCOe2bSjkhZyYoyyNaXNzh4DJZll6inVJQQw+8228Zk=
|
||||||
|
github.com/yeqown/go-qrcode/v2 v2.2.5/go.mod h1:uHpt9CM0V1HeXLz+Wg5MN50/sI/fQhfkZlOM+cOTHxw=
|
||||||
|
github.com/yeqown/go-qrcode/writer/standard v1.3.0 h1:chdyhEfRtUPgQtuPeaWVGQ/TQx4rE1PqeoW3U+53t34=
|
||||||
|
github.com/yeqown/go-qrcode/writer/standard v1.3.0/go.mod h1:O4MbzsotGCvy8upYPCR91j81dr5XLT7heuljcNXW+oQ=
|
||||||
|
github.com/yeqown/reedsolomon v1.0.0 h1:x1h/Ej/uJnNu8jaX7GLHBWmZKCAWjEJTetkqaabr4B0=
|
||||||
|
github.com/yeqown/reedsolomon v1.0.0/go.mod h1:P76zpcn2TCuL0ul1Fso373qHRc69LKwAw/Iy6g1WiiM=
|
||||||
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE=
|
github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE=
|
||||||
github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||||
|
|||||||
@@ -1062,6 +1062,62 @@ func (_c *MockBackend_GetWiFiNetworkDetails_Call) RunAndReturn(run func(string)
|
|||||||
return _c
|
return _c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetWiFiQRCodeContent provides a mock function with given fields: ssid
|
||||||
|
func (_m *MockBackend) GetWiFiQRCodeContent(ssid string) (string, error) {
|
||||||
|
ret := _m.Called(ssid)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetWiFiQRCodeContent")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 string
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(string) (string, error)); ok {
|
||||||
|
return rf(ssid)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(string) string); ok {
|
||||||
|
r0 = rf(ssid)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||||
|
r1 = rf(ssid)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockBackend_GetWiFiQRCodeContent_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWiFiQRCodeContent'
|
||||||
|
type MockBackend_GetWiFiQRCodeContent_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWiFiQRCodeContent is a helper method to define mock.On call
|
||||||
|
// - ssid string
|
||||||
|
func (_e *MockBackend_Expecter) GetWiFiQRCodeContent(ssid interface{}) *MockBackend_GetWiFiQRCodeContent_Call {
|
||||||
|
return &MockBackend_GetWiFiQRCodeContent_Call{Call: _e.mock.On("GetWiFiQRCodeContent", ssid)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockBackend_GetWiFiQRCodeContent_Call) Run(run func(ssid string)) *MockBackend_GetWiFiQRCodeContent_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run(args[0].(string))
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockBackend_GetWiFiQRCodeContent_Call) Return(_a0 string, _a1 error) *MockBackend_GetWiFiQRCodeContent_Call {
|
||||||
|
_c.Call.Return(_a0, _a1)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockBackend_GetWiFiQRCodeContent_Call) RunAndReturn(run func(string) (string, error)) *MockBackend_GetWiFiQRCodeContent_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
// GetWiredConnections provides a mock function with no fields
|
// GetWiredConnections provides a mock function with no fields
|
||||||
func (_m *MockBackend) GetWiredConnections() ([]network.WiredConnection, error) {
|
func (_m *MockBackend) GetWiredConnections() ([]network.WiredConnection, error) {
|
||||||
ret := _m.Called()
|
ret := _m.Called()
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ type Backend interface {
|
|||||||
ScanWiFi() error
|
ScanWiFi() error
|
||||||
ScanWiFiDevice(device string) error
|
ScanWiFiDevice(device string) error
|
||||||
GetWiFiNetworkDetails(ssid string) (*NetworkInfoResponse, error)
|
GetWiFiNetworkDetails(ssid string) (*NetworkInfoResponse, error)
|
||||||
|
GetWiFiQRCodeContent(ssid string) (string, error)
|
||||||
GetWiFiDevices() []WiFiDevice
|
GetWiFiDevices() []WiFiDevice
|
||||||
|
|
||||||
ConnectWiFi(req ConnectionRequest) error
|
ConnectWiFi(req ConnectionRequest) error
|
||||||
|
|||||||
@@ -111,6 +111,10 @@ func (b *HybridIwdNetworkdBackend) GetWiFiNetworkDetails(ssid string) (*NetworkI
|
|||||||
return b.wifi.GetWiFiNetworkDetails(ssid)
|
return b.wifi.GetWiFiNetworkDetails(ssid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *HybridIwdNetworkdBackend) GetWiFiQRCodeContent(ssid string) (string, error) {
|
||||||
|
return b.wifi.GetWiFiQRCodeContent(ssid)
|
||||||
|
}
|
||||||
|
|
||||||
func (b *HybridIwdNetworkdBackend) ConnectWiFi(req ConnectionRequest) error {
|
func (b *HybridIwdNetworkdBackend) ConnectWiFi(req ConnectionRequest) error {
|
||||||
if err := b.wifi.ConnectWiFi(req); err != nil {
|
if err := b.wifi.ConnectWiFi(req); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package network
|
package network
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
func (b *IWDBackend) GetWiredConnections() ([]WiredConnection, error) {
|
func (b *IWDBackend) GetWiredConnections() ([]WiredConnection, error) {
|
||||||
return nil, fmt.Errorf("wired connections not supported by iwd")
|
return nil, fmt.Errorf("wired connections not supported by iwd")
|
||||||
@@ -112,3 +115,19 @@ func (b *IWDBackend) getWiFiDevicesLocked() []WiFiDevice {
|
|||||||
Networks: b.state.WiFiNetworks,
|
Networks: b.state.WiFiNetworks,
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *IWDBackend) GetWiFiQRCodeContent(ssid string) (string, error) {
|
||||||
|
path := iwdConfigPath(ssid)
|
||||||
|
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("no saved iwd config for `%s`: %w", ssid, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
passphrase, err := parseIWDPassphrase(string(data))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to read passphrase for `%s`: %w", ssid, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return FormatWiFiQRString("WPA", ssid, passphrase), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ func (b *SystemdNetworkdBackend) GetWiFiNetworkDetails(ssid string) (*NetworkInf
|
|||||||
return nil, fmt.Errorf("WiFi details not supported by networkd backend")
|
return nil, fmt.Errorf("WiFi details not supported by networkd backend")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *SystemdNetworkdBackend) GetWiFiQRCodeContent(ssid string) (string, error) {
|
||||||
|
return "", fmt.Errorf("WiFi QR Code not supported by networkd backend")
|
||||||
|
}
|
||||||
|
|
||||||
func (b *SystemdNetworkdBackend) ConnectWiFi(req ConnectionRequest) error {
|
func (b *SystemdNetworkdBackend) ConnectWiFi(req ConnectionRequest) error {
|
||||||
return fmt.Errorf("WiFi connect not supported by networkd backend")
|
return fmt.Errorf("WiFi connect not supported by networkd backend")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,6 +196,65 @@ func (b *NetworkManagerBackend) GetWiFiNetworkDetails(ssid string) (*NetworkInfo
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *NetworkManagerBackend) GetWiFiQRCodeContent(ssid string) (string, error) {
|
||||||
|
conn, err := b.findConnection(ssid)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("no saved connection for `%s`: %w", ssid, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
connSettings, err := conn.GetSettings()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get settings for `%s`: %w", ssid, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secSettings, ok := connSettings["802-11-wireless-security"]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("network `%s` has no security settings", ssid)
|
||||||
|
}
|
||||||
|
|
||||||
|
keyMgmt, ok := secSettings["key-mgmt"].(string)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("failed to identify security type of network `%s`", ssid)
|
||||||
|
}
|
||||||
|
|
||||||
|
var securityType string
|
||||||
|
switch keyMgmt {
|
||||||
|
case "none":
|
||||||
|
authAlg, _ := secSettings["auth-alg"].(string)
|
||||||
|
switch authAlg {
|
||||||
|
case "open":
|
||||||
|
securityType = "nopass"
|
||||||
|
default:
|
||||||
|
securityType = "WEP"
|
||||||
|
}
|
||||||
|
case "ieee8021x":
|
||||||
|
securityType = "WEP"
|
||||||
|
default:
|
||||||
|
securityType = "WPA"
|
||||||
|
}
|
||||||
|
|
||||||
|
if securityType != "WPA" {
|
||||||
|
return "", fmt.Errorf("QR code generation only supports WPA connections, `%s` uses %s", ssid, securityType)
|
||||||
|
}
|
||||||
|
|
||||||
|
secrets, err := conn.GetSecrets("802-11-wireless-security")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to retrieve connection secrets for `%s`: %w", ssid, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secSecrets, ok := secrets["802-11-wireless-security"]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("failed to retrieve password for `%s`", ssid)
|
||||||
|
}
|
||||||
|
|
||||||
|
psk, ok := secSecrets["psk"].(string)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("failed to retrieve password for `%s`", ssid)
|
||||||
|
}
|
||||||
|
|
||||||
|
return FormatWiFiQRString(securityType, ssid, psk), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (b *NetworkManagerBackend) ConnectWiFi(req ConnectionRequest) error {
|
func (b *NetworkManagerBackend) ConnectWiFi(req ConnectionRequest) error {
|
||||||
devInfo, err := b.getWifiDeviceForConnection(req.Device)
|
devInfo, err := b.getWifiDeviceForConnection(req.Device)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||||
@@ -40,6 +41,10 @@ func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
|
|||||||
handleSetPreference(conn, req, manager)
|
handleSetPreference(conn, req, manager)
|
||||||
case "network.info":
|
case "network.info":
|
||||||
handleGetNetworkInfo(conn, req, manager)
|
handleGetNetworkInfo(conn, req, manager)
|
||||||
|
case "network.qrcode":
|
||||||
|
handleGetNetworkQRCode(conn, req, manager)
|
||||||
|
case "network.delete-qrcode":
|
||||||
|
handleDeleteQRCode(conn, req, manager)
|
||||||
case "network.ethernet.info":
|
case "network.ethernet.info":
|
||||||
handleGetWiredNetworkInfo(conn, req, manager)
|
handleGetWiredNetworkInfo(conn, req, manager)
|
||||||
case "network.subscribe":
|
case "network.subscribe":
|
||||||
@@ -320,6 +325,42 @@ func handleGetNetworkInfo(conn net.Conn, req models.Request, manager *Manager) {
|
|||||||
models.Respond(conn, req.ID, network)
|
models.Respond(conn, req.ID, network)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleGetNetworkQRCode(conn net.Conn, req models.Request, manager *Manager) {
|
||||||
|
ssid, err := params.String(req.Params, "ssid")
|
||||||
|
if err != nil {
|
||||||
|
models.RespondError(conn, req.ID, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := manager.GetNetworkQRCode(ssid)
|
||||||
|
if err != nil {
|
||||||
|
models.RespondError(conn, req.ID, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
models.Respond(conn, req.ID, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleDeleteQRCode(conn net.Conn, req models.Request, _ *Manager) {
|
||||||
|
path, err := params.String(req.Params, "path")
|
||||||
|
if err != nil {
|
||||||
|
models.RespondError(conn, req.ID, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isValidQRCodePath(path) {
|
||||||
|
models.RespondError(conn, req.ID, "invalid QR code path")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Remove(path); err != nil {
|
||||||
|
models.RespondError(conn, req.ID, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "QR code file deleted"})
|
||||||
|
}
|
||||||
|
|
||||||
func handleGetWiredNetworkInfo(conn net.Conn, req models.Request, manager *Manager) {
|
func handleGetWiredNetworkInfo(conn net.Conn, req models.Request, manager *Manager) {
|
||||||
uuid, err := params.String(req.Params, "uuid")
|
uuid, err := params.String(req.Params, "uuid")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
||||||
|
"github.com/yeqown/go-qrcode/v2"
|
||||||
|
"github.com/yeqown/go-qrcode/writer/standard"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewManager() (*Manager, error) {
|
func NewManager() (*Manager, error) {
|
||||||
@@ -438,6 +440,43 @@ func (m *Manager) GetNetworkInfoDetailed(ssid string) (*NetworkInfoResponse, err
|
|||||||
return m.backend.GetWiFiNetworkDetails(ssid)
|
return m.backend.GetWiFiNetworkDetails(ssid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) GetNetworkQRCode(ssid string) ([2]string, error) {
|
||||||
|
content, err := m.backend.GetWiFiQRCodeContent(ssid)
|
||||||
|
if err != nil {
|
||||||
|
return [2]string{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
qrc, err := qrcode.New(content)
|
||||||
|
if err != nil {
|
||||||
|
return [2]string{}, fmt.Errorf("failed to create QR code for `%s`: %w", ssid, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pathThemed, pathNormal := qrCodePaths(ssid)
|
||||||
|
|
||||||
|
wThemed, err := standard.New(
|
||||||
|
pathThemed,
|
||||||
|
standard.WithBuiltinImageEncoder(standard.PNG_FORMAT),
|
||||||
|
standard.WithBgTransparent(),
|
||||||
|
standard.WithFgColorRGBHex("#ffffff"),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return [2]string{}, fmt.Errorf("failed to create QR code writer: %w", err)
|
||||||
|
}
|
||||||
|
if err := qrc.Save(wThemed); err != nil {
|
||||||
|
return [2]string{}, fmt.Errorf("failed to save QR code for `%s`: %w", ssid, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wNormal, err := standard.New(pathNormal, standard.WithBuiltinImageEncoder(standard.PNG_FORMAT))
|
||||||
|
if err != nil {
|
||||||
|
return [2]string{}, fmt.Errorf("failed to create QR code writer: %w", err)
|
||||||
|
}
|
||||||
|
if err := qrc.Save(wNormal); err != nil {
|
||||||
|
return [2]string{}, fmt.Errorf("failed to save QR code for `%s`: %w", ssid, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return [2]string{pathThemed, pathNormal}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Manager) ToggleWiFi() error {
|
func (m *Manager) ToggleWiFi() error {
|
||||||
enabled, err := m.backend.GetWiFiEnabled()
|
enabled, err := m.backend.GetWiFiEnabled()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const qrCodeTmpPrefix = "/tmp/dank-wifi-qrcode-"
|
||||||
|
|
||||||
|
func FormatWiFiQRString(securityType, ssid, password string) string {
|
||||||
|
return fmt.Sprintf("WIFI:T:%s;S:%s;P:%s;;", securityType, ssid, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
func qrCodePaths(ssid string) (themed, normal string) {
|
||||||
|
safe := sanitizeSSIDForPath(ssid)
|
||||||
|
themed = fmt.Sprintf("%s%s-themed.png", qrCodeTmpPrefix, safe)
|
||||||
|
normal = fmt.Sprintf("%s%s-normal.png", qrCodeTmpPrefix, safe)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidQRCodePath(path string) bool {
|
||||||
|
clean := filepath.Clean(path)
|
||||||
|
return strings.HasPrefix(clean, qrCodeTmpPrefix) && strings.HasSuffix(clean, ".png")
|
||||||
|
}
|
||||||
|
|
||||||
|
var safePathChar = regexp.MustCompile(`[^a-zA-Z0-9_-]`)
|
||||||
|
|
||||||
|
func sanitizeSSIDForPath(ssid string) string {
|
||||||
|
return safePathChar.ReplaceAllString(ssid, "_")
|
||||||
|
}
|
||||||
|
|
||||||
|
var iwdVerbatimSSID = regexp.MustCompile(`^[a-zA-Z0-9 _-]+$`)
|
||||||
|
|
||||||
|
func iwdConfigPath(ssid string) string {
|
||||||
|
switch {
|
||||||
|
case iwdVerbatimSSID.MatchString(ssid):
|
||||||
|
return fmt.Sprintf("/var/lib/iwd/%s.psk", ssid)
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("/var/lib/iwd/=%x.psk", []byte(ssid))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseIWDPassphrase(data string) (string, error) {
|
||||||
|
inSecurity := false
|
||||||
|
for _, line := range strings.Split(data, "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
switch {
|
||||||
|
case line == "[Security]":
|
||||||
|
inSecurity = true
|
||||||
|
case strings.HasPrefix(line, "["):
|
||||||
|
inSecurity = false
|
||||||
|
case inSecurity && strings.HasPrefix(line, "Passphrase="):
|
||||||
|
return strings.TrimPrefix(line, "Passphrase="), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("no passphrase found in iwd config")
|
||||||
|
}
|
||||||
@@ -27,12 +27,12 @@ override_dh_auto_build:
|
|||||||
# Verify core directory exists (native package format has source at root)
|
# Verify core directory exists (native package format has source at root)
|
||||||
test -d core || (echo "ERROR: core directory not found!" && exit 1)
|
test -d core || (echo "ERROR: core directory not found!" && exit 1)
|
||||||
|
|
||||||
# Patch go.mod to use Go 1.24 base version (Debian 13 has 1.23.x, may vary)
|
# Pin go.mod and vendor/modules.txt to the installed Go toolchain version
|
||||||
sed -i 's/^go 1\.24\.[0-9]*/go 1.24/' core/go.mod
|
GO_INSTALLED=$$(go version | grep -oP 'go\K[0-9]+\.[0-9]+'); \
|
||||||
|
sed -i "s/^go [0-9]\+\.[0-9]\+\(\.[0-9]*\)\?$$/go $${GO_INSTALLED}/" core/go.mod; \
|
||||||
|
sed -i "s/^\(## explicit; go \)[0-9]\+\.[0-9]\+\(\.[0-9]*\)\?$$/\1$${GO_INSTALLED}/" core/vendor/modules.txt
|
||||||
|
|
||||||
# Build dms-cli from source using vendored dependencies
|
# Build dms-cli (single shell to preserve variables; arch: Debian amd64/arm64 -> Makefile amd64/arm64)
|
||||||
# Extract version info and build in single shell to preserve variables
|
|
||||||
# Architecture mapping: Debian amd64/arm64 -> Makefile amd64/arm64
|
|
||||||
VERSION="$(UPSTREAM_VERSION)"; \
|
VERSION="$(UPSTREAM_VERSION)"; \
|
||||||
COMMIT=$$(echo "$(UPSTREAM_VERSION)" | grep -oP '(?<=git)[0-9]+\.[a-f0-9]+' | cut -d. -f2 | head -c8 || echo "unknown"); \
|
COMMIT=$$(echo "$(UPSTREAM_VERSION)" | grep -oP '(?<=git)[0-9]+\.[a-f0-9]+' | cut -d. -f2 | head -c8 || echo "unknown"); \
|
||||||
if [ "$(DEB_HOST_ARCH)" = "amd64" ]; then \
|
if [ "$(DEB_HOST_ARCH)" = "amd64" ]; then \
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<service name="download_url">
|
<service name="download_url">
|
||||||
<param name="protocol">https</param>
|
<param name="protocol">https</param>
|
||||||
<param name="host">github.com</param>
|
<param name="host">github.com</param>
|
||||||
<param name="path">/AvengeMedia/DankMaterialShell/releases/download/v1.4.2/dms-qml.tar.gz</param>
|
<param name="path">/AvengeMedia/DankMaterialShell/releases/download/v1.4.3/dms-qml.tar.gz</param>
|
||||||
<param name="filename">dms-qml.tar.gz</param>
|
<param name="filename">dms-qml.tar.gz</param>
|
||||||
</service>
|
</service>
|
||||||
</services>
|
</services>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
dms-greeter (1.4.2db8) unstable; urgency=medium
|
dms-greeter (1.4.3db1) unstable; urgency=medium
|
||||||
|
|
||||||
* Initial Debian OBS package
|
* Update to v1.4.3 stable release
|
||||||
* Port from Ubuntu/Fedora packaging
|
|
||||||
|
|
||||||
-- Avenge Media <AvengeMedia.US@gmail.com> Sat, 21 Feb 2026 00:00:00 +0000
|
-- Avenge Media <AvengeMedia.US@gmail.com> Tue, 25 Feb 2026 02:40:00 +0000
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
%global debug_package %{nil}
|
%global debug_package %{nil}
|
||||||
%global version {{{ git_repo_version }}}
|
%global version {{{ git_repo_version }}}
|
||||||
%global pkg_summary DankMaterialShell - Material 3 inspired shell for Wayland compositors
|
%global pkg_summary DankMaterialShell - Material 3 inspired shell for Wayland compositors
|
||||||
|
%global go_toolchain_version 1.25.7
|
||||||
|
|
||||||
Name: dms
|
Name: dms
|
||||||
Epoch: 2
|
Epoch: 2
|
||||||
@@ -14,12 +15,12 @@ License: MIT
|
|||||||
URL: https://github.com/AvengeMedia/DankMaterialShell
|
URL: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
VCS: {{{ git_repo_vcs }}}
|
VCS: {{{ git_repo_vcs }}}
|
||||||
Source0: {{{ git_repo_pack }}}
|
Source0: {{{ git_repo_pack }}}
|
||||||
|
Source1: https://go.dev/dl/go%{go_toolchain_version}.linux-amd64.tar.gz
|
||||||
|
Source2: https://go.dev/dl/go%{go_toolchain_version}.linux-arm64.tar.gz
|
||||||
|
|
||||||
BuildRequires: git-core
|
BuildRequires: git-core
|
||||||
BuildRequires: gzip
|
BuildRequires: gzip
|
||||||
BuildRequires: golang >= 1.24
|
|
||||||
BuildRequires: make
|
BuildRequires: make
|
||||||
BuildRequires: wget
|
|
||||||
BuildRequires: systemd-rpm-macros
|
BuildRequires: systemd-rpm-macros
|
||||||
|
|
||||||
# Core requirements
|
# Core requirements
|
||||||
@@ -28,7 +29,7 @@ Requires: accountsservice
|
|||||||
Requires: dms-cli = %{epoch}:%{version}-%{release}
|
Requires: dms-cli = %{epoch}:%{version}-%{release}
|
||||||
Requires: dgop
|
Requires: dgop
|
||||||
|
|
||||||
# Core utilities (Highly recommended for DMS functionality)
|
# Core utilities (Recommended for DMS functionality)
|
||||||
Recommends: cava
|
Recommends: cava
|
||||||
Recommends: danksearch
|
Recommends: danksearch
|
||||||
Recommends: matugen
|
Recommends: matugen
|
||||||
@@ -66,6 +67,28 @@ Provides native DBus bindings, NetworkManager integration, and system utilities.
|
|||||||
VERSION="%{version}"
|
VERSION="%{version}"
|
||||||
COMMIT=$(echo "%{version}" | grep -oP '[a-f0-9]{7,}' | head -n1 || echo "unknown")
|
COMMIT=$(echo "%{version}" | grep -oP '[a-f0-9]{7,}' | head -n1 || echo "unknown")
|
||||||
|
|
||||||
|
# Use pinned bundled Go toolchain (deterministic across chroots)
|
||||||
|
case "%{_arch}" in
|
||||||
|
x86_64)
|
||||||
|
GO_TARBALL="%{_sourcedir}/go%{go_toolchain_version}.linux-amd64.tar.gz"
|
||||||
|
;;
|
||||||
|
aarch64)
|
||||||
|
GO_TARBALL="%{_sourcedir}/go%{go_toolchain_version}.linux-arm64.tar.gz"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported architecture for bundled Go: %{_arch}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
rm -rf .go
|
||||||
|
tar -xzf "$GO_TARBALL"
|
||||||
|
mv go .go
|
||||||
|
export GOROOT="$PWD/.go"
|
||||||
|
export PATH="$GOROOT/bin:$PATH"
|
||||||
|
export GOTOOLCHAIN=local
|
||||||
|
go version
|
||||||
|
|
||||||
cd core
|
cd core
|
||||||
make dist VERSION="$VERSION" COMMIT="$COMMIT"
|
make dist VERSION="$VERSION" COMMIT="$COMMIT"
|
||||||
|
|
||||||
|
|||||||
@@ -56,8 +56,10 @@ mkdir -p $HOME $GOCACHE $GOMODCACHE
|
|||||||
# OBS has no network access, so use local toolchain only
|
# OBS has no network access, so use local toolchain only
|
||||||
export GOTOOLCHAIN=local
|
export GOTOOLCHAIN=local
|
||||||
|
|
||||||
# Patch go.mod to use base Go version (e.g., go 1.24 instead of go 1.24.6)
|
# Pin go.mod and vendor/modules.txt to the installed Go toolchain version
|
||||||
sed -i 's/^go 1\.24\.[0-9]*/go 1.24/' core/go.mod
|
GO_INSTALLED=$(go version | grep -oP 'go\K[0-9]+\.[0-9]+')
|
||||||
|
sed -i "s/^go [0-9]\+\.[0-9]\+\(\.[0-9]*\)\?$/go ${GO_INSTALLED}/" core/go.mod
|
||||||
|
sed -i "s/^\(## explicit; go \)[0-9]\+\.[0-9]\+\(\.[0-9]*\)\?$/\1${GO_INSTALLED}/" core/vendor/modules.txt
|
||||||
|
|
||||||
# Extract version info for embedding in binary
|
# Extract version info for embedding in binary
|
||||||
VERSION="%{version}"
|
VERSION="%{version}"
|
||||||
|
|||||||
@@ -419,6 +419,9 @@ if [[ "$UPLOAD_OPENSUSE" == true ]] && [[ -f "distro/opensuse/$PACKAGE.spec" ]];
|
|||||||
sed -i "s/VERSION_PLACEHOLDER/${DMS_GREETER_BASE_VERSION}/g" "$WORK_DIR/$PACKAGE.spec"
|
sed -i "s/VERSION_PLACEHOLDER/${DMS_GREETER_BASE_VERSION}/g" "$WORK_DIR/$PACKAGE.spec"
|
||||||
sed -i "s/RELEASE_PLACEHOLDER/${DMS_GREETER_RELEASE}/g" "$WORK_DIR/$PACKAGE.spec"
|
sed -i "s/RELEASE_PLACEHOLDER/${DMS_GREETER_RELEASE}/g" "$WORK_DIR/$PACKAGE.spec"
|
||||||
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" "$WORK_DIR/$PACKAGE.spec"
|
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" "$WORK_DIR/$PACKAGE.spec"
|
||||||
|
# Explicitly set Version:/Release: in case the spec uses %{version} macro
|
||||||
|
sed -i "s/^Version:.*/Version: ${DMS_GREETER_BASE_VERSION}/" "$WORK_DIR/$PACKAGE.spec"
|
||||||
|
sed -i "s/^Release:.*/Release: ${DMS_GREETER_RELEASE}%{?dist}/" "$WORK_DIR/$PACKAGE.spec"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -f "$WORK_DIR/.osc/$PACKAGE.spec" ]]; then
|
if [[ -f "$WORK_DIR/.osc/$PACKAGE.spec" ]]; then
|
||||||
@@ -813,6 +816,9 @@ if [[ "$UPLOAD_DEBIAN" == true ]] && [[ -d "distro/debian/$PACKAGE/debian" ]]; t
|
|||||||
sed -i "s/VERSION_PLACEHOLDER/${DMS_GREETER_BASE_VERSION}/g" "$WORK_DIR/$PACKAGE.spec"
|
sed -i "s/VERSION_PLACEHOLDER/${DMS_GREETER_BASE_VERSION}/g" "$WORK_DIR/$PACKAGE.spec"
|
||||||
sed -i "s/RELEASE_PLACEHOLDER/${DMS_GREETER_RELEASE}/g" "$WORK_DIR/$PACKAGE.spec"
|
sed -i "s/RELEASE_PLACEHOLDER/${DMS_GREETER_RELEASE}/g" "$WORK_DIR/$PACKAGE.spec"
|
||||||
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" "$WORK_DIR/$PACKAGE.spec"
|
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" "$WORK_DIR/$PACKAGE.spec"
|
||||||
|
# Explicitly set Version:/Release: in case the spec uses %{version} macro
|
||||||
|
sed -i "s/^Version:.*/Version: ${DMS_GREETER_BASE_VERSION}/" "$WORK_DIR/$PACKAGE.spec"
|
||||||
|
sed -i "s/^Release:.*/Release: ${DMS_GREETER_RELEASE}%{?dist}/" "$WORK_DIR/$PACKAGE.spec"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -182,6 +182,7 @@
|
|||||||
with pkgs;
|
with pkgs;
|
||||||
[
|
[
|
||||||
go_1_25
|
go_1_25
|
||||||
|
go-mockery_2
|
||||||
gopls
|
gopls
|
||||||
delve
|
delve
|
||||||
go-tools
|
go-tools
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
Saffron Bloom
|
The Wolverine
|
||||||
|
|||||||
+31
-17
@@ -8,7 +8,9 @@ import Quickshell.Io
|
|||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property string _rawLocale: Qt.locale().name
|
property string _resolvedLocale: "en"
|
||||||
|
|
||||||
|
readonly property string _rawLocale: SessionData.locale === "" ? Qt.locale().name : SessionData.locale
|
||||||
readonly property string _lang: _rawLocale.split(/[_-]/)[0]
|
readonly property string _lang: _rawLocale.split(/[_-]/)[0]
|
||||||
readonly property var _candidates: {
|
readonly property var _candidates: {
|
||||||
const fullUnderscore = _rawLocale;
|
const fullUnderscore = _rawLocale;
|
||||||
@@ -21,7 +23,8 @@ Singleton {
|
|||||||
|
|
||||||
readonly property url translationsFolder: Qt.resolvedUrl("../translations/poexports")
|
readonly property url translationsFolder: Qt.resolvedUrl("../translations/poexports")
|
||||||
|
|
||||||
property string currentLocale: "en"
|
readonly property alias folder: dir.folder
|
||||||
|
property var presentLocales: ({ "en": Qt.locale("en") })
|
||||||
property var translations: ({})
|
property var translations: ({})
|
||||||
property bool translationsLoaded: false
|
property bool translationsLoaded: false
|
||||||
|
|
||||||
@@ -34,8 +37,10 @@ Singleton {
|
|||||||
showDirs: false
|
showDirs: false
|
||||||
showDotAndDotDot: false
|
showDotAndDotDot: false
|
||||||
|
|
||||||
onStatusChanged: if (status === FolderListModel.Ready)
|
onStatusChanged: if (status === FolderListModel.Ready) {
|
||||||
root._pickTranslation()
|
root._loadPresentLocales();
|
||||||
|
root._pickTranslation();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FileView {
|
FileView {
|
||||||
@@ -46,41 +51,51 @@ Singleton {
|
|||||||
try {
|
try {
|
||||||
root.translations = JSON.parse(text());
|
root.translations = JSON.parse(text());
|
||||||
root.translationsLoaded = true;
|
root.translationsLoaded = true;
|
||||||
console.info(`I18n: Loaded translations for '${root.currentLocale}' ` + `(${Object.keys(root.translations).length} contexts)`);
|
console.info(`I18n: Loaded translations for '${root._resolvedLocale}' (${Object.keys(root.translations).length} contexts)`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(`I18n: Error parsing '${root.currentLocale}':`, e, "- falling back to English");
|
console.warn(`I18n: Error parsing '${root._resolvedLocale}':`, e, "- falling back to English");
|
||||||
root._fallbackToEnglish();
|
root._fallbackToEnglish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoadFailed: error => {
|
onLoadFailed: error => {
|
||||||
console.warn(`I18n: Failed to load '${root.currentLocale}' (${error}), ` + "falling back to English");
|
console.warn(`I18n: Failed to load '${root._resolvedLocale}' (${error}), ` + "falling back to English");
|
||||||
root._fallbackToEnglish();
|
root._fallbackToEnglish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _pickTranslation() {
|
function locale() {
|
||||||
const present = new Set();
|
return presentLocales[_resolvedLocale] ?? presentLocales["en"];
|
||||||
|
}
|
||||||
|
|
||||||
|
function _loadPresentLocales() {
|
||||||
|
if (Object.keys(presentLocales).length > 1) {
|
||||||
|
return; // already loaded
|
||||||
|
}
|
||||||
for (let i = 0; i < dir.count; i++) {
|
for (let i = 0; i < dir.count; i++) {
|
||||||
const name = dir.get(i, "fileName"); // e.g. "zh_CN.json"
|
const name = dir.get(i, "fileName"); // e.g. "zh_CN.json"
|
||||||
if (name && name.endsWith(".json")) {
|
if (name && name.endsWith(".json")) {
|
||||||
present.add(name.slice(0, -5));
|
const shortName = name.slice(0, -5);
|
||||||
|
presentLocales[shortName] = Qt.locale(shortName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _pickTranslation() {
|
||||||
for (let i = 0; i < _candidates.length; i++) {
|
for (let i = 0; i < _candidates.length; i++) {
|
||||||
const cand = _candidates[i];
|
const cand = _candidates[i];
|
||||||
if (present.has(cand)) {
|
if (presentLocales[cand] === undefined) continue;
|
||||||
_useLocale(cand, dir.folder + "/" + cand + ".json");
|
_resolvedLocale = cand;
|
||||||
return;
|
useLocale(cand, cand.startsWith("en") ? "" : translationsFolder + "/" + cand + ".json");
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_resolvedLocale = "en";
|
||||||
_fallbackToEnglish();
|
_fallbackToEnglish();
|
||||||
}
|
}
|
||||||
|
|
||||||
function _useLocale(localeTag, fileUrl) {
|
function useLocale(localeTag, fileUrl) {
|
||||||
currentLocale = localeTag;
|
_resolvedLocale = localeTag || "en";
|
||||||
_selectedPath = fileUrl;
|
_selectedPath = fileUrl;
|
||||||
translationsLoaded = false;
|
translationsLoaded = false;
|
||||||
translations = ({});
|
translations = ({});
|
||||||
@@ -88,7 +103,6 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _fallbackToEnglish() {
|
function _fallbackToEnglish() {
|
||||||
currentLocale = "en";
|
|
||||||
_selectedPath = "";
|
_selectedPath = "";
|
||||||
translationsLoaded = false;
|
translationsLoaded = false;
|
||||||
translations = ({});
|
translations = ({});
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ Singleton {
|
|||||||
property bool _isReadOnly: false
|
property bool _isReadOnly: false
|
||||||
property bool _hasUnsavedChanges: false
|
property bool _hasUnsavedChanges: false
|
||||||
property var _loadedSessionSnapshot: null
|
property var _loadedSessionSnapshot: null
|
||||||
readonly property var _hooks: ({})
|
readonly property var _hooks: ({
|
||||||
|
"updateLocale": updateLocale
|
||||||
|
})
|
||||||
readonly property string _stateUrl: StandardPaths.writableLocation(StandardPaths.GenericStateLocation)
|
readonly property string _stateUrl: StandardPaths.writableLocation(StandardPaths.GenericStateLocation)
|
||||||
readonly property string _stateDir: Paths.strip(_stateUrl)
|
readonly property string _stateDir: Paths.strip(_stateUrl)
|
||||||
|
|
||||||
@@ -126,6 +128,8 @@ Singleton {
|
|||||||
property var hiddenOutputDeviceNames: []
|
property var hiddenOutputDeviceNames: []
|
||||||
property var hiddenInputDeviceNames: []
|
property var hiddenInputDeviceNames: []
|
||||||
|
|
||||||
|
property string locale: ""
|
||||||
|
|
||||||
property string launcherLastMode: "all"
|
property string launcherLastMode: "all"
|
||||||
property string appDrawerLastMode: "apps"
|
property string appDrawerLastMode: "apps"
|
||||||
property string niriOverviewLastMode: "apps"
|
property string niriOverviewLastMode: "apps"
|
||||||
@@ -1104,6 +1108,14 @@ Singleton {
|
|||||||
saveSettings();
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateLocale() {
|
||||||
|
if (!locale) {
|
||||||
|
I18n._pickTranslation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
I18n.useLocale(locale, locale.startsWith("en") ? "" : I18n.folder + "/" + locale + ".json");
|
||||||
|
}
|
||||||
|
|
||||||
function setLauncherLastMode(mode) {
|
function setLauncherLastMode(mode) {
|
||||||
launcherLastMode = mode;
|
launcherLastMode = mode;
|
||||||
saveSettings();
|
saveSettings();
|
||||||
|
|||||||
@@ -494,9 +494,15 @@ Singleton {
|
|||||||
property bool enableFprint: false
|
property bool enableFprint: false
|
||||||
property int maxFprintTries: 15
|
property int maxFprintTries: 15
|
||||||
property bool fprintdAvailable: false
|
property bool fprintdAvailable: false
|
||||||
|
property bool enableU2f: false
|
||||||
|
property string u2fMode: "or"
|
||||||
|
property bool u2fAvailable: false
|
||||||
property string lockScreenActiveMonitor: "all"
|
property string lockScreenActiveMonitor: "all"
|
||||||
property string lockScreenInactiveColor: "#000000"
|
property string lockScreenInactiveColor: "#000000"
|
||||||
property int lockScreenNotificationMode: 0
|
property int lockScreenNotificationMode: 0
|
||||||
|
property bool lockScreenVideoEnabled: false
|
||||||
|
property string lockScreenVideoPath: ""
|
||||||
|
property bool lockScreenVideoCycling: false
|
||||||
property bool hideBrightnessSlider: false
|
property bool hideBrightnessSlider: false
|
||||||
|
|
||||||
property int notificationTimeoutLow: 5000
|
property int notificationTimeoutLow: 5000
|
||||||
@@ -982,6 +988,7 @@ Singleton {
|
|||||||
loadSettings();
|
loadSettings();
|
||||||
initializeListModels();
|
initializeListModels();
|
||||||
Processes.detectFprintd();
|
Processes.detectFprintd();
|
||||||
|
Processes.detectU2f();
|
||||||
Processes.checkPluginSettings();
|
Processes.checkPluginSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1129,7 +1136,7 @@ Singleton {
|
|||||||
"updateCompositorLayout": updateCompositorLayout,
|
"updateCompositorLayout": updateCompositorLayout,
|
||||||
"applyStoredIconTheme": applyStoredIconTheme,
|
"applyStoredIconTheme": applyStoredIconTheme,
|
||||||
"updateBarConfigs": updateBarConfigs,
|
"updateBarConfigs": updateBarConfigs,
|
||||||
"updateCompositorCursor": updateCompositorCursor
|
"updateCompositorCursor": updateCompositorCursor,
|
||||||
})
|
})
|
||||||
|
|
||||||
function set(key, value) {
|
function set(key, value) {
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ Singleton {
|
|||||||
fprintdDetectionProcess.running = true;
|
fprintdDetectionProcess.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function detectU2f() {
|
||||||
|
u2fDetectionProcess.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
function checkPluginSettings() {
|
function checkPluginSettings() {
|
||||||
pluginSettingsCheckProcess.running = true;
|
pluginSettingsCheckProcess.running = true;
|
||||||
}
|
}
|
||||||
@@ -57,6 +61,16 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property var u2fDetectionProcess: Process {
|
||||||
|
command: ["sh", "-c", "(test -f /usr/lib/security/pam_u2f.so || test -f /usr/lib64/security/pam_u2f.so) && (test -f /etc/pam.d/dankshell-u2f || test -f \"$HOME/.config/Yubico/u2f_keys\")"]
|
||||||
|
running: false
|
||||||
|
onExited: function (exitCode) {
|
||||||
|
if (!settingsRoot)
|
||||||
|
return;
|
||||||
|
settingsRoot.u2fAvailable = (exitCode === 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
property var pluginSettingsCheckProcess: Process {
|
property var pluginSettingsCheckProcess: Process {
|
||||||
command: ["test", "-f", settingsRoot?.pluginSettingsPath || ""]
|
command: ["test", "-f", settingsRoot?.pluginSettingsPath || ""]
|
||||||
running: false
|
running: false
|
||||||
|
|||||||
@@ -79,6 +79,8 @@ var SPEC = {
|
|||||||
hiddenOutputDeviceNames: { def: [] },
|
hiddenOutputDeviceNames: { def: [] },
|
||||||
hiddenInputDeviceNames: { def: [] },
|
hiddenInputDeviceNames: { def: [] },
|
||||||
|
|
||||||
|
locale: { def: "", onChange: "updateLocale" },
|
||||||
|
|
||||||
launcherLastMode: { def: "all" },
|
launcherLastMode: { def: "all" },
|
||||||
appDrawerLastMode: { def: "apps" },
|
appDrawerLastMode: { def: "apps" },
|
||||||
niriOverviewLastMode: { def: "apps" }
|
niriOverviewLastMode: { def: "apps" }
|
||||||
|
|||||||
@@ -317,9 +317,15 @@ var SPEC = {
|
|||||||
enableFprint: { def: false },
|
enableFprint: { def: false },
|
||||||
maxFprintTries: { def: 15 },
|
maxFprintTries: { def: 15 },
|
||||||
fprintdAvailable: { def: false, persist: false },
|
fprintdAvailable: { def: false, persist: false },
|
||||||
|
enableU2f: { def: false },
|
||||||
|
u2fMode: { def: "or" },
|
||||||
|
u2fAvailable: { def: false, persist: false },
|
||||||
lockScreenActiveMonitor: { def: "all" },
|
lockScreenActiveMonitor: { def: "all" },
|
||||||
lockScreenInactiveColor: { def: "#000000" },
|
lockScreenInactiveColor: { def: "#000000" },
|
||||||
lockScreenNotificationMode: { def: 0 },
|
lockScreenNotificationMode: { def: 0 },
|
||||||
|
lockScreenVideoEnabled: { def: false },
|
||||||
|
lockScreenVideoPath: { def: "" },
|
||||||
|
lockScreenVideoCycling: { def: false },
|
||||||
hideBrightnessSlider: { def: false },
|
hideBrightnessSlider: { def: false },
|
||||||
|
|
||||||
notificationTimeoutLow: { def: 5000 },
|
notificationTimeoutLow: { def: 5000 },
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ function parse(root, jsonObj) {
|
|||||||
|
|
||||||
for (var k in SPEC) {
|
for (var k in SPEC) {
|
||||||
if (k === "pluginSettings") continue;
|
if (k === "pluginSettings") continue;
|
||||||
|
// Runtime-only keys are never in the JSON; resetting them here
|
||||||
|
// would wipe values set by detection processes on every reload.
|
||||||
|
if (SPEC[k].persist === false) continue;
|
||||||
if (!(k in jsonObj)) {
|
if (!(k in jsonObj)) {
|
||||||
root[k] = SPEC[k].def;
|
root[k] = SPEC[k].def;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -365,6 +365,23 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: wifiQRCodeModalLoader
|
||||||
|
active: false
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.wifiQRCodeModalLoader = wifiQRCodeModalLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
WifiQRCodeModal {
|
||||||
|
id: wifiQRCodeModalItem
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.wifiQRCodeModal = wifiQRCodeModalItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LazyLoader {
|
LazyLoader {
|
||||||
id: polkitAuthModalLoader
|
id: polkitAuthModalLoader
|
||||||
active: false
|
active: false
|
||||||
|
|||||||
@@ -162,6 +162,11 @@ Item {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
property string fileSearchType: "all"
|
||||||
|
property string fileSearchExt: ""
|
||||||
|
property string fileSearchFolder: ""
|
||||||
|
property string fileSearchSort: "score"
|
||||||
|
|
||||||
property string pluginFilter: ""
|
property string pluginFilter: ""
|
||||||
property string activePluginName: ""
|
property string activePluginName: ""
|
||||||
property var activePluginCategories: []
|
property var activePluginCategories: []
|
||||||
@@ -346,6 +351,10 @@ Item {
|
|||||||
previousSearchMode = "all";
|
previousSearchMode = "all";
|
||||||
autoSwitchedToFiles = false;
|
autoSwitchedToFiles = false;
|
||||||
isFileSearching = false;
|
isFileSearching = false;
|
||||||
|
fileSearchType = "all";
|
||||||
|
fileSearchExt = "";
|
||||||
|
fileSearchFolder = "";
|
||||||
|
fileSearchSort = "score";
|
||||||
sections = [];
|
sections = [];
|
||||||
flatModel = [];
|
flatModel = [];
|
||||||
selectedFlatIndex = 0;
|
selectedFlatIndex = 0;
|
||||||
@@ -399,6 +408,34 @@ Item {
|
|||||||
performSearch();
|
performSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setFileSearchType(type) {
|
||||||
|
if (fileSearchType === type)
|
||||||
|
return;
|
||||||
|
fileSearchType = type;
|
||||||
|
performFileSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFileSearchExt(ext) {
|
||||||
|
if (fileSearchExt === ext)
|
||||||
|
return;
|
||||||
|
fileSearchExt = ext;
|
||||||
|
performFileSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFileSearchFolder(folder) {
|
||||||
|
if (fileSearchFolder === folder)
|
||||||
|
return;
|
||||||
|
fileSearchFolder = folder;
|
||||||
|
performFileSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFileSearchSort(sort) {
|
||||||
|
if (fileSearchSort === sort)
|
||||||
|
return;
|
||||||
|
fileSearchSort = sort;
|
||||||
|
performFileSearch();
|
||||||
|
}
|
||||||
|
|
||||||
function clearPluginFilter() {
|
function clearPluginFilter() {
|
||||||
if (pluginFilter) {
|
if (pluginFilter) {
|
||||||
pluginFilter = "";
|
pluginFilter = "";
|
||||||
@@ -827,10 +864,20 @@ Item {
|
|||||||
var params = {
|
var params = {
|
||||||
limit: 20,
|
limit: 20,
|
||||||
fuzzy: true,
|
fuzzy: true,
|
||||||
sort: "score",
|
sort: fileSearchSort || "score",
|
||||||
desc: true
|
desc: true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (DSearchService.supportsTypeFilter) {
|
||||||
|
params.type = (fileSearchType && fileSearchType !== "all") ? fileSearchType : "all";
|
||||||
|
}
|
||||||
|
if (fileSearchExt) {
|
||||||
|
params.ext = fileSearchExt;
|
||||||
|
}
|
||||||
|
if (fileSearchFolder) {
|
||||||
|
params.folder = fileSearchFolder;
|
||||||
|
}
|
||||||
|
|
||||||
DSearchService.search(fileQuery, params, function (response) {
|
DSearchService.search(fileQuery, params, function (response) {
|
||||||
isFileSearching = false;
|
isFileSearching = false;
|
||||||
if (response.error)
|
if (response.error)
|
||||||
@@ -840,34 +887,73 @@ Item {
|
|||||||
|
|
||||||
for (var i = 0; i < hits.length; i++) {
|
for (var i = 0; i < hits.length; i++) {
|
||||||
var hit = hits[i];
|
var hit = hits[i];
|
||||||
|
var docTypes = hit.locations?.doc_type;
|
||||||
|
var isDir = docTypes ? !!docTypes["dir"] : false;
|
||||||
fileItems.push(transformFileResult({
|
fileItems.push(transformFileResult({
|
||||||
path: hit.id || "",
|
path: hit.id || "",
|
||||||
score: hit.score || 0
|
score: hit.score || 0,
|
||||||
|
is_dir: isDir
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileSection = {
|
var fileSections = [];
|
||||||
id: "files",
|
var showType = fileSearchType || "all";
|
||||||
title: I18n.tr("Files"),
|
|
||||||
icon: "folder",
|
if (showType === "all" && DSearchService.supportsTypeFilter) {
|
||||||
priority: 4,
|
var onlyFiles = [];
|
||||||
items: fileItems,
|
var onlyDirs = [];
|
||||||
collapsed: collapsedSections["files"] || false,
|
for (var j = 0; j < fileItems.length; j++) {
|
||||||
flatStartIndex: 0
|
if (fileItems[j].data?.is_dir)
|
||||||
};
|
onlyDirs.push(fileItems[j]);
|
||||||
|
else
|
||||||
|
onlyFiles.push(fileItems[j]);
|
||||||
|
}
|
||||||
|
if (onlyFiles.length > 0) {
|
||||||
|
fileSections.push({
|
||||||
|
id: "files",
|
||||||
|
title: I18n.tr("Files"),
|
||||||
|
icon: "insert_drive_file",
|
||||||
|
priority: 4,
|
||||||
|
items: onlyFiles,
|
||||||
|
collapsed: collapsedSections["files"] || false,
|
||||||
|
flatStartIndex: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (onlyDirs.length > 0) {
|
||||||
|
fileSections.push({
|
||||||
|
id: "folders",
|
||||||
|
title: I18n.tr("Folders"),
|
||||||
|
icon: "folder",
|
||||||
|
priority: 4.1,
|
||||||
|
items: onlyDirs,
|
||||||
|
collapsed: collapsedSections["folders"] || false,
|
||||||
|
flatStartIndex: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var filesIcon = showType === "dir" ? "folder" : showType === "file" ? "insert_drive_file" : "folder";
|
||||||
|
var filesTitle = showType === "dir" ? I18n.tr("Folders") : I18n.tr("Files");
|
||||||
|
if (fileItems.length > 0) {
|
||||||
|
fileSections.push({
|
||||||
|
id: "files",
|
||||||
|
title: filesTitle,
|
||||||
|
icon: filesIcon,
|
||||||
|
priority: 4,
|
||||||
|
items: fileItems,
|
||||||
|
collapsed: collapsedSections["files"] || false,
|
||||||
|
flatStartIndex: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var newSections;
|
var newSections;
|
||||||
if (searchMode === "files") {
|
if (searchMode === "files") {
|
||||||
newSections = fileItems.length > 0 ? [fileSection] : [];
|
newSections = fileSections;
|
||||||
} else {
|
} else {
|
||||||
var existingNonFile = sections.filter(function (s) {
|
var existingNonFile = sections.filter(function (s) {
|
||||||
return s.id !== "files";
|
return s.id !== "files" && s.id !== "folders";
|
||||||
});
|
});
|
||||||
if (fileItems.length > 0) {
|
newSections = existingNonFile.concat(fileSections);
|
||||||
newSections = existingNonFile.concat([fileSection]);
|
|
||||||
} else {
|
|
||||||
newSections = existingNonFile;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
newSections.sort(function (a, b) {
|
newSections.sort(function (a, b) {
|
||||||
return a.priority - b.priority;
|
return a.priority - b.priority;
|
||||||
@@ -913,7 +999,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function transformFileResult(file) {
|
function transformFileResult(file) {
|
||||||
return Transform.transformFileResult(file, I18n.tr("Open"), I18n.tr("Open folder"), I18n.tr("Copy path"));
|
return Transform.transformFileResult(file, I18n.tr("Open"), I18n.tr("Open folder"), I18n.tr("Copy path"), I18n.tr("Open in terminal"));
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectTrigger(query) {
|
function detectTrigger(query) {
|
||||||
@@ -1581,6 +1667,9 @@ Item {
|
|||||||
case "copy_path":
|
case "copy_path":
|
||||||
copyToClipboard(item.data.path);
|
copyToClipboard(item.data.path);
|
||||||
break;
|
break;
|
||||||
|
case "open_terminal":
|
||||||
|
openTerminal(item.data.path);
|
||||||
|
break;
|
||||||
case "copy":
|
case "copy":
|
||||||
copyToClipboard(item.name);
|
copyToClipboard(item.name);
|
||||||
break;
|
break;
|
||||||
@@ -1662,6 +1751,16 @@ Item {
|
|||||||
Qt.openUrlExternally("file://" + folder);
|
Qt.openUrlExternally("file://" + folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openTerminal(path) {
|
||||||
|
if (!path)
|
||||||
|
return;
|
||||||
|
var terminal = Quickshell.env("TERMINAL") || "xterm";
|
||||||
|
Quickshell.execDetached({
|
||||||
|
command: [terminal],
|
||||||
|
workingDirectory: path
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function copyToClipboard(text) {
|
function copyToClipboard(text) {
|
||||||
if (!text)
|
if (!text)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -107,6 +107,10 @@ Item {
|
|||||||
spotlightContent.controller.activePluginId = "";
|
spotlightContent.controller.activePluginId = "";
|
||||||
spotlightContent.controller.activePluginName = "";
|
spotlightContent.controller.activePluginName = "";
|
||||||
spotlightContent.controller.pluginFilter = "";
|
spotlightContent.controller.pluginFilter = "";
|
||||||
|
spotlightContent.controller.fileSearchType = "all";
|
||||||
|
spotlightContent.controller.fileSearchExt = "";
|
||||||
|
spotlightContent.controller.fileSearchFolder = "";
|
||||||
|
spotlightContent.controller.fileSearchSort = "score";
|
||||||
spotlightContent.controller.collapsedSections = {};
|
spotlightContent.controller.collapsedSections = {};
|
||||||
spotlightContent.controller.selectedFlatIndex = 0;
|
spotlightContent.controller.selectedFlatIndex = 0;
|
||||||
spotlightContent.controller.selectedItem = null;
|
spotlightContent.controller.selectedItem = null;
|
||||||
|
|||||||
@@ -116,31 +116,43 @@ function transformBuiltInLauncherItem(item, pluginId, openLabel) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function transformFileResult(file, openLabel, openFolderLabel, copyPathLabel) {
|
function transformFileResult(file, openLabel, openFolderLabel, copyPathLabel, openTerminalLabel) {
|
||||||
var filename = file.path ? file.path.split("/").pop() : "";
|
var filename = file.path ? file.path.split("/").pop() : "";
|
||||||
var dirname = file.path ? file.path.substring(0, file.path.lastIndexOf("/")) : "";
|
var dirname = file.path ? file.path.substring(0, file.path.lastIndexOf("/")) : "";
|
||||||
|
var isDir = file.is_dir || false;
|
||||||
|
|
||||||
|
var actions = [];
|
||||||
|
if (isDir) {
|
||||||
|
if (openTerminalLabel) {
|
||||||
|
actions.push({
|
||||||
|
name: openTerminalLabel,
|
||||||
|
icon: "terminal",
|
||||||
|
action: "open_terminal"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
actions.push({
|
||||||
|
name: openFolderLabel,
|
||||||
|
icon: "folder_open",
|
||||||
|
action: "open_folder"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
actions.push({
|
||||||
|
name: copyPathLabel,
|
||||||
|
icon: "content_copy",
|
||||||
|
action: "copy_path"
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: file.path || "",
|
id: file.path || "",
|
||||||
type: "file",
|
type: "file",
|
||||||
name: filename,
|
name: filename,
|
||||||
subtitle: dirname,
|
subtitle: dirname,
|
||||||
icon: Utils.getFileIcon(filename),
|
icon: isDir ? "folder" : Utils.getFileIcon(filename),
|
||||||
iconType: "material",
|
iconType: "material",
|
||||||
section: "files",
|
section: "files",
|
||||||
data: file,
|
data: file,
|
||||||
actions: [
|
actions: actions,
|
||||||
{
|
|
||||||
name: openFolderLabel,
|
|
||||||
icon: "folder_open",
|
|
||||||
action: "open_folder"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: copyPathLabel,
|
|
||||||
icon: "content_copy",
|
|
||||||
action: "copy_path"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
primaryAction: {
|
primaryAction: {
|
||||||
name: openLabel,
|
name: openLabel,
|
||||||
icon: "open_in_new",
|
icon: "open_in_new",
|
||||||
|
|||||||
@@ -549,8 +549,151 @@ FocusScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
id: fileFilterRow
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height - searchField.height - categoryRow.height - actionPanel.height - Theme.spacingXS * (categoryRow.visible ? 3 : 2)
|
height: showFileFilters ? fileFilterContent.height : 0
|
||||||
|
visible: showFileFilters
|
||||||
|
|
||||||
|
readonly property bool showFileFilters: controller.searchMode === "files"
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: fileFilterContent
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: typeChips
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 2
|
||||||
|
visible: DSearchService.supportsTypeFilter
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: [
|
||||||
|
{
|
||||||
|
id: "all",
|
||||||
|
label: I18n.tr("All"),
|
||||||
|
icon: "search"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "file",
|
||||||
|
label: I18n.tr("Files"),
|
||||||
|
icon: "insert_drive_file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "dir",
|
||||||
|
label: I18n.tr("Folders"),
|
||||||
|
icon: "folder"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
width: chipContent.width + Theme.spacingM * 2
|
||||||
|
height: sortDropdown.height
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: controller.fileSearchType === modelData.id || chipArea.containsMouse ? Theme.primaryContainer : "transparent"
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: chipContent
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
name: modelData.icon
|
||||||
|
size: 14
|
||||||
|
color: controller.fileSearchType === modelData.id ? Theme.primary : Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: modelData.label
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: controller.fileSearchType === modelData.id ? Theme.primary : Theme.surfaceText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: chipArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: controller.setFileSearchType(modelData.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 1
|
||||||
|
height: 20
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: Theme.outlineMedium
|
||||||
|
visible: typeChips.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
DankDropdown {
|
||||||
|
id: sortDropdown
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: Math.min(130, parent.width / 3)
|
||||||
|
compactMode: true
|
||||||
|
dropdownWidth: 130
|
||||||
|
popupWidth: 150
|
||||||
|
maxPopupHeight: 200
|
||||||
|
currentValue: {
|
||||||
|
switch (controller.fileSearchSort) {
|
||||||
|
case "score":
|
||||||
|
return I18n.tr("Score");
|
||||||
|
case "name":
|
||||||
|
return I18n.tr("Name");
|
||||||
|
case "modified":
|
||||||
|
return I18n.tr("Modified");
|
||||||
|
case "size":
|
||||||
|
return I18n.tr("Size");
|
||||||
|
default:
|
||||||
|
return I18n.tr("Score");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
options: [I18n.tr("Score"), I18n.tr("Name"), I18n.tr("Modified"), I18n.tr("Size")]
|
||||||
|
|
||||||
|
onValueChanged: value => {
|
||||||
|
var sortMap = {};
|
||||||
|
sortMap[I18n.tr("Score")] = "score";
|
||||||
|
sortMap[I18n.tr("Name")] = "name";
|
||||||
|
sortMap[I18n.tr("Modified")] = "modified";
|
||||||
|
sortMap[I18n.tr("Size")] = "size";
|
||||||
|
controller.setFileSearchSort(sortMap[value] || "score");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: extFilterField
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: Math.min(100, parent.width / 4)
|
||||||
|
height: sortDropdown.height
|
||||||
|
placeholderText: I18n.tr("ext")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
showClearButton: text.length > 0
|
||||||
|
|
||||||
|
onTextChanged: {
|
||||||
|
controller.setFileSearchExt(text.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height - searchField.height - categoryRow.height - fileFilterRow.height - actionPanel.height - Theme.spacingXS * ((categoryRow.visible ? 1 : 0) + (fileFilterRow.visible ? 1 : 0) + 2)
|
||||||
opacity: root.parentModal?.isClosing ? 0 : 1
|
opacity: root.parentModal?.isClosing ? 0 : 1
|
||||||
|
|
||||||
ResultsList {
|
ResultsList {
|
||||||
@@ -586,6 +729,9 @@ FocusScope {
|
|||||||
function onSearchQueryRequested(query) {
|
function onSearchQueryRequested(query) {
|
||||||
searchField.text = query;
|
searchField.text = query;
|
||||||
}
|
}
|
||||||
|
function onModeChanged() {
|
||||||
|
extFilterField.text = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FocusScope {
|
FocusScope {
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ Rectangle {
|
|||||||
font.family: Theme.fontFamily
|
font.family: Theme.fontFamily
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
|
clip: true
|
||||||
visible: (root.item?.subtitle ?? "").length > 0
|
visible: (root.item?.subtitle ?? "").length > 0
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
}
|
}
|
||||||
@@ -181,7 +182,7 @@ Rectangle {
|
|||||||
case "plugin":
|
case "plugin":
|
||||||
return I18n.tr("Plugin");
|
return I18n.tr("Plugin");
|
||||||
case "file":
|
case "file":
|
||||||
return I18n.tr("File");
|
return root.item.data?.is_dir ? I18n.tr("Folder") : I18n.tr("File");
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -435,7 +435,15 @@ Item {
|
|||||||
var mode = root.controller?.searchMode ?? "all";
|
var mode = root.controller?.searchMode ?? "all";
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case "files":
|
case "files":
|
||||||
return "folder_open";
|
var fileType = root.controller?.fileSearchType ?? "all";
|
||||||
|
switch (fileType) {
|
||||||
|
case "dir":
|
||||||
|
return "folder_open";
|
||||||
|
case "file":
|
||||||
|
return "insert_drive_file";
|
||||||
|
default:
|
||||||
|
return "folder_open";
|
||||||
|
}
|
||||||
case "plugins":
|
case "plugins":
|
||||||
return "extension";
|
return "extension";
|
||||||
case "apps":
|
case "apps":
|
||||||
@@ -465,7 +473,15 @@ Item {
|
|||||||
return I18n.tr("Type to search files");
|
return I18n.tr("Type to search files");
|
||||||
if (root.controller.searchQuery.length < 2)
|
if (root.controller.searchQuery.length < 2)
|
||||||
return I18n.tr("Type at least 2 characters");
|
return I18n.tr("Type at least 2 characters");
|
||||||
return I18n.tr("No files found");
|
var fileType = root.controller?.fileSearchType ?? "all";
|
||||||
|
switch (fileType) {
|
||||||
|
case "dir":
|
||||||
|
return I18n.tr("No folders found");
|
||||||
|
case "file":
|
||||||
|
return I18n.tr("No files found");
|
||||||
|
default:
|
||||||
|
return I18n.tr("No results found");
|
||||||
|
}
|
||||||
case "plugins":
|
case "plugins":
|
||||||
return hasQuery ? I18n.tr("No plugin results") : I18n.tr("Browse or search plugins");
|
return hasQuery ? I18n.tr("No plugin results") : I18n.tr("Browse or search plugins");
|
||||||
case "apps":
|
case "apps":
|
||||||
|
|||||||
@@ -225,7 +225,13 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: root.errorCount > 0 ? I18n.tr("%1 issue(s) found", "greeter doctor page error count").arg(root.errorCount) : I18n.tr("All checks passed", "greeter doctor page success")
|
text: {
|
||||||
|
if (root.errorCount === 0)
|
||||||
|
return I18n.tr("All checks passed", "greeter doctor page success");
|
||||||
|
return root.errorCount === 1
|
||||||
|
? I18n.tr("%1 issue found", "greeter doctor page error count").arg(root.errorCount)
|
||||||
|
: I18n.tr("%1 issues found", "greeter doctor page error count").arg(root.errorCount);
|
||||||
|
}
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: root.errorCount > 0 ? Theme.error : Theme.surfaceVariantText
|
color: root.errorCount > 0 ? Theme.error : Theme.surfaceVariantText
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -470,7 +470,22 @@ FocusScope {
|
|||||||
|
|
||||||
onActiveChanged: {
|
onActiveChanged: {
|
||||||
if (active && item)
|
if (active && item)
|
||||||
Qt.callLater(() => item.forceActiveFocus());
|
Qt.callLater(() => item.forceActiveFocus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: localeLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
active: root.currentIndex === 30
|
||||||
|
visible: active
|
||||||
|
focus: active
|
||||||
|
|
||||||
|
sourceComponent: LocaleTab {}
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
if (active && item)
|
||||||
|
Qt.callLater(() => item.forceActiveFocus());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -246,6 +246,12 @@ Rectangle {
|
|||||||
"icon": "headphones",
|
"icon": "headphones",
|
||||||
"tabIndex": 29
|
"tabIndex": 29
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "locale",
|
||||||
|
"text": I18n.tr("Locale"),
|
||||||
|
"icon": "language",
|
||||||
|
"tabIndex": 30
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "clipboard",
|
"id": "clipboard",
|
||||||
"text": I18n.tr("Clipboard"),
|
"text": I18n.tr("Clipboard"),
|
||||||
|
|||||||
@@ -0,0 +1,170 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Effects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import qs.Modals.Common
|
||||||
|
import qs.Modals.FileBrowser
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
DankModal {
|
||||||
|
id: root
|
||||||
|
visible: false
|
||||||
|
layerNamespace: "dms:wifi-qrcode"
|
||||||
|
|
||||||
|
property bool disablePopupTransparency: true
|
||||||
|
property string wifiSSID: ""
|
||||||
|
property string themedQrCodePath: ""
|
||||||
|
property string normalQrCodePath: ""
|
||||||
|
modalWidth: 420
|
||||||
|
modalHeight: 480
|
||||||
|
onBackgroundClicked: hide()
|
||||||
|
onOpened: {
|
||||||
|
Qt.callLater(() => {
|
||||||
|
modalFocusScope.forceActiveFocus();
|
||||||
|
contentLoader.item.wifiSSID = wifiSSID;
|
||||||
|
contentLoader.item.themedQrCodePath = themedQrCodePath;
|
||||||
|
contentLoader.item.saveBrowserLoader = saveBrowserLoader;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function show(ssid) {
|
||||||
|
wifiSSID = ssid;
|
||||||
|
fetchNetworkQRCode(ssid);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
if (themedQrCodePath !== "") {
|
||||||
|
deleteQRCodeFile(themedQrCodePath);
|
||||||
|
}
|
||||||
|
if (normalQrCodePath !== "") {
|
||||||
|
deleteQRCodeFile(normalQrCodePath);
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchNetworkQRCode(ssid) {
|
||||||
|
// TODO: Add loading UI?
|
||||||
|
|
||||||
|
DMSService.sendRequest("network.qrcode", {
|
||||||
|
ssid: ssid
|
||||||
|
}, response => {
|
||||||
|
if (response.error) {
|
||||||
|
ToastService.showError("Failed to fetch network QR code: ", JSON.stringify(response.error));
|
||||||
|
} else if (response.result) {
|
||||||
|
themedQrCodePath = response.result[0];
|
||||||
|
normalQrCodePath = response.result[1];
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteQRCodeFile(path) {
|
||||||
|
DMSService.sendRequest("network.delete-qrcode", {
|
||||||
|
path: path
|
||||||
|
}, response => {
|
||||||
|
if (response.error) {
|
||||||
|
ToastService.showError(`Failed to remove QR code at ${path}: `, JSON.stringify(response.error));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: saveBrowserLoader
|
||||||
|
active: false
|
||||||
|
|
||||||
|
FileBrowserSurfaceModal {
|
||||||
|
id: saveBrowser
|
||||||
|
|
||||||
|
browserTitle: I18n.tr("Save QR Code")
|
||||||
|
browserIcon: "qr_code"
|
||||||
|
browserType: "default"
|
||||||
|
fileExtensions: ["*.png"]
|
||||||
|
allowStacking: true
|
||||||
|
saveMode: true
|
||||||
|
defaultFileName: `${root.wifiSSID ?? "wifi-qrcode"}.png`
|
||||||
|
onFileSelected: path => {
|
||||||
|
const cleanPath = decodeURI(path.toString().replace(/^file:\/\//, ''));
|
||||||
|
const fileName = cleanPath.split('/').pop();
|
||||||
|
const fileUrl = "file://" + cleanPath;
|
||||||
|
|
||||||
|
copyQrCodeProcess.exec(["cp", root.normalQrCodePath, cleanPath, "-f"])
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: copyQrCodeProcess
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
saveBrowser.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
content: Component {
|
||||||
|
Item {
|
||||||
|
id: theItem
|
||||||
|
property alias themedQrCodePath: qrCodeImg.source
|
||||||
|
property var saveBrowserLoader: null
|
||||||
|
property string wifiSSID: ""
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingL
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: modalTitle
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("WiFi QR code for ") + theItem.wifiSSID
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Bold
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
iconName: "save"
|
||||||
|
iconSize: Theme.iconSize - 4
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
onClicked: {
|
||||||
|
saveBrowserLoader.active = true;
|
||||||
|
if (saveBrowserLoader.item) {
|
||||||
|
saveBrowserLoader.item.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
iconName: "close"
|
||||||
|
iconSize: Theme.iconSize - 4
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
onClicked: root.hide()
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: qrCodeImg
|
||||||
|
height: parent.height - parent.spacing - modalTitle.height
|
||||||
|
width: height
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
source: qrCodeImg
|
||||||
|
anchors.fill: source
|
||||||
|
colorization: 1.0
|
||||||
|
colorizationColor: Theme.primary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -271,8 +271,8 @@ Item {
|
|||||||
|
|
||||||
text: {
|
text: {
|
||||||
if (SettingsData.clockDateFormat && SettingsData.clockDateFormat.length > 0)
|
if (SettingsData.clockDateFormat && SettingsData.clockDateFormat.length > 0)
|
||||||
return systemClock.date?.toLocaleDateString(Qt.locale(), SettingsData.clockDateFormat) ?? "";
|
return systemClock.date?.toLocaleDateString(I18n.locale(), SettingsData.clockDateFormat) ?? "";
|
||||||
return systemClock.date?.toLocaleDateString(Qt.locale(), "ddd, MMM d") ?? "";
|
return systemClock.date?.toLocaleDateString(I18n.locale(), "ddd, MMM d") ?? "";
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: root.accentColor
|
color: root.accentColor
|
||||||
@@ -324,8 +324,8 @@ Item {
|
|||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
text: {
|
text: {
|
||||||
if (SettingsData.clockDateFormat && SettingsData.clockDateFormat.length > 0)
|
if (SettingsData.clockDateFormat && SettingsData.clockDateFormat.length > 0)
|
||||||
return systemClock.date?.toLocaleDateString(Qt.locale(), SettingsData.clockDateFormat) ?? "";
|
return systemClock.date?.toLocaleDateString(I18n.locale(), SettingsData.clockDateFormat) ?? "";
|
||||||
return systemClock.date?.toLocaleDateString(Qt.locale(), "ddd, MMM d") ?? "";
|
return systemClock.date?.toLocaleDateString(I18n.locale(), "ddd, MMM d") ?? "";
|
||||||
}
|
}
|
||||||
font.pixelSize: digitalRoot.smallSize
|
font.pixelSize: digitalRoot.smallSize
|
||||||
color: Theme.withAlpha(root.accentColor, 0.7)
|
color: Theme.withAlpha(root.accentColor, 0.7)
|
||||||
@@ -528,7 +528,7 @@ Item {
|
|||||||
StyledText {
|
StyledText {
|
||||||
visible: stackedRoot.hasDate
|
visible: stackedRoot.hasDate
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
text: systemClock.date?.toLocaleDateString(Qt.locale(), "MMM dd") ?? ""
|
text: systemClock.date?.toLocaleDateString(I18n.locale(), "MMM dd") ?? ""
|
||||||
font.pixelSize: stackedRoot.smallSize * 0.7
|
font.pixelSize: stackedRoot.smallSize * 0.7
|
||||||
color: Theme.withAlpha(root.accentColor, 0.7)
|
color: Theme.withAlpha(root.accentColor, 0.7)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -651,6 +651,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
id: pinButton
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: optionsButton.width + Theme.spacingM + Theme.spacingS
|
anchors.rightMargin: optionsButton.width + Theme.spacingM + Theme.spacingS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -711,6 +712,19 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
id: qrCodeButton
|
||||||
|
visible: modelData.secured && modelData.saved
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: optionsButton.width + pinWifiRow.width + 3 * Theme.spacingM + Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
iconName: "qr_code"
|
||||||
|
buttonSize: 28
|
||||||
|
onClicked: {
|
||||||
|
PopoutService.showWifiQRCodeModal(modelData.ssid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DankRipple {
|
DankRipple {
|
||||||
id: wifiRipple
|
id: wifiRipple
|
||||||
cornerRadius: parent.radius
|
cornerRadius: parent.radius
|
||||||
@@ -719,7 +733,7 @@ Rectangle {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
id: networkMouseArea
|
id: networkMouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.rightMargin: optionsButton.width + Theme.spacingM + Theme.spacingS + pinWifiRow.width + Theme.spacingS * 4
|
anchors.rightMargin: optionsButton.width + pinWifiRow.width + (qrCodeButton.visible ? qrCodeButton.width : 0) + Theme.spacingS * 5 + Theme.spacingM
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onPressed: mouse => wifiRipple.trigger(mouse.x, mouse.y)
|
onPressed: mouse => wifiRipple.trigger(mouse.x, mouse.y)
|
||||||
|
|||||||
@@ -624,7 +624,7 @@ PanelWindow {
|
|||||||
Item {
|
Item {
|
||||||
id: topBarCore
|
id: topBarCore
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
layer.enabled: true
|
layer.enabled: false
|
||||||
|
|
||||||
property bool autoHide: barConfig?.autoHide ?? false
|
property bool autoHide: barConfig?.autoHide ?? false
|
||||||
property bool revealSticky: false
|
property bool revealSticky: false
|
||||||
|
|||||||
@@ -167,9 +167,22 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
id: headerInfoColumn
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: parent.width - Theme.iconSizeLarge - 32 - Theme.spacingM * 2
|
width: parent.width - Theme.iconSizeLarge - 32 - Theme.spacingM * 2
|
||||||
|
readonly property string timeInfoText: {
|
||||||
|
if (!BatteryService.batteryAvailable)
|
||||||
|
return "Power profile management available";
|
||||||
|
const time = BatteryService.formatTimeRemaining();
|
||||||
|
if (time !== "Unknown") {
|
||||||
|
return BatteryService.isCharging ? `Time until full: ${time}` : `Time remaining: ${time}`;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
readonly property bool showPowerRate: BatteryService.batteryAvailable && Math.abs(BatteryService.changeRate) > 0.05
|
||||||
|
readonly property bool isOnAC: BatteryService.batteryAvailable && (BatteryService.isCharging || BatteryService.isPluggedIn)
|
||||||
|
readonly property bool isDischarging: BatteryService.batteryAvailable && !BatteryService.isCharging && !BatteryService.isPluggedIn
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
@@ -207,21 +220,35 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
Row {
|
||||||
text: {
|
|
||||||
if (!BatteryService.batteryAvailable)
|
|
||||||
return "Power profile management available";
|
|
||||||
const time = BatteryService.formatTimeRemaining();
|
|
||||||
if (time !== "Unknown") {
|
|
||||||
return BatteryService.isCharging ? `Time until full: ${time}` : `Time remaining: ${time}`;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceTextMedium
|
|
||||||
visible: text.length > 0
|
|
||||||
elide: Text.ElideRight
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
visible: headerInfoColumn.timeInfoText.length > 0
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: powerRateText
|
||||||
|
text: `${headerInfoColumn.isOnAC ? "+" : (headerInfoColumn.isDischarging ? "-" : "")}${Math.abs(BatteryService.changeRate).toFixed(1)}W`
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: {
|
||||||
|
if (headerInfoColumn.isOnAC) {
|
||||||
|
return Theme.primary;
|
||||||
|
}
|
||||||
|
if (headerInfoColumn.isDischarging) {
|
||||||
|
return Theme.warning;
|
||||||
|
}
|
||||||
|
return Theme.surfaceTextMedium;
|
||||||
|
}
|
||||||
|
font.weight: Font.Medium
|
||||||
|
visible: headerInfoColumn.showPowerRate
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: headerInfoColumn.timeInfoText
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: parent.width - (powerRateText.visible ? (powerRateText.implicitWidth + parent.spacing) : 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,12 +29,21 @@ Loader {
|
|||||||
|
|
||||||
readonly property bool orientationMatches: (axis?.isVertical ?? false) === isInColumn
|
readonly property bool orientationMatches: (axis?.isVertical ?? false) === isInColumn
|
||||||
|
|
||||||
|
readonly property bool widgetEnabled: widgetData?.enabled !== false
|
||||||
|
|
||||||
active: orientationMatches && getWidgetVisible(widgetId, DgopService.dgopAvailable) && (widgetId !== "music" || MprisController.activePlayer !== null)
|
active: orientationMatches && getWidgetVisible(widgetId, DgopService.dgopAvailable) && (widgetId !== "music" || MprisController.activePlayer !== null)
|
||||||
sourceComponent: getWidgetComponent(widgetId, components)
|
sourceComponent: getWidgetComponent(widgetId, components)
|
||||||
opacity: getWidgetEnabled(widgetData?.enabled) ? 1 : 0
|
|
||||||
|
|
||||||
signal contentItemReady(var item)
|
signal contentItemReady(var item)
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: root.item
|
||||||
|
when: root.item && !root.widgetEnabled
|
||||||
|
property: "visible"
|
||||||
|
value: false
|
||||||
|
restoreMode: Binding.RestoreBinding
|
||||||
|
}
|
||||||
|
|
||||||
Binding {
|
Binding {
|
||||||
target: root.item
|
target: root.item
|
||||||
when: root.item && "parentScreen" in root.item
|
when: root.item && "parentScreen" in root.item
|
||||||
@@ -269,8 +278,4 @@ Loader {
|
|||||||
|
|
||||||
return widgetVisibility[widgetId] ?? true;
|
return widgetVisibility[widgetId] ?? true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWidgetEnabled(enabled) {
|
|
||||||
return enabled !== false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ BasePill {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
const locale = Qt.locale();
|
const locale = I18n.locale();
|
||||||
const dateFormatShort = locale.dateFormat(Locale.ShortFormat);
|
const dateFormatShort = locale.dateFormat(Locale.ShortFormat);
|
||||||
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M');
|
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M');
|
||||||
const value = dayFirst ? String(systemClock?.date?.getDate()).padStart(2, '0') : String(systemClock?.date?.getMonth() + 1).padStart(2, '0');
|
const value = dayFirst ? String(systemClock?.date?.getDate()).padStart(2, '0') : String(systemClock?.date?.getMonth() + 1).padStart(2, '0');
|
||||||
@@ -144,7 +144,7 @@ BasePill {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
const locale = Qt.locale();
|
const locale = I18n.locale();
|
||||||
const dateFormatShort = locale.dateFormat(Locale.ShortFormat);
|
const dateFormatShort = locale.dateFormat(Locale.ShortFormat);
|
||||||
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M');
|
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M');
|
||||||
const value = dayFirst ? String(systemClock?.date?.getDate()).padStart(2, '0') : String(systemClock?.date?.getMonth() + 1).padStart(2, '0');
|
const value = dayFirst ? String(systemClock?.date?.getDate()).padStart(2, '0') : String(systemClock?.date?.getMonth() + 1).padStart(2, '0');
|
||||||
@@ -165,7 +165,7 @@ BasePill {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
const locale = Qt.locale();
|
const locale = I18n.locale();
|
||||||
const dateFormatShort = locale.dateFormat(Locale.ShortFormat);
|
const dateFormatShort = locale.dateFormat(Locale.ShortFormat);
|
||||||
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M');
|
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M');
|
||||||
const value = dayFirst ? String(systemClock?.date?.getMonth() + 1).padStart(2, '0') : String(systemClock?.date?.getDate()).padStart(2, '0');
|
const value = dayFirst ? String(systemClock?.date?.getMonth() + 1).padStart(2, '0') : String(systemClock?.date?.getDate()).padStart(2, '0');
|
||||||
@@ -180,7 +180,7 @@ BasePill {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
const locale = Qt.locale();
|
const locale = I18n.locale();
|
||||||
const dateFormatShort = locale.dateFormat(Locale.ShortFormat);
|
const dateFormatShort = locale.dateFormat(Locale.ShortFormat);
|
||||||
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M');
|
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M');
|
||||||
const value = dayFirst ? String(systemClock?.date?.getMonth() + 1).padStart(2, '0') : String(systemClock?.date?.getDate()).padStart(2, '0');
|
const value = dayFirst ? String(systemClock?.date?.getMonth() + 1).padStart(2, '0') : String(systemClock?.date?.getDate()).padStart(2, '0');
|
||||||
@@ -311,9 +311,9 @@ BasePill {
|
|||||||
id: dateText
|
id: dateText
|
||||||
text: {
|
text: {
|
||||||
if (SettingsData.clockDateFormat && SettingsData.clockDateFormat.length > 0) {
|
if (SettingsData.clockDateFormat && SettingsData.clockDateFormat.length > 0) {
|
||||||
return systemClock?.date?.toLocaleDateString(Qt.locale(), SettingsData.clockDateFormat);
|
return systemClock?.date?.toLocaleDateString(I18n.locale(), SettingsData.clockDateFormat);
|
||||||
}
|
}
|
||||||
return systemClock?.date?.toLocaleDateString(Qt.locale(), "ddd d");
|
return systemClock?.date?.toLocaleDateString(I18n.locale(), "ddd d");
|
||||||
}
|
}
|
||||||
font.pixelSize: clockRow.fontSize
|
font.pixelSize: clockRow.fontSize
|
||||||
color: Theme.widgetTextColor
|
color: Theme.widgetTextColor
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ BasePill {
|
|||||||
|
|
||||||
property var widgetData: null
|
property var widgetData: null
|
||||||
property string mountPath: (widgetData && widgetData.mountPath !== undefined) ? widgetData.mountPath : "/"
|
property string mountPath: (widgetData && widgetData.mountPath !== undefined) ? widgetData.mountPath : "/"
|
||||||
|
property int diskUsageMode: (widgetData && widgetData.diskUsageMode !== undefined) ? widgetData.diskUsageMode : 0
|
||||||
property bool isHovered: mouseArea.containsMouse
|
property bool isHovered: mouseArea.containsMouse
|
||||||
property bool isAutoHideBar: false
|
property bool isAutoHideBar: false
|
||||||
|
|
||||||
@@ -130,7 +131,13 @@ BasePill {
|
|||||||
if (root.diskUsagePercent === undefined || root.diskUsagePercent === null || root.diskUsagePercent === 0) {
|
if (root.diskUsagePercent === undefined || root.diskUsagePercent === null || root.diskUsagePercent === 0) {
|
||||||
return "--";
|
return "--";
|
||||||
}
|
}
|
||||||
return root.diskUsagePercent.toFixed(0);
|
if (!root.selectedMount) return "--";
|
||||||
|
switch (root.diskUsageMode) {
|
||||||
|
case 1: return root.selectedMount.size || "--";
|
||||||
|
case 2: return root.selectedMount.avail || "--";
|
||||||
|
case 3: return (root.selectedMount.avail || "--") + " / " + (root.selectedMount.size || "--");
|
||||||
|
default: return root.diskUsagePercent.toFixed(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
||||||
color: Theme.widgetTextColor
|
color: Theme.widgetTextColor
|
||||||
@@ -178,7 +185,13 @@ BasePill {
|
|||||||
if (root.diskUsagePercent === undefined || root.diskUsagePercent === null || root.diskUsagePercent === 0) {
|
if (root.diskUsagePercent === undefined || root.diskUsagePercent === null || root.diskUsagePercent === 0) {
|
||||||
return "--%";
|
return "--%";
|
||||||
}
|
}
|
||||||
return root.diskUsagePercent.toFixed(0) + "%";
|
if (!root.selectedMount) return "--%";
|
||||||
|
switch (root.diskUsageMode) {
|
||||||
|
case 1: return root.selectedMount.size || "--";
|
||||||
|
case 2: return root.selectedMount.avail || "--";
|
||||||
|
case 3: return (root.selectedMount.avail || "--") + " / " + (root.selectedMount.size || "--");
|
||||||
|
default: return root.diskUsagePercent.toFixed(0) + "%";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
||||||
color: Theme.widgetTextColor
|
color: Theme.widgetTextColor
|
||||||
@@ -189,7 +202,14 @@ BasePill {
|
|||||||
StyledTextMetrics {
|
StyledTextMetrics {
|
||||||
id: diskBaseline
|
id: diskBaseline
|
||||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
||||||
text: "100%"
|
text: {
|
||||||
|
switch (root.diskUsageMode) {
|
||||||
|
case 3: return "888.8G / 888.8G";
|
||||||
|
case 1:
|
||||||
|
case 2: return "888.8G";
|
||||||
|
default: return "100%";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
width: Math.max(diskBaseline.width, paintedWidth)
|
width: Math.max(diskBaseline.width, paintedWidth)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ BasePill {
|
|||||||
property var widgetData: null
|
property var widgetData: null
|
||||||
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true
|
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true
|
||||||
property bool showSwap: (widgetData && widgetData.showSwap !== undefined) ? widgetData.showSwap : false
|
property bool showSwap: (widgetData && widgetData.showSwap !== undefined) ? widgetData.showSwap : false
|
||||||
|
property bool showInGb: (widgetData && widgetData.showInGb !== undefined) ? widgetData.showInGb : false
|
||||||
readonly property real swapUsage: DgopService.totalSwapKB > 0 ? (DgopService.usedSwapKB / DgopService.totalSwapKB) * 100 : 0
|
readonly property real swapUsage: DgopService.totalSwapKB > 0 ? (DgopService.usedSwapKB / DgopService.totalSwapKB) * 100 : 0
|
||||||
|
|
||||||
signal ramClicked
|
signal ramClicked
|
||||||
@@ -59,6 +60,10 @@ BasePill {
|
|||||||
return "--";
|
return "--";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (root.showInGb) {
|
||||||
|
return (DgopService.usedMemoryMB / 1024).toFixed(1);
|
||||||
|
}
|
||||||
|
|
||||||
return DgopService.memoryUsage.toFixed(0);
|
return DgopService.memoryUsage.toFixed(0);
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
||||||
@@ -113,13 +118,14 @@ BasePill {
|
|||||||
id: ramBaseline
|
id: ramBaseline
|
||||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
||||||
text: {
|
text: {
|
||||||
|
let baseText = root.showInGb ? "88.8 GB" : "88%";
|
||||||
if (!root.showSwap) {
|
if (!root.showSwap) {
|
||||||
return "88%";
|
return baseText;
|
||||||
}
|
}
|
||||||
if (root.swapUsage < 10) {
|
if (root.swapUsage < 10) {
|
||||||
return "88% · 0%";
|
return baseText + " · 0%";
|
||||||
}
|
}
|
||||||
return "88% · 88%";
|
return baseText + " · 88%";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,10 +133,16 @@ BasePill {
|
|||||||
id: ramText
|
id: ramText
|
||||||
text: {
|
text: {
|
||||||
if (DgopService.memoryUsage === undefined || DgopService.memoryUsage === null || DgopService.memoryUsage === 0) {
|
if (DgopService.memoryUsage === undefined || DgopService.memoryUsage === null || DgopService.memoryUsage === 0) {
|
||||||
return "--%";
|
return root.showInGb ? "-- GB" : "--%";
|
||||||
|
}
|
||||||
|
|
||||||
|
let ramText = "";
|
||||||
|
if (root.showInGb) {
|
||||||
|
ramText = (DgopService.usedMemoryMB / 1024).toFixed(1) + " GB";
|
||||||
|
} else {
|
||||||
|
ramText = DgopService.memoryUsage.toFixed(0) + "%";
|
||||||
}
|
}
|
||||||
|
|
||||||
let ramText = DgopService.memoryUsage.toFixed(0) + "%";
|
|
||||||
if (root.showSwap && DgopService.totalSwapKB > 0) {
|
if (root.showSwap && DgopService.totalSwapKB > 0) {
|
||||||
return ramText + " · " + root.swapUsage.toFixed(0) + "%";
|
return ramText + " · " + root.swapUsage.toFixed(0) + "%";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ BasePill {
|
|||||||
return `${id}::${tooltipTitle}`;
|
return `${id}::${tooltipTitle}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ! TODO - replace with either native dbus client (like plugins use) or just a DMS cli or something
|
||||||
|
function callContextMenuFallback(trayItemId, globalX, globalY) {
|
||||||
|
const script = ['ITEMS=$(dbus-send --session --print-reply --dest=org.kde.StatusNotifierWatcher /StatusNotifierWatcher org.freedesktop.DBus.Properties.Get string:org.kde.StatusNotifierWatcher string:RegisteredStatusNotifierItems 2>/dev/null)', 'while IFS= read -r line; do', ' line="${line#*\\\"}"', ' line="${line%\\\"*}"', ' [ -z "$line" ] && continue', ' BUS="${line%%/*}"', ' OBJ="/${line#*/}"', ' ID=$(dbus-send --session --print-reply --dest="$BUS" "$OBJ" org.freedesktop.DBus.Properties.Get string:org.kde.StatusNotifierItem string:Id 2>/dev/null | grep -oP "(?<=\\\")(.*?)(?=\\\")" | tail -1)', ' if [ "$ID" = "$1" ]; then', ' dbus-send --session --type=method_call --dest="$BUS" "$OBJ" org.kde.StatusNotifierItem.ContextMenu int32:"$2" int32:"$3"', ' exit 0', ' fi', 'done <<< "$ITEMS"',].join("\n");
|
||||||
|
Quickshell.execDetached(["bash", "-c", script, "_", trayItemId, String(globalX), String(globalY)]);
|
||||||
|
}
|
||||||
|
|
||||||
property int _trayOrderTrigger: 0
|
property int _trayOrderTrigger: 0
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -380,8 +386,11 @@ BasePill {
|
|||||||
return;
|
return;
|
||||||
if (mouse.button !== Qt.RightButton)
|
if (mouse.button !== Qt.RightButton)
|
||||||
return;
|
return;
|
||||||
if (!delegateRoot.trayItem?.hasMenu)
|
if (!delegateRoot.trayItem?.hasMenu) {
|
||||||
|
const gp = trayItemArea.mapToGlobal(mouse.x, mouse.y);
|
||||||
|
root.callContextMenuFallback(delegateRoot.trayItem.id, Math.round(gp.x), Math.round(gp.y));
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
root.menuOpen = false;
|
root.menuOpen = false;
|
||||||
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis);
|
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis);
|
||||||
}
|
}
|
||||||
@@ -637,8 +646,11 @@ BasePill {
|
|||||||
return;
|
return;
|
||||||
if (mouse.button !== Qt.RightButton)
|
if (mouse.button !== Qt.RightButton)
|
||||||
return;
|
return;
|
||||||
if (!delegateRoot.trayItem?.hasMenu)
|
if (!delegateRoot.trayItem?.hasMenu) {
|
||||||
|
const gp = trayItemArea.mapToGlobal(mouse.x, mouse.y);
|
||||||
|
root.callContextMenuFallback(delegateRoot.trayItem.id, Math.round(gp.x), Math.round(gp.y));
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
root.menuOpen = false;
|
root.menuOpen = false;
|
||||||
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis);
|
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis);
|
||||||
}
|
}
|
||||||
@@ -1065,9 +1077,11 @@ BasePill {
|
|||||||
root.menuOpen = false;
|
root.menuOpen = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!trayItem.hasMenu) {
|
||||||
if (!trayItem.hasMenu)
|
const gp = itemArea.mapToGlobal(mouse.x, mouse.y);
|
||||||
|
root.callContextMenuFallback(trayItem.id, Math.round(gp.x), Math.round(gp.y));
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
root.showForTrayItem(trayItem, menuContainer, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis);
|
root.showForTrayItem(trayItem, menuContainer, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ Rectangle {
|
|||||||
StyledText {
|
StyledText {
|
||||||
width: parent.width - 56
|
width: parent.width - 56
|
||||||
height: 28
|
height: 28
|
||||||
text: calendarGrid.displayDate.toLocaleDateString(Qt.locale(), "MMMM yyyy")
|
text: calendarGrid.displayDate.toLocaleDateString(I18n.locale(), "MMMM yyyy")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -223,11 +223,10 @@ Rectangle {
|
|||||||
Repeater {
|
Repeater {
|
||||||
model: {
|
model: {
|
||||||
const days = [];
|
const days = [];
|
||||||
const loc = Qt.locale();
|
const qtFirst = Qt.locale().firstDayOfWeek;
|
||||||
const qtFirst = loc.firstDayOfWeek;
|
|
||||||
for (let i = 0; i < 7; ++i) {
|
for (let i = 0; i < 7; ++i) {
|
||||||
const qtDay = ((qtFirst - 1 + i) % 7) + 1;
|
const qtDay = ((qtFirst - 1 + i) % 7) + 1;
|
||||||
days.push(loc.dayName(qtDay, Locale.ShortFormat));
|
days.push(I18n.locale().dayName(qtDay, Locale.ShortFormat));
|
||||||
}
|
}
|
||||||
return days;
|
return days;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ Card {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: systemClock?.date?.toLocaleDateString(Qt.locale(), "MMM dd")
|
text: systemClock?.date?.toLocaleDateString(I18n.locale(), "MMM dd")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|||||||
@@ -41,6 +41,11 @@ Singleton {
|
|||||||
property string lockDateFormat: ""
|
property string lockDateFormat: ""
|
||||||
property bool lockScreenShowPowerActions: true
|
property bool lockScreenShowPowerActions: true
|
||||||
property bool lockScreenShowProfileImage: true
|
property bool lockScreenShowProfileImage: true
|
||||||
|
property bool powerActionConfirm: true
|
||||||
|
property real powerActionHoldDuration: 0.5
|
||||||
|
property var powerMenuActions: ["reboot", "logout", "poweroff", "lock", "suspend", "restart"]
|
||||||
|
property string powerMenuDefaultAction: "logout"
|
||||||
|
property bool powerMenuGridLayout: false
|
||||||
property var screenPreferences: ({})
|
property var screenPreferences: ({})
|
||||||
property int animationSpeed: 2
|
property int animationSpeed: 2
|
||||||
property string wallpaperFillMode: "Fill"
|
property string wallpaperFillMode: "Fill"
|
||||||
@@ -75,6 +80,11 @@ Singleton {
|
|||||||
lockDateFormat = settings.lockDateFormat !== undefined ? settings.lockDateFormat : "";
|
lockDateFormat = settings.lockDateFormat !== undefined ? settings.lockDateFormat : "";
|
||||||
lockScreenShowPowerActions = settings.lockScreenShowPowerActions !== undefined ? settings.lockScreenShowPowerActions : true;
|
lockScreenShowPowerActions = settings.lockScreenShowPowerActions !== undefined ? settings.lockScreenShowPowerActions : true;
|
||||||
lockScreenShowProfileImage = settings.lockScreenShowProfileImage !== undefined ? settings.lockScreenShowProfileImage : true;
|
lockScreenShowProfileImage = settings.lockScreenShowProfileImage !== undefined ? settings.lockScreenShowProfileImage : true;
|
||||||
|
powerActionConfirm = settings.powerActionConfirm !== undefined ? settings.powerActionConfirm : true;
|
||||||
|
powerActionHoldDuration = settings.powerActionHoldDuration !== undefined ? settings.powerActionHoldDuration : 0.5;
|
||||||
|
powerMenuActions = settings.powerMenuActions !== undefined ? settings.powerMenuActions : ["reboot", "logout", "poweroff", "lock", "suspend", "restart"];
|
||||||
|
powerMenuDefaultAction = settings.powerMenuDefaultAction !== undefined ? settings.powerMenuDefaultAction : "logout";
|
||||||
|
powerMenuGridLayout = settings.powerMenuGridLayout !== undefined ? settings.powerMenuGridLayout : false;
|
||||||
screenPreferences = settings.screenPreferences !== undefined ? settings.screenPreferences : ({});
|
screenPreferences = settings.screenPreferences !== undefined ? settings.screenPreferences : ({});
|
||||||
animationSpeed = settings.animationSpeed !== undefined ? settings.animationSpeed : 2;
|
animationSpeed = settings.animationSpeed !== undefined ? settings.animationSpeed : 2;
|
||||||
wallpaperFillMode = settings.wallpaperFillMode !== undefined ? settings.wallpaperFillMode : "Fill";
|
wallpaperFillMode = settings.wallpaperFillMode !== undefined ? settings.wallpaperFillMode : "Fill";
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ Item {
|
|||||||
|
|
||||||
property string fullTimeStr: {
|
property string fullTimeStr: {
|
||||||
const format = GreetdSettings.getEffectiveTimeFormat();
|
const format = GreetdSettings.getEffectiveTimeFormat();
|
||||||
return systemClock.date.toLocaleTimeString(Qt.locale(), format);
|
return systemClock.date.toLocaleTimeString(I18n.locale(), format);
|
||||||
}
|
}
|
||||||
property var timeParts: fullTimeStr.split(':')
|
property var timeParts: fullTimeStr.split(':')
|
||||||
property string hours: timeParts[0] || ""
|
property string hours: timeParts[0] || ""
|
||||||
@@ -328,9 +328,9 @@ Item {
|
|||||||
anchors.topMargin: 4
|
anchors.topMargin: 4
|
||||||
text: {
|
text: {
|
||||||
if (GreetdSettings.lockDateFormat && GreetdSettings.lockDateFormat.length > 0) {
|
if (GreetdSettings.lockDateFormat && GreetdSettings.lockDateFormat.length > 0) {
|
||||||
return systemClock.date.toLocaleDateString(Qt.locale(), GreetdSettings.lockDateFormat);
|
return systemClock.date.toLocaleDateString(I18n.locale(), GreetdSettings.lockDateFormat);
|
||||||
}
|
}
|
||||||
return systemClock.date.toLocaleDateString(Qt.locale(), Locale.LongFormat);
|
return systemClock.date.toLocaleDateString(I18n.locale(), Locale.LongFormat);
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeXLarge
|
font.pixelSize: Theme.fontSizeXLarge
|
||||||
color: "white"
|
color: "white"
|
||||||
@@ -1231,6 +1231,12 @@ Item {
|
|||||||
LockPowerMenu {
|
LockPowerMenu {
|
||||||
id: powerMenu
|
id: powerMenu
|
||||||
showLogout: false
|
showLogout: false
|
||||||
|
powerActionConfirmOverride: GreetdSettings.powerActionConfirm
|
||||||
|
powerActionHoldDurationOverride: GreetdSettings.powerActionHoldDuration
|
||||||
|
powerMenuActionsOverride: GreetdSettings.powerMenuActions
|
||||||
|
powerMenuDefaultActionOverride: GreetdSettings.powerMenuDefaultAction
|
||||||
|
powerMenuGridLayoutOverride: GreetdSettings.powerMenuGridLayout
|
||||||
|
requiredActions: ["poweroff"]
|
||||||
onClosed: {
|
onClosed: {
|
||||||
if (isPrimaryScreen && inputField && inputField.forceActiveFocus) {
|
if (isPrimaryScreen && inputField && inputField.forceActiveFocus) {
|
||||||
Qt.callLater(() => inputField.forceActiveFocus());
|
Qt.callLater(() => inputField.forceActiveFocus());
|
||||||
|
|||||||
@@ -24,13 +24,20 @@ Rectangle {
|
|||||||
property real holdProgress: 0
|
property real holdProgress: 0
|
||||||
property bool showHoldHint: false
|
property bool showHoldHint: false
|
||||||
|
|
||||||
readonly property bool needsConfirmation: SettingsData.powerActionConfirm
|
property var powerActionConfirmOverride: undefined
|
||||||
readonly property int holdDurationMs: SettingsData.powerActionHoldDuration * 1000
|
property var powerActionHoldDurationOverride: undefined
|
||||||
|
property var powerMenuActionsOverride: undefined
|
||||||
|
property var powerMenuDefaultActionOverride: undefined
|
||||||
|
property var powerMenuGridLayoutOverride: undefined
|
||||||
|
property var requiredActions: []
|
||||||
|
|
||||||
|
readonly property bool needsConfirmation: powerActionConfirmOverride !== undefined ? powerActionConfirmOverride : SettingsData.powerActionConfirm
|
||||||
|
readonly property int holdDurationMs: (powerActionHoldDurationOverride !== undefined ? powerActionHoldDurationOverride : SettingsData.powerActionHoldDuration) * 1000
|
||||||
|
|
||||||
signal closed
|
signal closed
|
||||||
|
|
||||||
function updateVisibleActions() {
|
function updateVisibleActions() {
|
||||||
const allActions = (typeof SettingsData !== "undefined" && SettingsData.powerMenuActions) ? SettingsData.powerMenuActions : ["logout", "suspend", "hibernate", "reboot", "poweroff"];
|
const allActions = powerMenuActionsOverride !== undefined ? powerMenuActionsOverride : ((typeof SettingsData !== "undefined" && SettingsData.powerMenuActions) ? SettingsData.powerMenuActions : ["logout", "suspend", "hibernate", "reboot", "poweroff"]);
|
||||||
const hibernateSupported = (typeof SessionService !== "undefined" && SessionService.hibernateSupported) || false;
|
const hibernateSupported = (typeof SessionService !== "undefined" && SessionService.hibernateSupported) || false;
|
||||||
let filtered = allActions.filter(action => {
|
let filtered = allActions.filter(action => {
|
||||||
if (action === "hibernate" && !hibernateSupported)
|
if (action === "hibernate" && !hibernateSupported)
|
||||||
@@ -44,9 +51,14 @@ Rectangle {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (const action of requiredActions) {
|
||||||
|
if (!filtered.includes(action))
|
||||||
|
filtered.push(action);
|
||||||
|
}
|
||||||
|
|
||||||
visibleActions = filtered;
|
visibleActions = filtered;
|
||||||
|
|
||||||
useGridLayout = (typeof SettingsData !== "undefined" && SettingsData.powerMenuGridLayout !== undefined) ? SettingsData.powerMenuGridLayout : false;
|
useGridLayout = powerMenuGridLayoutOverride !== undefined ? powerMenuGridLayoutOverride : ((typeof SettingsData !== "undefined" && SettingsData.powerMenuGridLayout !== undefined) ? SettingsData.powerMenuGridLayout : false);
|
||||||
if (!useGridLayout)
|
if (!useGridLayout)
|
||||||
return;
|
return;
|
||||||
const count = visibleActions.length;
|
const count = visibleActions.length;
|
||||||
@@ -73,7 +85,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getDefaultActionIndex() {
|
function getDefaultActionIndex() {
|
||||||
const defaultAction = (typeof SettingsData !== "undefined" && SettingsData.powerMenuDefaultAction) ? SettingsData.powerMenuDefaultAction : "suspend";
|
const defaultAction = powerMenuDefaultActionOverride !== undefined ? powerMenuDefaultActionOverride : ((typeof SettingsData !== "undefined" && SettingsData.powerMenuDefaultAction) ? SettingsData.powerMenuDefaultAction : "suspend");
|
||||||
const index = visibleActions.indexOf(defaultAction);
|
const index = visibleActions.indexOf(defaultAction);
|
||||||
return index >= 0 ? index : 0;
|
return index >= 0 ? index : 0;
|
||||||
}
|
}
|
||||||
@@ -780,8 +792,9 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
readonly property real totalMs: SettingsData.powerActionHoldDuration * 1000
|
readonly property real totalMs: root.holdDurationMs
|
||||||
readonly property int remainingMs: Math.ceil(totalMs * (1 - root.holdProgress))
|
readonly property int remainingMs: Math.ceil(totalMs * (1 - root.holdProgress))
|
||||||
|
readonly property real durationSec: root.holdDurationMs / 1000
|
||||||
text: {
|
text: {
|
||||||
if (root.showHoldHint)
|
if (root.showHoldHint)
|
||||||
return I18n.tr("Hold longer to confirm");
|
return I18n.tr("Hold longer to confirm");
|
||||||
@@ -792,7 +805,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
if (totalMs < 1000)
|
if (totalMs < 1000)
|
||||||
return I18n.tr("Hold to confirm (%1 ms)").arg(totalMs);
|
return I18n.tr("Hold to confirm (%1 ms)").arg(totalMs);
|
||||||
return I18n.tr("Hold to confirm (%1s)").arg(SettingsData.powerActionHoldDuration);
|
return I18n.tr("Hold to confirm (%1s)").arg(durationSec);
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: root.showHoldHint ? Theme.warning : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
color: root.showHoldHint ? Theme.warning : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
||||||
|
|||||||
@@ -333,9 +333,9 @@ Item {
|
|||||||
visible: SettingsData.lockScreenShowDate
|
visible: SettingsData.lockScreenShowDate
|
||||||
text: {
|
text: {
|
||||||
if (SettingsData.lockDateFormat && SettingsData.lockDateFormat.length > 0) {
|
if (SettingsData.lockDateFormat && SettingsData.lockDateFormat.length > 0) {
|
||||||
return systemClock.date.toLocaleDateString(Qt.locale(), SettingsData.lockDateFormat);
|
return systemClock.date.toLocaleDateString(I18n.locale(), SettingsData.lockDateFormat);
|
||||||
}
|
}
|
||||||
return systemClock.date.toLocaleDateString(Qt.locale(), Locale.LongFormat);
|
return systemClock.date.toLocaleDateString(I18n.locale(), Locale.LongFormat);
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeXLarge
|
font.pixelSize: Theme.fontSizeXLarge
|
||||||
color: "white"
|
color: "white"
|
||||||
@@ -687,14 +687,24 @@ Item {
|
|||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
name: {
|
name: {
|
||||||
|
if (pam.u2fPending)
|
||||||
|
return "passkey";
|
||||||
if (pam.fprint.tries >= SettingsData.maxFprintTries)
|
if (pam.fprint.tries >= SettingsData.maxFprintTries)
|
||||||
return "fingerprint_off";
|
return "fingerprint_off";
|
||||||
if (pam.fprint.active)
|
if (pam.fprint.active)
|
||||||
return "fingerprint";
|
return "fingerprint";
|
||||||
|
if (pam.u2f.active)
|
||||||
|
return "passkey";
|
||||||
return "lock";
|
return "lock";
|
||||||
}
|
}
|
||||||
size: 20
|
size: 20
|
||||||
color: pam.fprint.tries >= SettingsData.maxFprintTries ? Theme.error : (passwordField.activeFocus ? Theme.primary : Theme.surfaceVariantText)
|
color: {
|
||||||
|
if (pam.fprint.tries >= SettingsData.maxFprintTries)
|
||||||
|
return Theme.error;
|
||||||
|
if (pam.u2fState !== "")
|
||||||
|
return Theme.tertiary;
|
||||||
|
return passwordField.activeFocus ? Theme.primary : Theme.surfaceVariantText;
|
||||||
|
}
|
||||||
opacity: pam.passwd.active ? 0 : 1
|
opacity: pam.passwd.active ? 0 : 1
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
@@ -745,8 +755,7 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
if (!demoMode && !pam.passwd.active) {
|
if (!demoMode && !pam.passwd.active && !pam.u2fPending) {
|
||||||
console.log("Enter pressed, starting PAM authentication");
|
|
||||||
pam.passwd.start();
|
pam.passwd.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -756,6 +765,11 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === Qt.Key_Escape) {
|
if (event.key === Qt.Key_Escape) {
|
||||||
|
if (pam.u2fPending) {
|
||||||
|
pam.cancelU2fPending();
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -820,6 +834,11 @@ Item {
|
|||||||
if (root.unlocking) {
|
if (root.unlocking) {
|
||||||
return "Unlocking...";
|
return "Unlocking...";
|
||||||
}
|
}
|
||||||
|
if (pam.u2fPending) {
|
||||||
|
if (pam.u2fState === "insert")
|
||||||
|
return "Insert your security key...";
|
||||||
|
return "Touch your security key...";
|
||||||
|
}
|
||||||
if (pam.passwd.active) {
|
if (pam.passwd.active) {
|
||||||
return "Authenticating...";
|
return "Authenticating...";
|
||||||
}
|
}
|
||||||
@@ -894,7 +913,7 @@ Item {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
iconName: "keyboard"
|
iconName: "keyboard"
|
||||||
buttonSize: 32
|
buttonSize: 32
|
||||||
visible: !demoMode && !pam.passwd.active && !root.unlocking
|
visible: !demoMode && !pam.passwd.active && !root.unlocking && !pam.u2fPending
|
||||||
enabled: visible
|
enabled: visible
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (keyboardController.isKeyboardActive) {
|
if (keyboardController.isKeyboardActive) {
|
||||||
@@ -995,11 +1014,10 @@ Item {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
iconName: "keyboard_return"
|
iconName: "keyboard_return"
|
||||||
buttonSize: 36
|
buttonSize: 36
|
||||||
visible: (demoMode || (!pam.passwd.active && !root.unlocking))
|
visible: (demoMode || (!pam.passwd.active && !root.unlocking && !pam.u2fPending))
|
||||||
enabled: !demoMode
|
enabled: !demoMode
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!demoMode) {
|
if (!demoMode && !pam.u2fPending) {
|
||||||
console.log("Enter button clicked, starting PAM authentication");
|
|
||||||
pam.passwd.start();
|
pam.passwd.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1025,6 +1043,12 @@ Item {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: 20
|
Layout.preferredHeight: 20
|
||||||
text: {
|
text: {
|
||||||
|
if (pam.u2fState === "insert" && !pam.u2fPending) {
|
||||||
|
return "Insert your security key...";
|
||||||
|
}
|
||||||
|
if (pam.u2fState === "waiting" && !pam.u2fPending) {
|
||||||
|
return "Touch your security key...";
|
||||||
|
}
|
||||||
if (root.pamState === "error") {
|
if (root.pamState === "error") {
|
||||||
return "Authentication error - try again";
|
return "Authentication error - try again";
|
||||||
}
|
}
|
||||||
@@ -1036,10 +1060,10 @@ Item {
|
|||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
color: Theme.error
|
color: (pam.u2fState === "waiting" || pam.u2fState === "insert") ? Theme.outline : Theme.error
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
opacity: root.pamState !== "" ? 1 : 0
|
opacity: (root.pamState !== "" || ((pam.u2fState === "waiting" || pam.u2fState === "insert") && !pam.u2fPending)) ? 1 : 0
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
@@ -1607,6 +1631,14 @@ Item {
|
|||||||
root.passwordBuffer = "";
|
root.passwordBuffer = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onU2fPendingChanged: {
|
||||||
|
if (u2fPending) {
|
||||||
|
passwordField.text = "";
|
||||||
|
root.passwordBuffer = "";
|
||||||
|
if (keyboardController.isKeyboardActive)
|
||||||
|
keyboardController.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Binding {
|
Binding {
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ pragma ComponentBehavior: Bound
|
|||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
|
import qs.Common
|
||||||
|
|
||||||
Rectangle {
|
FocusScope {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property WlSessionLock lock
|
required property WlSessionLock lock
|
||||||
@@ -14,7 +15,17 @@ Rectangle {
|
|||||||
signal passwordChanged(string newPassword)
|
signal passwordChanged(string newPassword)
|
||||||
signal unlockRequested
|
signal unlockRequested
|
||||||
|
|
||||||
color: "transparent"
|
Keys.onPressed: event => {
|
||||||
|
if (videoScreensaver.active && videoScreensaver.inputEnabled) {
|
||||||
|
videoScreensaver.dismiss();
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
|
||||||
LockScreenContent {
|
LockScreenContent {
|
||||||
id: lockContent
|
id: lockContent
|
||||||
@@ -23,17 +34,38 @@ Rectangle {
|
|||||||
demoMode: false
|
demoMode: false
|
||||||
passwordBuffer: root.sharedPasswordBuffer
|
passwordBuffer: root.sharedPasswordBuffer
|
||||||
screenName: root.screenName
|
screenName: root.screenName
|
||||||
|
enabled: !videoScreensaver.active
|
||||||
|
focus: !videoScreensaver.active
|
||||||
|
opacity: videoScreensaver.active ? 0 : 1
|
||||||
onUnlockRequested: root.unlockRequested()
|
onUnlockRequested: root.unlockRequested()
|
||||||
onPasswordBufferChanged: {
|
onPasswordBufferChanged: {
|
||||||
if (root.sharedPasswordBuffer !== passwordBuffer) {
|
if (root.sharedPasswordBuffer !== passwordBuffer) {
|
||||||
root.passwordChanged(passwordBuffer);
|
root.passwordChanged(passwordBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VideoScreensaver {
|
||||||
|
id: videoScreensaver
|
||||||
|
anchors.fill: parent
|
||||||
|
screenName: root.screenName
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: forceActiveFocus()
|
||||||
|
|
||||||
onIsLockedChanged: {
|
onIsLockedChanged: {
|
||||||
if (isLocked) {
|
if (isLocked) {
|
||||||
|
forceActiveFocus();
|
||||||
lockContent.resetLockState();
|
lockContent.resetLockState();
|
||||||
|
if (SettingsData.lockScreenVideoEnabled) {
|
||||||
|
videoScreensaver.start();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lockContent.unlocking = false;
|
lockContent.unlocking = false;
|
||||||
|
|||||||
@@ -14,14 +14,51 @@ Scope {
|
|||||||
|
|
||||||
readonly property alias passwd: passwd
|
readonly property alias passwd: passwd
|
||||||
readonly property alias fprint: fprint
|
readonly property alias fprint: fprint
|
||||||
|
readonly property alias u2f: u2f
|
||||||
property string lockMessage
|
property string lockMessage
|
||||||
property string state
|
property string state
|
||||||
property string fprintState
|
property string fprintState
|
||||||
|
property string u2fState
|
||||||
|
property bool u2fPending: false
|
||||||
property string buffer
|
property string buffer
|
||||||
|
|
||||||
signal flashMsg
|
signal flashMsg
|
||||||
signal unlockRequested
|
signal unlockRequested
|
||||||
|
|
||||||
|
function completeUnlock(): void {
|
||||||
|
if (!unlockInProgress) {
|
||||||
|
unlockInProgress = true;
|
||||||
|
passwd.abort();
|
||||||
|
fprint.abort();
|
||||||
|
u2f.abort();
|
||||||
|
errorRetry.running = false;
|
||||||
|
u2fErrorRetry.running = false;
|
||||||
|
u2fPendingTimeout.running = false;
|
||||||
|
u2fPending = false;
|
||||||
|
u2fState = "";
|
||||||
|
unlockRequested();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function proceedAfterPrimaryAuth(): void {
|
||||||
|
if (SettingsData.enableU2f && SettingsData.u2fMode === "and" && u2f.available) {
|
||||||
|
u2f.startForSecondFactor();
|
||||||
|
} else {
|
||||||
|
completeUnlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelU2fPending(): void {
|
||||||
|
if (!u2fPending)
|
||||||
|
return;
|
||||||
|
u2f.abort();
|
||||||
|
u2fErrorRetry.running = false;
|
||||||
|
u2fPendingTimeout.running = false;
|
||||||
|
u2fPending = false;
|
||||||
|
u2fState = "";
|
||||||
|
fprint.checkAvail();
|
||||||
|
}
|
||||||
|
|
||||||
FileView {
|
FileView {
|
||||||
id: dankshellConfigWatcher
|
id: dankshellConfigWatcher
|
||||||
|
|
||||||
@@ -30,9 +67,9 @@ Scope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FileView {
|
FileView {
|
||||||
id: loginConfigWatcher
|
id: u2fConfigWatcher
|
||||||
|
|
||||||
path: "/etc/pam.d/login"
|
path: "/etc/pam.d/dankshell-u2f"
|
||||||
printErrors: false
|
printErrors: false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +77,7 @@ Scope {
|
|||||||
id: passwd
|
id: passwd
|
||||||
|
|
||||||
config: dankshellConfigWatcher.loaded ? "dankshell" : "login"
|
config: dankshellConfigWatcher.loaded ? "dankshell" : "login"
|
||||||
configDirectory: dankshellConfigWatcher.loaded || loginConfigWatcher.loaded ? "/etc/pam.d" : Quickshell.shellDir + "/assets/pam"
|
configDirectory: dankshellConfigWatcher.loaded ? "/etc/pam.d" : Quickshell.shellDir + "/assets/pam"
|
||||||
|
|
||||||
onMessageChanged: {
|
onMessageChanged: {
|
||||||
if (message.startsWith("The account is locked"))
|
if (message.startsWith("The account is locked"))
|
||||||
@@ -59,9 +96,8 @@ Scope {
|
|||||||
onCompleted: res => {
|
onCompleted: res => {
|
||||||
if (res === PamResult.Success) {
|
if (res === PamResult.Success) {
|
||||||
if (!root.unlockInProgress) {
|
if (!root.unlockInProgress) {
|
||||||
root.unlockInProgress = true;
|
|
||||||
fprint.abort();
|
fprint.abort();
|
||||||
root.unlockRequested();
|
root.proceedAfterPrimaryAuth();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -105,9 +141,8 @@ Scope {
|
|||||||
|
|
||||||
if (res === PamResult.Success) {
|
if (res === PamResult.Success) {
|
||||||
if (!root.unlockInProgress) {
|
if (!root.unlockInProgress) {
|
||||||
root.unlockInProgress = true;
|
|
||||||
passwd.abort();
|
passwd.abort();
|
||||||
root.unlockRequested();
|
root.proceedAfterPrimaryAuth();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -135,6 +170,74 @@ Scope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PamContext {
|
||||||
|
id: u2f
|
||||||
|
|
||||||
|
property bool available
|
||||||
|
|
||||||
|
function checkAvail(): void {
|
||||||
|
if (!available || !SettingsData.enableU2f || !root.lockSecured) {
|
||||||
|
abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SettingsData.u2fMode === "or") {
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startForSecondFactor(): void {
|
||||||
|
if (!available || !SettingsData.enableU2f) {
|
||||||
|
root.completeUnlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
abort();
|
||||||
|
root.u2fPending = true;
|
||||||
|
root.u2fState = "";
|
||||||
|
u2fPendingTimeout.restart();
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
config: u2fConfigWatcher.loaded ? "dankshell-u2f" : "u2f"
|
||||||
|
configDirectory: u2fConfigWatcher.loaded ? "/etc/pam.d" : Quickshell.shellDir + "/assets/pam"
|
||||||
|
|
||||||
|
onMessageChanged: {
|
||||||
|
if (message.toLowerCase().includes("touch"))
|
||||||
|
root.u2fState = "waiting";
|
||||||
|
}
|
||||||
|
|
||||||
|
onCompleted: res => {
|
||||||
|
if (!available || root.unlockInProgress)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (res === PamResult.Success) {
|
||||||
|
root.completeUnlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res === PamResult.Error || res === PamResult.MaxTries || res === PamResult.Failed) {
|
||||||
|
abort();
|
||||||
|
|
||||||
|
if (root.u2fPending) {
|
||||||
|
if (root.u2fState === "waiting") {
|
||||||
|
// AND mode: device was found but auth failed → back to password
|
||||||
|
root.u2fPending = false;
|
||||||
|
root.u2fState = "";
|
||||||
|
fprint.checkAvail();
|
||||||
|
} else {
|
||||||
|
// AND mode: no device found → keep pending, show "Insert...", retry
|
||||||
|
root.u2fState = "insert";
|
||||||
|
u2fErrorRetry.restart();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// OR mode: prompt to insert key, silently retry
|
||||||
|
root.u2fState = "insert";
|
||||||
|
u2fErrorRetry.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: availProc
|
id: availProc
|
||||||
|
|
||||||
@@ -145,6 +248,16 @@ Scope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: u2fAvailProc
|
||||||
|
|
||||||
|
command: ["sh", "-c", "(test -f /usr/lib/security/pam_u2f.so || test -f /usr/lib64/security/pam_u2f.so) && (test -f /etc/pam.d/dankshell-u2f || test -f \"$HOME/.config/Yubico/u2f_keys\")"]
|
||||||
|
onExited: code => {
|
||||||
|
u2f.available = code === 0;
|
||||||
|
u2f.checkAvail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: errorRetry
|
id: errorRetry
|
||||||
|
|
||||||
@@ -152,6 +265,20 @@ Scope {
|
|||||||
onTriggered: fprint.start()
|
onTriggered: fprint.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: u2fErrorRetry
|
||||||
|
|
||||||
|
interval: 800
|
||||||
|
onTriggered: u2f.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: u2fPendingTimeout
|
||||||
|
|
||||||
|
interval: 30000
|
||||||
|
onTriggered: root.cancelU2fPending()
|
||||||
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: stateReset
|
id: stateReset
|
||||||
|
|
||||||
@@ -175,13 +302,22 @@ Scope {
|
|||||||
onLockSecuredChanged: {
|
onLockSecuredChanged: {
|
||||||
if (lockSecured) {
|
if (lockSecured) {
|
||||||
availProc.running = true;
|
availProc.running = true;
|
||||||
|
u2fAvailProc.running = true;
|
||||||
root.state = "";
|
root.state = "";
|
||||||
root.fprintState = "";
|
root.fprintState = "";
|
||||||
|
root.u2fState = "";
|
||||||
|
root.u2fPending = false;
|
||||||
root.lockMessage = "";
|
root.lockMessage = "";
|
||||||
root.unlockInProgress = false;
|
root.unlockInProgress = false;
|
||||||
} else {
|
} else {
|
||||||
fprint.abort();
|
fprint.abort();
|
||||||
passwd.abort();
|
passwd.abort();
|
||||||
|
u2f.abort();
|
||||||
|
errorRetry.running = false;
|
||||||
|
u2fErrorRetry.running = false;
|
||||||
|
u2fPendingTimeout.running = false;
|
||||||
|
root.u2fPending = false;
|
||||||
|
root.u2fState = "";
|
||||||
root.unlockInProgress = false;
|
root.unlockInProgress = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,5 +328,20 @@ Scope {
|
|||||||
function onEnableFprintChanged(): void {
|
function onEnableFprintChanged(): void {
|
||||||
fprint.checkAvail();
|
fprint.checkAvail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onEnableU2fChanged(): void {
|
||||||
|
u2f.checkAvail();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onU2fModeChanged(): void {
|
||||||
|
if (root.lockSecured) {
|
||||||
|
u2f.abort();
|
||||||
|
u2fErrorRetry.running = false;
|
||||||
|
u2fPendingTimeout.running = false;
|
||||||
|
root.u2fPending = false;
|
||||||
|
root.u2fState = "";
|
||||||
|
u2f.checkAvail();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,200 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Io
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property string screenName
|
||||||
|
property bool active: false
|
||||||
|
property string videoSource: ""
|
||||||
|
property bool inputEnabled: false
|
||||||
|
property point lastMousePos: Qt.point(-1, -1)
|
||||||
|
property bool mouseInitialized: false
|
||||||
|
property var videoPlayer: null
|
||||||
|
|
||||||
|
signal dismissed
|
||||||
|
|
||||||
|
visible: active
|
||||||
|
z: 1000
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: background
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "black"
|
||||||
|
visible: root.active
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: inputEnableTimer
|
||||||
|
interval: 500
|
||||||
|
onTriggered: root.inputEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: videoPicker
|
||||||
|
property string result: ""
|
||||||
|
property string folder: ""
|
||||||
|
|
||||||
|
command: ["sh", "-c", "find '" + folder + "' -maxdepth 1 -type f \\( " + "-iname '*.mp4' -o -iname '*.mkv' -o -iname '*.webm' -o " + "-iname '*.mov' -o -iname '*.avi' -o -iname '*.m4v' " + "\\) 2>/dev/null | shuf -n1"]
|
||||||
|
|
||||||
|
stdout: SplitParser {
|
||||||
|
onRead: data => {
|
||||||
|
const path = data.trim();
|
||||||
|
if (path) {
|
||||||
|
videoPicker.result = path;
|
||||||
|
root.videoSource = "file://" + path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onExited: exitCode => {
|
||||||
|
if (exitCode !== 0 || !videoPicker.result) {
|
||||||
|
console.warn("VideoScreensaver: no video found in folder");
|
||||||
|
ToastService.showError(I18n.tr("Video Screensaver"), I18n.tr("No video found in folder"));
|
||||||
|
root.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: fileChecker
|
||||||
|
command: ["test", "-d", SettingsData.lockScreenVideoPath]
|
||||||
|
|
||||||
|
onExited: exitCode => {
|
||||||
|
const isDir = exitCode === 0;
|
||||||
|
const videoPath = SettingsData.lockScreenVideoPath;
|
||||||
|
|
||||||
|
if (isDir) {
|
||||||
|
videoPicker.folder = videoPath;
|
||||||
|
videoPicker.running = true;
|
||||||
|
} else if (SettingsData.lockScreenVideoCycling) {
|
||||||
|
const parentFolder = videoPath.substring(0, videoPath.lastIndexOf('/'));
|
||||||
|
videoPicker.folder = parentFolder;
|
||||||
|
videoPicker.running = true;
|
||||||
|
} else {
|
||||||
|
root.videoSource = "file://" + videoPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createVideoPlayer() {
|
||||||
|
if (videoPlayer)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
videoPlayer = Qt.createQmlObject(`
|
||||||
|
import QtQuick
|
||||||
|
import QtMultimedia
|
||||||
|
Video {
|
||||||
|
anchors.fill: parent
|
||||||
|
fillMode: VideoOutput.PreserveAspectCrop
|
||||||
|
loops: MediaPlayer.Infinite
|
||||||
|
volume: 0
|
||||||
|
}
|
||||||
|
`, background, "VideoScreensaver.VideoPlayer");
|
||||||
|
|
||||||
|
videoPlayer.errorOccurred.connect((error, errorString) => {
|
||||||
|
console.warn("VideoScreensaver: playback error:", errorString);
|
||||||
|
ToastService.showError(I18n.tr("Video Screensaver"), I18n.tr("Playback error: ") + errorString);
|
||||||
|
root.dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("VideoScreensaver: Failed to create video player:", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function destroyVideoPlayer() {
|
||||||
|
if (videoPlayer) {
|
||||||
|
videoPlayer.stop();
|
||||||
|
videoPlayer.destroy();
|
||||||
|
videoPlayer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
if (!SettingsData.lockScreenVideoEnabled || !SettingsData.lockScreenVideoPath)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!MultimediaService.available) {
|
||||||
|
ToastService.showError(I18n.tr("Video Screensaver"), I18n.tr("QtMultimedia is not available"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!createVideoPlayer())
|
||||||
|
return;
|
||||||
|
|
||||||
|
videoPicker.result = "";
|
||||||
|
videoPicker.folder = "";
|
||||||
|
inputEnabled = false;
|
||||||
|
mouseInitialized = false;
|
||||||
|
lastMousePos = Qt.point(-1, -1);
|
||||||
|
active = true;
|
||||||
|
inputEnableTimer.start();
|
||||||
|
fileChecker.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dismiss() {
|
||||||
|
if (!active)
|
||||||
|
return;
|
||||||
|
destroyVideoPlayer();
|
||||||
|
inputEnabled = false;
|
||||||
|
active = false;
|
||||||
|
videoSource = "";
|
||||||
|
dismissed();
|
||||||
|
}
|
||||||
|
|
||||||
|
onVideoSourceChanged: {
|
||||||
|
if (videoSource && active && videoPlayer) {
|
||||||
|
videoPlayer.source = videoSource;
|
||||||
|
videoPlayer.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: root.active && root.inputEnabled
|
||||||
|
hoverEnabled: true
|
||||||
|
propagateComposedEvents: false
|
||||||
|
|
||||||
|
onPositionChanged: mouse => {
|
||||||
|
if (!root.mouseInitialized) {
|
||||||
|
root.lastMousePos = Qt.point(mouse.x, mouse.y);
|
||||||
|
root.mouseInitialized = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var dx = Math.abs(mouse.x - root.lastMousePos.x);
|
||||||
|
var dy = Math.abs(mouse.y - root.lastMousePos.y);
|
||||||
|
if (dx > 5 || dy > 5) {
|
||||||
|
root.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onClicked: root.dismiss()
|
||||||
|
onPressed: root.dismiss()
|
||||||
|
onWheel: root.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: IdleService
|
||||||
|
|
||||||
|
function onLockRequested() {
|
||||||
|
if (SettingsData.lockScreenVideoEnabled && !root.active) {
|
||||||
|
root.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onFadeToLockRequested() {
|
||||||
|
if (SettingsData.lockScreenVideoEnabled && !root.active) {
|
||||||
|
IdleService.cancelFadeToLock();
|
||||||
|
root.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,51 +34,51 @@ Rectangle {
|
|||||||
|
|
||||||
readonly property var timeoutOptions: [
|
readonly property var timeoutOptions: [
|
||||||
{
|
{
|
||||||
"text": "Never",
|
"text": I18n.tr("Never"),
|
||||||
"value": 0
|
"value": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "1 second",
|
"text": I18n.tr("1 second"),
|
||||||
"value": 1000
|
"value": 1000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "3 seconds",
|
"text": I18n.tr("3 seconds"),
|
||||||
"value": 3000
|
"value": 3000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "5 seconds",
|
"text": I18n.tr("5 seconds"),
|
||||||
"value": 5000
|
"value": 5000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "8 seconds",
|
"text": I18n.tr("8 seconds"),
|
||||||
"value": 8000
|
"value": 8000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "10 seconds",
|
"text": I18n.tr("10 seconds"),
|
||||||
"value": 10000
|
"value": 10000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "15 seconds",
|
"text": I18n.tr("15 seconds"),
|
||||||
"value": 15000
|
"value": 15000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "30 seconds",
|
"text": I18n.tr("30 seconds"),
|
||||||
"value": 30000
|
"value": 30000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "1 minute",
|
"text": I18n.tr("1 minute"),
|
||||||
"value": 60000
|
"value": 60000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "2 minutes",
|
"text": I18n.tr("2 minutes"),
|
||||||
"value": 120000
|
"value": 120000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "5 minutes",
|
"text": I18n.tr("5 minutes"),
|
||||||
"value": 300000
|
"value": 300000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "10 minutes",
|
"text": I18n.tr("10 minutes"),
|
||||||
"value": 600000
|
"value": 600000
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -351,6 +351,7 @@ Item {
|
|||||||
Loader {
|
Loader {
|
||||||
id: contentLoader
|
id: contentLoader
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
active: root.widgetEnabled && root.activeComponent !== null
|
||||||
sourceComponent: root.activeComponent
|
sourceComponent: root.activeComponent
|
||||||
|
|
||||||
function reloadComponent() {
|
function reloadComponent() {
|
||||||
|
|||||||
@@ -305,7 +305,9 @@ Item {
|
|||||||
const prefs = cfg?.screenPreferences || ["all"];
|
const prefs = cfg?.screenPreferences || ["all"];
|
||||||
if (prefs.includes("all") || (typeof prefs[0] === "string" && prefs[0] === "all"))
|
if (prefs.includes("all") || (typeof prefs[0] === "string" && prefs[0] === "all"))
|
||||||
return I18n.tr("All displays");
|
return I18n.tr("All displays");
|
||||||
return I18n.tr("%1 display(s)").replace("%1", prefs.length);
|
return prefs.length === 1
|
||||||
|
? I18n.tr("%1 display").arg(prefs.length)
|
||||||
|
: I18n.tr("%1 displays").arg(prefs.length);
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
|
|||||||
@@ -344,7 +344,11 @@ Item {
|
|||||||
return I18n.tr("%1 exists but is not included in config. Custom keybinds will not work until this is fixed.").arg(bindsFile);
|
return I18n.tr("%1 exists but is not included in config. Custom keybinds will not work until this is fixed.").arg(bindsFile);
|
||||||
if (warningBox.showWarning) {
|
if (warningBox.showWarning) {
|
||||||
const count = warningBox.status.overriddenBy;
|
const count = warningBox.status.overriddenBy;
|
||||||
return I18n.tr("%1 DMS bind(s) may be overridden by config binds that come after the include.").arg(count);
|
return I18n.ntr(
|
||||||
|
"%1 DMS bind may be overridden by config binds that come after the include.",
|
||||||
|
"%1 DMS binds may be overridden by config binds that come after the include.",
|
||||||
|
count
|
||||||
|
).arg(count);
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1170,7 +1170,7 @@ Item {
|
|||||||
spacing: 2
|
spacing: 2
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: modelData.name || "Unknown App"
|
text: modelData.name || I18n.tr("Unknown App")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -1179,7 +1179,7 @@ Item {
|
|||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
if (!modelData.lastUsed)
|
if (!modelData.lastUsed)
|
||||||
return "Never used";
|
return I18n.tr("Never used");
|
||||||
var date = new Date(modelData.lastUsed);
|
var date = new Date(modelData.lastUsed);
|
||||||
var now = new Date();
|
var now = new Date();
|
||||||
var diffMs = now - date;
|
var diffMs = now - date;
|
||||||
@@ -1189,11 +1189,17 @@ Item {
|
|||||||
if (diffMins < 1)
|
if (diffMins < 1)
|
||||||
return I18n.tr("Last launched just now");
|
return I18n.tr("Last launched just now");
|
||||||
if (diffMins < 60)
|
if (diffMins < 60)
|
||||||
return I18n.tr("Last launched %1 minute%2 ago").arg(diffMins).arg(diffMins === 1 ? "" : "s");
|
return diffMins === 1
|
||||||
|
? I18n.tr("Last launched %1 minute ago").arg(diffMins)
|
||||||
|
: I18n.tr("Last launched %1 minutes ago").arg(diffMins);
|
||||||
if (diffHours < 24)
|
if (diffHours < 24)
|
||||||
return I18n.tr("Last launched %1 hour%2 ago").arg(diffHours).arg(diffHours === 1 ? "" : "s");
|
return diffHours === 1
|
||||||
|
? I18n.tr("Last launched %1 hour ago").arg(diffHours)
|
||||||
|
: I18n.tr("Last launched %1 hours ago").arg(diffHours);
|
||||||
if (diffDays < 7)
|
if (diffDays < 7)
|
||||||
return I18n.tr("Last launched %1 day%2 ago").arg(diffDays).arg(diffDays === 1 ? "" : "s");
|
return diffDays === 1
|
||||||
|
? I18n.tr("Last launched %1 day ago").arg(diffDays)
|
||||||
|
: I18n.tr("Last launched %1 days ago").arg(diffDays);
|
||||||
return I18n.tr("Last launched %1").arg(date.toLocaleDateString());
|
return I18n.tr("Last launched %1").arg(date.toLocaleDateString());
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
import qs.Modules.Settings.Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: localeTab
|
||||||
|
|
||||||
|
readonly property string _systemDefaultLabel: I18n.tr("System Default")
|
||||||
|
|
||||||
|
function capitalizeNativeLanguageName(localeCode) {
|
||||||
|
if (I18n.presentLocales[localeCode] == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const nativeName = I18n.presentLocales[localeCode].nativeLanguageName;
|
||||||
|
return nativeName[0].toUpperCase() + nativeName.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _displayValue() {
|
||||||
|
if (!SessionData.locale) return _systemDefaultLabel;
|
||||||
|
return capitalizeNativeLanguageName(SessionData.locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
DankFlickable {
|
||||||
|
anchors.fill: parent
|
||||||
|
clip: true
|
||||||
|
contentHeight: mainColumn.height + Theme.spacingXL
|
||||||
|
contentWidth: width
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: mainColumn
|
||||||
|
topPadding: 4
|
||||||
|
width: Math.min(550, parent.width - Theme.spacingL * 2)
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: Theme.spacingXL
|
||||||
|
|
||||||
|
SettingsCard {
|
||||||
|
tab: "locale"
|
||||||
|
tags: ["locale", "language", "country"]
|
||||||
|
title: I18n.tr("Locale Settings")
|
||||||
|
iconName: "language"
|
||||||
|
|
||||||
|
SettingsDropdownRow {
|
||||||
|
id: localeDropdown
|
||||||
|
tab: "locale"
|
||||||
|
tags: ["locale", "language", "country"]
|
||||||
|
settingKey: "locale"
|
||||||
|
text: I18n.tr("Current Locale")
|
||||||
|
description: I18n.tr("Change the locale used by the DMS interface.")
|
||||||
|
options: [localeTab._systemDefaultLabel].concat(Object.keys(I18n.presentLocales).map(localeTab.capitalizeNativeLanguageName))
|
||||||
|
enableFuzzySearch: true
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
currentValue = localeTab._displayValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
onValueChanged: value => {
|
||||||
|
if (value === localeTab._systemDefaultLabel) {
|
||||||
|
SessionData.set("locale", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let code of Object.keys(I18n.presentLocales)) {
|
||||||
|
if (localeTab.capitalizeNativeLanguageName(code) === value) {
|
||||||
|
SessionData.set("locale", code);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modals.FileBrowser
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
import qs.Modules.Settings.Widgets
|
import qs.Modules.Settings.Widgets
|
||||||
@@ -8,6 +9,16 @@ import qs.Modules.Settings.Widgets
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
FileBrowserModal {
|
||||||
|
id: videoBrowserModal
|
||||||
|
browserTitle: I18n.tr("Select Video or Folder")
|
||||||
|
browserIcon: "movie"
|
||||||
|
browserType: "video"
|
||||||
|
showHiddenFiles: false
|
||||||
|
fileExtensions: ["*.mp4", "*.mkv", "*.webm", "*.mov", "*.avi", "*.m4v"]
|
||||||
|
onFileSelected: path => SettingsData.set("lockScreenVideoPath", path)
|
||||||
|
}
|
||||||
|
|
||||||
DankFlickable {
|
DankFlickable {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
clip: true
|
clip: true
|
||||||
@@ -161,11 +172,120 @@ Item {
|
|||||||
settingKey: "enableFprint"
|
settingKey: "enableFprint"
|
||||||
tags: ["lock", "screen", "fingerprint", "authentication", "biometric", "fprint"]
|
tags: ["lock", "screen", "fingerprint", "authentication", "biometric", "fprint"]
|
||||||
text: I18n.tr("Enable fingerprint authentication")
|
text: I18n.tr("Enable fingerprint authentication")
|
||||||
description: I18n.tr("Use fingerprint reader for lock screen authentication (requires enrolled fingerprints)")
|
description: SettingsData.fprintdAvailable ? I18n.tr("Use fingerprint reader for lock screen authentication (requires enrolled fingerprints)") : I18n.tr("Not enrolled", "fingerprint not detected status")
|
||||||
|
descriptionColor: SettingsData.fprintdAvailable ? Theme.surfaceVariantText : Theme.warning
|
||||||
checked: SettingsData.enableFprint
|
checked: SettingsData.enableFprint
|
||||||
visible: SettingsData.fprintdAvailable
|
enabled: SettingsData.fprintdAvailable
|
||||||
onToggled: checked => SettingsData.set("enableFprint", checked)
|
onToggled: checked => SettingsData.set("enableFprint", checked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsToggleRow {
|
||||||
|
settingKey: "enableU2f"
|
||||||
|
tags: ["lock", "screen", "u2f", "yubikey", "security", "key", "fido", "authentication", "hardware"]
|
||||||
|
text: I18n.tr("Enable security key authentication", "Enable FIDO2/U2F hardware security key for lock screen")
|
||||||
|
description: SettingsData.u2fAvailable ? I18n.tr("Use a FIDO2/U2F security key (e.g. YubiKey) for lock screen authentication (requires enrolled keys)", "lock screen U2F security key setting") : I18n.tr("Not enrolled", "security key not detected status")
|
||||||
|
descriptionColor: SettingsData.u2fAvailable ? Theme.surfaceVariantText : Theme.warning
|
||||||
|
checked: SettingsData.enableU2f
|
||||||
|
enabled: SettingsData.u2fAvailable
|
||||||
|
onToggled: checked => SettingsData.set("enableU2f", checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsDropdownRow {
|
||||||
|
settingKey: "u2fMode"
|
||||||
|
tags: ["lock", "screen", "u2f", "yubikey", "security", "key", "mode", "factor", "second"]
|
||||||
|
text: I18n.tr("Security key mode", "lock screen U2F security key mode setting")
|
||||||
|
description: I18n.tr("'Alternative' lets the key unlock on its own. 'Second factor' requires password or fingerprint first, then the key.", "lock screen U2F security key mode setting")
|
||||||
|
visible: SettingsData.u2fAvailable && SettingsData.enableU2f
|
||||||
|
options: [I18n.tr("Alternative (OR)", "U2F mode option: key works as standalone unlock method"), I18n.tr("Second Factor (AND)", "U2F mode option: key required after password or fingerprint")]
|
||||||
|
currentValue: SettingsData.u2fMode === "and" ? I18n.tr("Second Factor (AND)", "U2F mode option: key required after password or fingerprint") : I18n.tr("Alternative (OR)", "U2F mode option: key works as standalone unlock method")
|
||||||
|
onValueChanged: value => {
|
||||||
|
if (value === I18n.tr("Second Factor (AND)", "U2F mode option: key required after password or fingerprint"))
|
||||||
|
SettingsData.set("u2fMode", "and");
|
||||||
|
else
|
||||||
|
SettingsData.set("u2fMode", "or");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsCard {
|
||||||
|
width: parent.width
|
||||||
|
iconName: "movie"
|
||||||
|
title: I18n.tr("Video Screensaver")
|
||||||
|
settingKey: "videoScreensaver"
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
visible: !MultimediaService.available
|
||||||
|
text: I18n.tr("QtMultimedia is not available - video screensaver requires qt multimedia services")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.warning
|
||||||
|
width: parent.width
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsToggleRow {
|
||||||
|
settingKey: "lockScreenVideoEnabled"
|
||||||
|
tags: ["lock", "screen", "video", "screensaver", "animation", "movie"]
|
||||||
|
text: I18n.tr("Enable Video Screensaver")
|
||||||
|
description: I18n.tr("Play a video when the screen locks.")
|
||||||
|
enabled: MultimediaService.available
|
||||||
|
checked: SettingsData.lockScreenVideoEnabled
|
||||||
|
onToggled: checked => SettingsData.set("lockScreenVideoEnabled", checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
visible: SettingsData.lockScreenVideoEnabled && MultimediaService.available
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Video Path")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Path to a video file or folder containing videos")
|
||||||
|
font.pixelSize: Theme.fontSizeXSmall
|
||||||
|
color: Theme.outlineVariant
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: videoPathField
|
||||||
|
width: parent.width - browseVideoButton.width - Theme.spacingS
|
||||||
|
placeholderText: I18n.tr("/path/to/videos")
|
||||||
|
text: SettingsData.lockScreenVideoPath
|
||||||
|
backgroundColor: Theme.surfaceContainerHighest
|
||||||
|
onTextChanged: {
|
||||||
|
if (text !== SettingsData.lockScreenVideoPath) {
|
||||||
|
SettingsData.set("lockScreenVideoPath", text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankButton {
|
||||||
|
id: browseVideoButton
|
||||||
|
text: I18n.tr("Browse")
|
||||||
|
onClicked: videoBrowserModal.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsToggleRow {
|
||||||
|
settingKey: "lockScreenVideoCycling"
|
||||||
|
tags: ["lock", "screen", "video", "screensaver", "cycling", "random", "shuffle"]
|
||||||
|
text: I18n.tr("Automatic Cycling")
|
||||||
|
description: I18n.tr("Pick a different random video each time from the same folder")
|
||||||
|
visible: SettingsData.lockScreenVideoEnabled && MultimediaService.available
|
||||||
|
enabled: MultimediaService.available
|
||||||
|
checked: SettingsData.lockScreenVideoCycling
|
||||||
|
onToggled: checked => SettingsData.set("lockScreenVideoCycling", checked)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsCard {
|
SettingsCard {
|
||||||
|
|||||||
@@ -340,7 +340,9 @@ Item {
|
|||||||
if (devices.length === 0)
|
if (devices.length === 0)
|
||||||
return I18n.tr("No adapters");
|
return I18n.tr("No adapters");
|
||||||
if (connected === 0)
|
if (connected === 0)
|
||||||
return I18n.tr("%1 adapter(s), none connected").arg(devices.length);
|
return devices.length === 1
|
||||||
|
? I18n.tr("%1 adapter, none connected").arg(devices.length)
|
||||||
|
: I18n.tr("%1 adapters, none connected").arg(devices.length);
|
||||||
return I18n.tr("%1 connected").arg(connected);
|
return I18n.tr("%1 connected").arg(connected);
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
@@ -1281,6 +1283,15 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
iconName: "qr_code"
|
||||||
|
buttonSize: 28
|
||||||
|
visible: modelData.secured && modelData.saved
|
||||||
|
onClicked: {
|
||||||
|
PopoutService.showWifiQRCodeModal(modelData.ssid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DankActionButton {
|
DankActionButton {
|
||||||
iconName: isPinned ? "push_pin" : "push_pin"
|
iconName: isPinned ? "push_pin" : "push_pin"
|
||||||
buttonSize: 28
|
buttonSize: 28
|
||||||
|
|||||||
@@ -548,7 +548,7 @@ Item {
|
|||||||
const count = CupsService.printerNames.length;
|
const count = CupsService.printerNames.length;
|
||||||
if (count === 0)
|
if (count === 0)
|
||||||
return I18n.tr("No printers configured");
|
return I18n.tr("No printers configured");
|
||||||
return I18n.tr("%1 printer(s)").arg(count);
|
return I18n.ntr("%1 printer", "%1 printers", count).arg(count);
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
@@ -698,7 +698,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: I18n.tr("%1 job(s)").arg(printerData?.jobs?.length ?? 0)
|
text: I18n.ntr("%1 job", "%1 jobs", printerData?.jobs?.length ?? 0).arg(printerData?.jobs?.length ?? 0)
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
visible: (printerData?.jobs?.length ?? 0) > 0
|
visible: (printerData?.jobs?.length ?? 0) > 0
|
||||||
@@ -1245,7 +1245,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: I18n.tr("%1 class(es)").arg(CupsService.printerClasses.length)
|
text: I18n.ntr("%1 class", "%1 classes", CupsService.printerClasses.length).arg(CupsService.printerClasses.length)
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -1310,7 +1310,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: I18n.tr("%1 printer(s)").arg(modelData.members?.length ?? 0)
|
text: I18n.ntr("%1 printer", "%1 printers", modelData.members?.length ?? 0).arg(modelData.members?.length ?? 0)
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1908,6 +1908,7 @@ Item {
|
|||||||
tags: ["modal", "darken", "background", "overlay"]
|
tags: ["modal", "darken", "background", "overlay"]
|
||||||
title: I18n.tr("Modal Background")
|
title: I18n.tr("Modal Background")
|
||||||
settingKey: "modalBackground"
|
settingKey: "modalBackground"
|
||||||
|
iconName: "layers"
|
||||||
|
|
||||||
SettingsToggleRow {
|
SettingsToggleRow {
|
||||||
tab: "theme"
|
tab: "theme"
|
||||||
@@ -1925,7 +1926,7 @@ Item {
|
|||||||
tags: ["applications", "portal", "dark", "terminal"]
|
tags: ["applications", "portal", "dark", "terminal"]
|
||||||
title: I18n.tr("Applications")
|
title: I18n.tr("Applications")
|
||||||
settingKey: "applications"
|
settingKey: "applications"
|
||||||
iconName: "terminal"
|
iconName: "apps"
|
||||||
|
|
||||||
SettingsToggleRow {
|
SettingsToggleRow {
|
||||||
tab: "theme"
|
tab: "theme"
|
||||||
@@ -2452,6 +2453,7 @@ Item {
|
|||||||
tags: ["icon", "theme", "system"]
|
tags: ["icon", "theme", "system"]
|
||||||
title: I18n.tr("Icon Theme")
|
title: I18n.tr("Icon Theme")
|
||||||
settingKey: "iconTheme"
|
settingKey: "iconTheme"
|
||||||
|
iconName: "interests"
|
||||||
|
|
||||||
SettingsDropdownRow {
|
SettingsDropdownRow {
|
||||||
tab: "theme"
|
tab: "theme"
|
||||||
@@ -2478,7 +2480,7 @@ Item {
|
|||||||
tags: ["system", "app", "theming", "gtk", "qt"]
|
tags: ["system", "app", "theming", "gtk", "qt"]
|
||||||
title: I18n.tr("System App Theming")
|
title: I18n.tr("System App Theming")
|
||||||
settingKey: "systemAppTheming"
|
settingKey: "systemAppTheming"
|
||||||
iconName: "extension"
|
iconName: "brush"
|
||||||
visible: Theme.matugenAvailable
|
visible: Theme.matugenAvailable
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ Item {
|
|||||||
tags: ["date", "format", "topbar"]
|
tags: ["date", "format", "topbar"]
|
||||||
settingKey: "clockDateFormat"
|
settingKey: "clockDateFormat"
|
||||||
text: I18n.tr("Top Bar Format")
|
text: I18n.tr("Top Bar Format")
|
||||||
description: "Preview: " + (SettingsData.clockDateFormat ? new Date().toLocaleDateString(Qt.locale(), SettingsData.clockDateFormat) : new Date().toLocaleDateString(Qt.locale(), "ddd d"))
|
description: "Preview: " + (SettingsData.clockDateFormat ? new Date().toLocaleDateString(I18n.locale(), SettingsData.clockDateFormat) : new Date().toLocaleDateString(I18n.locale(), "ddd d"))
|
||||||
options: [I18n.tr("System Default", "date format option"), I18n.tr("Day Date", "date format option"), I18n.tr("Day Month Date", "date format option"), I18n.tr("Month Date", "date format option"), I18n.tr("Numeric (M/D)", "date format option"), I18n.tr("Numeric (D/M)", "date format option"), I18n.tr("Full with Year", "date format option"), I18n.tr("ISO Date", "date format option"), I18n.tr("Full Day & Month", "date format option"), I18n.tr("Custom...", "date format option")]
|
options: [I18n.tr("System Default", "date format option"), I18n.tr("Day Date", "date format option"), I18n.tr("Day Month Date", "date format option"), I18n.tr("Month Date", "date format option"), I18n.tr("Numeric (M/D)", "date format option"), I18n.tr("Numeric (D/M)", "date format option"), I18n.tr("Full with Year", "date format option"), I18n.tr("ISO Date", "date format option"), I18n.tr("Full Day & Month", "date format option"), I18n.tr("Custom...", "date format option")]
|
||||||
currentValue: {
|
currentValue: {
|
||||||
if (!SettingsData.clockDateFormat || SettingsData.clockDateFormat.length === 0)
|
if (!SettingsData.clockDateFormat || SettingsData.clockDateFormat.length === 0)
|
||||||
@@ -161,7 +161,7 @@ Item {
|
|||||||
tags: ["date", "format", "lock", "screen"]
|
tags: ["date", "format", "lock", "screen"]
|
||||||
settingKey: "lockDateFormat"
|
settingKey: "lockDateFormat"
|
||||||
text: I18n.tr("Lock Screen Format")
|
text: I18n.tr("Lock Screen Format")
|
||||||
description: "Preview: " + (SettingsData.lockDateFormat ? new Date().toLocaleDateString(Qt.locale(), SettingsData.lockDateFormat) : new Date().toLocaleDateString(Qt.locale(), Locale.LongFormat))
|
description: "Preview: " + (SettingsData.lockDateFormat ? new Date().toLocaleDateString(I18n.locale(), SettingsData.lockDateFormat) : new Date().toLocaleDateString(I18n.locale(), Locale.LongFormat))
|
||||||
options: [I18n.tr("System Default", "date format option"), I18n.tr("Day Date", "date format option"), I18n.tr("Day Month Date", "date format option"), I18n.tr("Month Date", "date format option"), I18n.tr("Numeric (M/D)", "date format option"), I18n.tr("Numeric (D/M)", "date format option"), I18n.tr("Full with Year", "date format option"), I18n.tr("ISO Date", "date format option"), I18n.tr("Full Day & Month", "date format option"), I18n.tr("Custom...", "date format option")]
|
options: [I18n.tr("System Default", "date format option"), I18n.tr("Day Date", "date format option"), I18n.tr("Day Month Date", "date format option"), I18n.tr("Month Date", "date format option"), I18n.tr("Numeric (M/D)", "date format option"), I18n.tr("Numeric (D/M)", "date format option"), I18n.tr("Full with Year", "date format option"), I18n.tr("ISO Date", "date format option"), I18n.tr("Full Day & Month", "date format option"), I18n.tr("Custom...", "date format option")]
|
||||||
currentValue: {
|
currentValue: {
|
||||||
if (!SettingsData.lockDateFormat || SettingsData.lockDateFormat.length === 0)
|
if (!SettingsData.lockDateFormat || SettingsData.lockDateFormat.length === 0)
|
||||||
|
|||||||
@@ -216,6 +216,15 @@ Rectangle {
|
|||||||
onToggled: checked => root.updateConfig("showMemoryGraph", checked)
|
onToggled: checked => root.updateConfig("showMemoryGraph", checked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
width: parent.width - Theme.spacingM * 2
|
||||||
|
x: Theme.spacingM
|
||||||
|
text: I18n.tr("Show Memory in GB")
|
||||||
|
visible: root.cfg.showMemory
|
||||||
|
checked: root.cfg.showInGb ?? false
|
||||||
|
onToggled: checked => root.updateConfig("showInGb", checked)
|
||||||
|
}
|
||||||
|
|
||||||
SettingsDivider {}
|
SettingsDivider {}
|
||||||
|
|
||||||
DankToggle {
|
DankToggle {
|
||||||
|
|||||||
@@ -398,10 +398,14 @@ Item {
|
|||||||
widgetObj.runningAppsCurrentWorkspace = SettingsData.runningAppsCurrentWorkspace;
|
widgetObj.runningAppsCurrentWorkspace = SettingsData.runningAppsCurrentWorkspace;
|
||||||
widgetObj.runningAppsCurrentMonitor = false;
|
widgetObj.runningAppsCurrentMonitor = false;
|
||||||
}
|
}
|
||||||
if (widgetId === "diskUsage")
|
if (widgetId === "diskUsage") {
|
||||||
widgetObj.mountPath = "/";
|
widgetObj.mountPath = "/";
|
||||||
|
widgetObj.diskUsageMode = 0;
|
||||||
|
}
|
||||||
if (widgetId === "cpuUsage" || widgetId === "memUsage" || widgetId === "cpuTemp" || widgetId === "gpuTemp")
|
if (widgetId === "cpuUsage" || widgetId === "memUsage" || widgetId === "cpuTemp" || widgetId === "gpuTemp")
|
||||||
widgetObj.minimumWidth = true;
|
widgetObj.minimumWidth = true;
|
||||||
|
if (widgetId === "memUsage")
|
||||||
|
widgetObj.showInGb = false;
|
||||||
|
|
||||||
var widgets = getWidgetsForSection(targetSection).slice();
|
var widgets = getWidgetsForSection(targetSection).slice();
|
||||||
widgets.push(widgetObj);
|
widgets.push(widgetObj);
|
||||||
@@ -425,7 +429,7 @@ Item {
|
|||||||
"id": widget.id,
|
"id": widget.id,
|
||||||
"enabled": widget.enabled
|
"enabled": widget.enabled
|
||||||
};
|
};
|
||||||
var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "minimumWidth", "showSwap", "mediaSize", "clockCompactMode", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge"];
|
var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "diskUsageMode", "minimumWidth", "showSwap", "showInGb", "mediaSize", "clockCompactMode", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge"];
|
||||||
for (var i = 0; i < keys.length; i++) {
|
for (var i = 0; i < keys.length; i++) {
|
||||||
if (widget[keys[i]] !== undefined)
|
if (widget[keys[i]] !== undefined)
|
||||||
result[keys[i]] = widget[keys[i]];
|
result[keys[i]] = widget[keys[i]];
|
||||||
@@ -536,6 +540,30 @@ Item {
|
|||||||
setWidgetsForSection(sectionId, widgets);
|
setWidgetsForSection(sectionId, widgets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleShowInGbChanged(sectionId, widgetIndex, enabled) {
|
||||||
|
var widgets = getWidgetsForSection(sectionId).slice();
|
||||||
|
if (widgetIndex < 0 || widgetIndex >= widgets.length) {
|
||||||
|
setWidgetsForSection(sectionId, widgets);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var newWidget = cloneWidgetData(widgets[widgetIndex]);
|
||||||
|
newWidget.showInGb = enabled;
|
||||||
|
widgets[widgetIndex] = newWidget;
|
||||||
|
setWidgetsForSection(sectionId, widgets);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDiskUsageModeChanged(sectionId, widgetIndex, mode) {
|
||||||
|
var widgets = getWidgetsForSection(sectionId).slice();
|
||||||
|
if (widgetIndex < 0 || widgetIndex >= widgets.length) {
|
||||||
|
setWidgetsForSection(sectionId, widgets);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var newWidget = cloneWidgetData(widgets[widgetIndex]);
|
||||||
|
newWidget.diskUsageMode = mode;
|
||||||
|
widgets[widgetIndex] = newWidget;
|
||||||
|
setWidgetsForSection(sectionId, widgets);
|
||||||
|
}
|
||||||
|
|
||||||
function handleOverflowSettingChanged(sectionId, widgetIndex, settingName, value) {
|
function handleOverflowSettingChanged(sectionId, widgetIndex, settingName, value) {
|
||||||
var widgets = getWidgetsForSection(sectionId).slice();
|
var widgets = getWidgetsForSection(sectionId).slice();
|
||||||
if (widgetIndex < 0 || widgetIndex >= widgets.length) {
|
if (widgetIndex < 0 || widgetIndex >= widgets.length) {
|
||||||
@@ -601,6 +629,8 @@ Item {
|
|||||||
item.pciId = widget.pciId;
|
item.pciId = widget.pciId;
|
||||||
if (widget.mountPath !== undefined)
|
if (widget.mountPath !== undefined)
|
||||||
item.mountPath = widget.mountPath;
|
item.mountPath = widget.mountPath;
|
||||||
|
if (widget.diskUsageMode !== undefined)
|
||||||
|
item.diskUsageMode = widget.diskUsageMode;
|
||||||
if (widget.showNetworkIcon !== undefined)
|
if (widget.showNetworkIcon !== undefined)
|
||||||
item.showNetworkIcon = widget.showNetworkIcon;
|
item.showNetworkIcon = widget.showNetworkIcon;
|
||||||
if (widget.showBluetoothIcon !== undefined)
|
if (widget.showBluetoothIcon !== undefined)
|
||||||
@@ -629,6 +659,8 @@ Item {
|
|||||||
item.minimumWidth = widget.minimumWidth;
|
item.minimumWidth = widget.minimumWidth;
|
||||||
if (widget.showSwap !== undefined)
|
if (widget.showSwap !== undefined)
|
||||||
item.showSwap = widget.showSwap;
|
item.showSwap = widget.showSwap;
|
||||||
|
if (widget.showInGb !== undefined)
|
||||||
|
item.showInGb = widget.showInGb;
|
||||||
if (widget.mediaSize !== undefined)
|
if (widget.mediaSize !== undefined)
|
||||||
item.mediaSize = widget.mediaSize;
|
item.mediaSize = widget.mediaSize;
|
||||||
if (widget.clockCompactMode !== undefined)
|
if (widget.clockCompactMode !== undefined)
|
||||||
@@ -925,6 +957,12 @@ Item {
|
|||||||
onShowSwapChanged: (sectionId, index, enabled) => {
|
onShowSwapChanged: (sectionId, index, enabled) => {
|
||||||
widgetsTab.handleShowSwapChanged(sectionId, index, enabled);
|
widgetsTab.handleShowSwapChanged(sectionId, index, enabled);
|
||||||
}
|
}
|
||||||
|
onShowInGbChanged: (sectionId, index, enabled) => {
|
||||||
|
widgetsTab.handleShowInGbChanged(sectionId, index, enabled);
|
||||||
|
}
|
||||||
|
onDiskUsageModeChanged: (sectionId, widgetIndex, mode) => {
|
||||||
|
widgetsTab.handleDiskUsageModeChanged(sectionId, widgetIndex, mode);
|
||||||
|
}
|
||||||
onCompactModeChanged: (widgetId, value) => {
|
onCompactModeChanged: (widgetId, value) => {
|
||||||
widgetsTab.handleCompactModeChanged(sectionId, widgetId, value);
|
widgetsTab.handleCompactModeChanged(sectionId, widgetId, value);
|
||||||
}
|
}
|
||||||
@@ -983,6 +1021,12 @@ Item {
|
|||||||
onShowSwapChanged: (sectionId, index, enabled) => {
|
onShowSwapChanged: (sectionId, index, enabled) => {
|
||||||
widgetsTab.handleShowSwapChanged(sectionId, index, enabled);
|
widgetsTab.handleShowSwapChanged(sectionId, index, enabled);
|
||||||
}
|
}
|
||||||
|
onShowInGbChanged: (sectionId, index, enabled) => {
|
||||||
|
widgetsTab.handleShowInGbChanged(sectionId, index, enabled);
|
||||||
|
}
|
||||||
|
onDiskUsageModeChanged: (sectionId, widgetIndex, mode) => {
|
||||||
|
widgetsTab.handleDiskUsageModeChanged(sectionId, widgetIndex, mode);
|
||||||
|
}
|
||||||
onCompactModeChanged: (widgetId, value) => {
|
onCompactModeChanged: (widgetId, value) => {
|
||||||
widgetsTab.handleCompactModeChanged(sectionId, widgetId, value);
|
widgetsTab.handleCompactModeChanged(sectionId, widgetId, value);
|
||||||
}
|
}
|
||||||
@@ -1041,6 +1085,12 @@ Item {
|
|||||||
onShowSwapChanged: (sectionId, index, enabled) => {
|
onShowSwapChanged: (sectionId, index, enabled) => {
|
||||||
widgetsTab.handleShowSwapChanged(sectionId, index, enabled);
|
widgetsTab.handleShowSwapChanged(sectionId, index, enabled);
|
||||||
}
|
}
|
||||||
|
onShowInGbChanged: (sectionId, index, enabled) => {
|
||||||
|
widgetsTab.handleShowInGbChanged(sectionId, index, enabled);
|
||||||
|
}
|
||||||
|
onDiskUsageModeChanged: (sectionId, widgetIndex, mode) => {
|
||||||
|
widgetsTab.handleDiskUsageModeChanged(sectionId, widgetIndex, mode);
|
||||||
|
}
|
||||||
onCompactModeChanged: (widgetId, value) => {
|
onCompactModeChanged: (widgetId, value) => {
|
||||||
widgetsTab.handleCompactModeChanged(sectionId, widgetId, value);
|
widgetsTab.handleCompactModeChanged(sectionId, widgetId, value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ Column {
|
|||||||
signal privacySettingChanged(string sectionId, int widgetIndex, string settingName, bool value)
|
signal privacySettingChanged(string sectionId, int widgetIndex, string settingName, bool value)
|
||||||
signal minimumWidthChanged(string sectionId, int widgetIndex, bool enabled)
|
signal minimumWidthChanged(string sectionId, int widgetIndex, bool enabled)
|
||||||
signal showSwapChanged(string sectionId, int widgetIndex, bool enabled)
|
signal showSwapChanged(string sectionId, int widgetIndex, bool enabled)
|
||||||
|
signal showInGbChanged(string sectionId, int widgetIndex, bool enabled)
|
||||||
|
signal diskUsageModeChanged(string sectionId, int widgetIndex, int mode)
|
||||||
signal overflowSettingChanged(string sectionId, int widgetIndex, string settingName, var value)
|
signal overflowSettingChanged(string sectionId, int widgetIndex, string settingName, var value)
|
||||||
|
|
||||||
function cloneWidgetData(widget) {
|
function cloneWidgetData(widget) {
|
||||||
@@ -37,7 +39,7 @@ Column {
|
|||||||
"id": widget.id,
|
"id": widget.id,
|
||||||
"enabled": widget.enabled
|
"enabled": widget.enabled
|
||||||
};
|
};
|
||||||
var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "minimumWidth", "showSwap", "mediaSize", "clockCompactMode", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge"];
|
var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "diskUsageMode", "minimumWidth", "showSwap", "showInGb", "mediaSize", "clockCompactMode", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge"];
|
||||||
for (var i = 0; i < keys.length; i++) {
|
for (var i = 0; i < keys.length; i++) {
|
||||||
if (widget[keys[i]] !== undefined)
|
if (widget[keys[i]] !== undefined)
|
||||||
result[keys[i]] = widget[keys[i]];
|
result[keys[i]] = widget[keys[i]];
|
||||||
@@ -67,53 +69,6 @@ Column {
|
|||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
|
||||||
height: 1
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
spacing: Theme.spacingXS
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
visible: root.sectionId === "center"
|
|
||||||
|
|
||||||
DankActionButton {
|
|
||||||
id: indexCenterButton
|
|
||||||
buttonSize: 28
|
|
||||||
iconName: "format_list_numbered"
|
|
||||||
iconSize: 16
|
|
||||||
iconColor: SettingsData.centeringMode === "index" ? Theme.primary : Theme.outline
|
|
||||||
onClicked: {
|
|
||||||
console.log("Centering mode changed to: index");
|
|
||||||
SettingsData.set("centeringMode", "index");
|
|
||||||
}
|
|
||||||
onEntered: {
|
|
||||||
sharedTooltip.show("Index Centering", indexCenterButton, 0, 0, "bottom");
|
|
||||||
}
|
|
||||||
onExited: {
|
|
||||||
sharedTooltip.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DankActionButton {
|
|
||||||
id: geometricCenterButton
|
|
||||||
buttonSize: 28
|
|
||||||
iconName: "center_focus_weak"
|
|
||||||
iconSize: 16
|
|
||||||
iconColor: SettingsData.centeringMode === "geometric" ? Theme.primary : Theme.outline
|
|
||||||
onClicked: {
|
|
||||||
console.log("Centering mode changed to: geometric");
|
|
||||||
SettingsData.set("centeringMode", "geometric");
|
|
||||||
}
|
|
||||||
onEntered: {
|
|
||||||
sharedTooltip.show("Geometric Centering", geometricCenterButton, 0, 0, "bottom");
|
|
||||||
}
|
|
||||||
onExited: {
|
|
||||||
sharedTooltip.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -135,6 +90,7 @@ Column {
|
|||||||
height: 70
|
height: 70
|
||||||
z: held ? 2 : 1
|
z: held ? 2 : 1
|
||||||
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: itemBackground
|
id: itemBackground
|
||||||
|
|
||||||
@@ -275,6 +231,34 @@ Column {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
id: diskMenuButton
|
||||||
|
visible: modelData.id === "diskUsage"
|
||||||
|
buttonSize: 32
|
||||||
|
iconName: "more_vert"
|
||||||
|
iconSize: 18
|
||||||
|
iconColor: Theme.outline
|
||||||
|
onClicked: {
|
||||||
|
diskUsageContextMenu.widgetData = modelData;
|
||||||
|
diskUsageContextMenu.sectionId = root.sectionId;
|
||||||
|
diskUsageContextMenu.widgetIndex = index;
|
||||||
|
|
||||||
|
var buttonPos = diskMenuButton.mapToItem(root, 0, 0);
|
||||||
|
var xPos = buttonPos.x - diskUsageContextMenu.width - Theme.spacingS;
|
||||||
|
if (xPos < 0)
|
||||||
|
xPos = buttonPos.x + diskMenuButton.width + Theme.spacingS;
|
||||||
|
var yPos = buttonPos.y - diskUsageContextMenu.height / 2 + diskMenuButton.height / 2;
|
||||||
|
if (yPos < 0)
|
||||||
|
yPos = Theme.spacingS;
|
||||||
|
else if (yPos + diskUsageContextMenu.height > root.height)
|
||||||
|
yPos = root.height - diskUsageContextMenu.height - Theme.spacingS;
|
||||||
|
|
||||||
|
diskUsageContextMenu.x = xPos;
|
||||||
|
diskUsageContextMenu.y = yPos;
|
||||||
|
diskUsageContextMenu.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
width: 32
|
width: 32
|
||||||
height: 32
|
height: 32
|
||||||
@@ -354,23 +338,36 @@ Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DankActionButton {
|
DankActionButton {
|
||||||
id: showSwapButton
|
id: memMenuButton
|
||||||
buttonSize: 28
|
|
||||||
visible: modelData.id === "memUsage"
|
visible: modelData.id === "memUsage"
|
||||||
iconName: "swap_horiz"
|
buttonSize: 32
|
||||||
iconSize: 16
|
iconName: "more_vert"
|
||||||
iconColor: (modelData.showSwap !== undefined ? modelData.showSwap : false) ? Theme.primary : Theme.outline
|
iconSize: 18
|
||||||
|
iconColor: Theme.outline
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var currentEnabled = modelData.showSwap !== undefined ? modelData.showSwap : false;
|
memUsageContextMenu.widgetData = modelData;
|
||||||
root.showSwapChanged(root.sectionId, index, !currentEnabled);
|
memUsageContextMenu.sectionId = root.sectionId;
|
||||||
}
|
memUsageContextMenu.widgetIndex = index;
|
||||||
onEntered: {
|
|
||||||
var currentEnabled = modelData.showSwap !== undefined ? modelData.showSwap : false;
|
var buttonPos = memMenuButton.mapToItem(root, 0, 0);
|
||||||
const tooltipText = currentEnabled ? "Hide Swap" : "Show Swap";
|
var popupWidth = memUsageContextMenu.width;
|
||||||
sharedTooltip.show(tooltipText, showSwapButton, 0, 0, "bottom");
|
var popupHeight = memUsageContextMenu.height;
|
||||||
}
|
|
||||||
onExited: {
|
var xPos = buttonPos.x - popupWidth - Theme.spacingS;
|
||||||
sharedTooltip.hide();
|
if (xPos < 0) {
|
||||||
|
xPos = buttonPos.x + memMenuButton.width + Theme.spacingS;
|
||||||
|
}
|
||||||
|
|
||||||
|
var yPos = buttonPos.y - popupHeight / 2 + memMenuButton.height / 2;
|
||||||
|
if (yPos < 0) {
|
||||||
|
yPos = Theme.spacingS;
|
||||||
|
} else if (yPos + popupHeight > root.height) {
|
||||||
|
yPos = root.height - popupHeight - Theme.spacingS;
|
||||||
|
}
|
||||||
|
|
||||||
|
memUsageContextMenu.x = xPos;
|
||||||
|
memUsageContextMenu.y = yPos;
|
||||||
|
memUsageContextMenu.open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -799,6 +796,257 @@ Column {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
id: memUsageContextMenu
|
||||||
|
|
||||||
|
property var widgetData: null
|
||||||
|
property string sectionId: ""
|
||||||
|
property int widgetIndex: -1
|
||||||
|
|
||||||
|
width: 200
|
||||||
|
height: 80
|
||||||
|
padding: 0
|
||||||
|
modal: true
|
||||||
|
focus: true
|
||||||
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: Theme.surfaceContainer
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
|
border.width: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingS
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: swapToggleArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "swap_horiz"
|
||||||
|
size: 16
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Show Swap")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Normal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
id: swapToggle
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: 40
|
||||||
|
height: 20
|
||||||
|
checked: memUsageContextMenu.widgetData?.showSwap ?? false
|
||||||
|
onToggled: {
|
||||||
|
root.showSwapChanged(memUsageContextMenu.sectionId, memUsageContextMenu.widgetIndex, toggled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: swapToggleArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onPressed: {
|
||||||
|
swapToggle.checked = !swapToggle.checked;
|
||||||
|
root.showSwapChanged(memUsageContextMenu.sectionId, memUsageContextMenu.widgetIndex, swapToggle.checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: gbToggleArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "straighten"
|
||||||
|
size: 16
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Show in GB")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Normal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
id: gbToggle
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: 40
|
||||||
|
height: 20
|
||||||
|
checked: memUsageContextMenu.widgetData?.showInGb ?? false
|
||||||
|
onToggled: {
|
||||||
|
root.showInGbChanged(memUsageContextMenu.sectionId, memUsageContextMenu.widgetIndex, toggled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: gbToggleArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onPressed: {
|
||||||
|
gbToggle.checked = !gbToggle.checked;
|
||||||
|
root.showInGbChanged(memUsageContextMenu.sectionId, memUsageContextMenu.widgetIndex, gbToggle.checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
id: diskUsageContextMenu
|
||||||
|
|
||||||
|
property var widgetData: null
|
||||||
|
property string sectionId: ""
|
||||||
|
property int widgetIndex: -1
|
||||||
|
readonly property var currentWidgetData: (widgetIndex >= 0 && widgetIndex < root.items.length) ? root.items[widgetIndex] : widgetData
|
||||||
|
|
||||||
|
width: 240
|
||||||
|
height: diskMenuColumn.implicitHeight + Theme.spacingS * 2
|
||||||
|
padding: 0
|
||||||
|
modal: true
|
||||||
|
focus: true
|
||||||
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: Theme.surfaceContainer
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
|
border.width: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
Column {
|
||||||
|
id: diskMenuColumn
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingS
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: I18n.tr("Disk Usage Display")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: [
|
||||||
|
{ label: I18n.tr("Percentage"), mode: 0, icon: "percent" },
|
||||||
|
{ label: I18n.tr("Total"), mode: 1, icon: "storage" },
|
||||||
|
{ label: I18n.tr("Remaining"), mode: 2, icon: "hourglass_empty" },
|
||||||
|
{ label: I18n.tr("Remaining / Total"), mode: 3, icon: "pie_chart" }
|
||||||
|
]
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
width: diskMenuColumn.width
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: diskOptionArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
|
||||||
|
function isSelected() {
|
||||||
|
return (diskUsageContextMenu.currentWidgetData?.diskUsageMode ?? 0) === modelData.mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: modelData.icon
|
||||||
|
size: 16
|
||||||
|
color: isSelected() ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.label
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: isSelected() ? Theme.primary : Theme.surfaceText
|
||||||
|
font.weight: isSelected() ? Font.Medium : Font.Normal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
name: "check"
|
||||||
|
size: 16
|
||||||
|
color: Theme.primary
|
||||||
|
visible: isSelected()
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: diskOptionArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
root.diskUsageModeChanged(diskUsageContextMenu.sectionId, diskUsageContextMenu.widgetIndex, modelData.mode);
|
||||||
|
diskUsageContextMenu.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
id: controlCenterContextMenu
|
id: controlCenterContextMenu
|
||||||
|
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ DesktopPluginComponent {
|
|||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.bottomMargin: Theme.spacingXS
|
anchors.bottomMargin: Theme.spacingXS
|
||||||
text: systemClock.date?.toLocaleDateString(Qt.locale(), "ddd, MMM d") ?? ""
|
text: systemClock.date?.toLocaleDateString(I18n.locale(), "ddd, MMM d") ?? ""
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -163,7 +163,7 @@ DesktopPluginComponent {
|
|||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.top: timeText.bottom
|
anchors.top: timeText.bottom
|
||||||
anchors.topMargin: Theme.spacingXS
|
anchors.topMargin: Theme.spacingXS
|
||||||
text: systemClock.date?.toLocaleDateString(Qt.locale(), "ddd, MMM d") ?? ""
|
text: systemClock.date?.toLocaleDateString(I18n.locale(), "ddd, MMM d") ?? ""
|
||||||
font.pixelSize: digitalRoot.dateFontSize
|
font.pixelSize: digitalRoot.dateFontSize
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import Quickshell
|
|||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell.Services.Pipewire
|
import Quickshell.Services.Pipewire
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
@@ -14,7 +15,7 @@ Singleton {
|
|||||||
readonly property PwNode sink: Pipewire.defaultAudioSink
|
readonly property PwNode sink: Pipewire.defaultAudioSink
|
||||||
readonly property PwNode source: Pipewire.defaultAudioSource
|
readonly property PwNode source: Pipewire.defaultAudioSource
|
||||||
|
|
||||||
property bool soundsAvailable: false
|
readonly property bool soundsAvailable: MultimediaService.available
|
||||||
property bool gsettingsAvailable: false
|
property bool gsettingsAvailable: false
|
||||||
property var availableSoundThemes: []
|
property var availableSoundThemes: []
|
||||||
property string currentSoundTheme: ""
|
property string currentSoundTheme: ""
|
||||||
@@ -312,24 +313,6 @@ EOFCONFIG
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectSoundsAvailability() {
|
|
||||||
try {
|
|
||||||
const testObj = Qt.createQmlObject(`
|
|
||||||
import QtQuick
|
|
||||||
import QtMultimedia
|
|
||||||
Item {}
|
|
||||||
`, root, "AudioService.TestComponent");
|
|
||||||
if (testObj) {
|
|
||||||
testObj.destroy();
|
|
||||||
}
|
|
||||||
soundsAvailable = true;
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
soundsAvailable = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkGsettings() {
|
function checkGsettings() {
|
||||||
Proc.runCommand("checkGsettings", ["sh", "-c", "gsettings get org.gnome.desktop.sound theme-name 2>/dev/null"], (output, exitCode) => {
|
Proc.runCommand("checkGsettings", ["sh", "-c", "gsettings get org.gnome.desktop.sound theme-name 2>/dev/null"], (output, exitCode) => {
|
||||||
gsettingsAvailable = (exitCode === 0);
|
gsettingsAvailable = (exitCode === 0);
|
||||||
@@ -1028,10 +1011,7 @@ EOFCONFIG
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (!detectSoundsAvailability()) {
|
if (soundsAvailable) {
|
||||||
console.warn("AudioService: QtMultimedia not available - sound effects disabled");
|
|
||||||
} else {
|
|
||||||
console.info("AudioService: Sound effects enabled");
|
|
||||||
checkGsettings();
|
checkGsettings();
|
||||||
Qt.callLater(createSoundPlayers);
|
Qt.callLater(createSoundPlayers);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,12 +157,12 @@ Singleton {
|
|||||||
// Parse start and end dates using detected format
|
// Parse start and end dates using detected format
|
||||||
let startDate, endDate
|
let startDate, endDate
|
||||||
if (event['start-date']) {
|
if (event['start-date']) {
|
||||||
startDate = Date.fromLocaleString(Qt.locale(), event['start-date'], root.khalDateFormat)
|
startDate = Date.fromLocaleString(I18n.locale(), event['start-date'], root.khalDateFormat)
|
||||||
} else {
|
} else {
|
||||||
startDate = new Date()
|
startDate = new Date()
|
||||||
}
|
}
|
||||||
if (event['end-date']) {
|
if (event['end-date']) {
|
||||||
endDate = Date.fromLocaleString(Qt.locale(), event['end-date'], root.khalDateFormat)
|
endDate = Date.fromLocaleString(I18n.locale(), event['end-date'], root.khalDateFormat)
|
||||||
} else {
|
} else {
|
||||||
endDate = new Date(startDate)
|
endDate = new Date(startDate)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Singleton {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property string currentVersion: "1.4"
|
readonly property string currentVersion: "1.4"
|
||||||
readonly property bool changelogEnabled: true
|
readonly property bool changelogEnabled: false
|
||||||
|
|
||||||
readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)) + "/DankMaterialShell"
|
readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)) + "/DankMaterialShell"
|
||||||
readonly property string changelogMarkerPath: configDir + "/.changelog-" + currentVersion
|
readonly property string changelogMarkerPath: configDir + "/.changelog-" + currentVersion
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtCore
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
@@ -13,6 +11,9 @@ Singleton {
|
|||||||
|
|
||||||
property bool dsearchAvailable: false
|
property bool dsearchAvailable: false
|
||||||
property int searchIdCounter: 0
|
property int searchIdCounter: 0
|
||||||
|
property int indexVersion: 0
|
||||||
|
property bool supportsTypeFilter: false
|
||||||
|
property bool versionChecked: false
|
||||||
|
|
||||||
signal searchResultsReceived(var results)
|
signal searchResultsReceived(var results)
|
||||||
signal statsReceived(var stats)
|
signal statsReceived(var stats)
|
||||||
@@ -26,118 +27,157 @@ Singleton {
|
|||||||
stdout: SplitParser {
|
stdout: SplitParser {
|
||||||
onRead: line => {
|
onRead: line => {
|
||||||
if (line && line.trim().length > 0) {
|
if (line && line.trim().length > 0) {
|
||||||
root.dsearchAvailable = true
|
root.dsearchAvailable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onExited: exitCode => {
|
onExited: exitCode => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
root.dsearchAvailable = false
|
root.dsearchAvailable = false;
|
||||||
|
} else {
|
||||||
|
root._checkVersion();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _checkVersion() {
|
||||||
|
Proc.runCommand("dsearch-version", ["dsearch", "version", "--json"], (stdout, exitCode) => {
|
||||||
|
root.versionChecked = true;
|
||||||
|
if (exitCode !== 0)
|
||||||
|
return;
|
||||||
|
const response = JSON.parse(stdout);
|
||||||
|
root.indexVersion = response.index_schema || 0;
|
||||||
|
root.supportsTypeFilter = root.indexVersion >= 2;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function ping(callback) {
|
function ping(callback) {
|
||||||
if (!dsearchAvailable) {
|
if (!dsearchAvailable) {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback({ "error": "dsearch not available" })
|
callback({
|
||||||
|
"error": "dsearch not available"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Proc.runCommand("dsearch-ping", ["dsearch", "ping", "--json"], (stdout, exitCode) => {
|
Proc.runCommand("dsearch-ping", ["dsearch", "ping", "--json"], (stdout, exitCode) => {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
if (exitCode === 0) {
|
if (exitCode === 0) {
|
||||||
try {
|
try {
|
||||||
const response = JSON.parse(stdout)
|
const response = JSON.parse(stdout);
|
||||||
callback({ "result": response })
|
callback({
|
||||||
|
"result": response
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callback({ "error": "failed to parse ping response" })
|
callback({
|
||||||
|
"error": "failed to parse ping response"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
callback({ "error": "ping failed" })
|
callback({
|
||||||
|
"error": "ping failed"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function search(query, params, callback) {
|
function search(query, params, callback) {
|
||||||
if (!query || query.length === 0) {
|
if (!query || query.length === 0) {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback({ "error": "query is required" })
|
callback({
|
||||||
|
"error": "query is required"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dsearchAvailable) {
|
if (!dsearchAvailable) {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback({ "error": "dsearch not available" })
|
callback({
|
||||||
|
"error": "dsearch not available"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const args = ["dsearch", "search", query, "--json"]
|
const args = ["dsearch", "search", query, "--json"];
|
||||||
|
|
||||||
if (params) {
|
if (params) {
|
||||||
if (params.limit !== undefined) {
|
if (params.limit !== undefined) {
|
||||||
args.push("-n", String(params.limit))
|
args.push("-n", String(params.limit));
|
||||||
|
}
|
||||||
|
if (params.type) {
|
||||||
|
args.push("-t", params.type);
|
||||||
}
|
}
|
||||||
if (params.ext) {
|
if (params.ext) {
|
||||||
args.push("-e", params.ext)
|
args.push("-e", params.ext);
|
||||||
|
}
|
||||||
|
if (params.folder) {
|
||||||
|
args.push("--folder", params.folder);
|
||||||
}
|
}
|
||||||
if (params.field) {
|
if (params.field) {
|
||||||
args.push("-f", params.field)
|
args.push("-f", params.field);
|
||||||
}
|
}
|
||||||
if (params.fuzzy) {
|
if (params.fuzzy) {
|
||||||
args.push("--fuzzy")
|
args.push("--fuzzy");
|
||||||
}
|
}
|
||||||
if (params.sort) {
|
if (params.sort) {
|
||||||
args.push("--sort", params.sort)
|
args.push("--sort", params.sort);
|
||||||
}
|
}
|
||||||
if (params.desc !== undefined) {
|
if (params.desc !== undefined) {
|
||||||
args.push("--desc=" + (params.desc ? "true" : "false"))
|
args.push("--desc=" + (params.desc ? "true" : "false"));
|
||||||
}
|
}
|
||||||
if (params.minSize !== undefined) {
|
if (params.minSize !== undefined) {
|
||||||
args.push("--min-size", String(params.minSize))
|
args.push("--min-size", String(params.minSize));
|
||||||
}
|
}
|
||||||
if (params.maxSize !== undefined) {
|
if (params.maxSize !== undefined) {
|
||||||
args.push("--max-size", String(params.maxSize))
|
args.push("--max-size", String(params.maxSize));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Proc.runCommand("dsearch-search", args, (stdout, exitCode) => {
|
Proc.runCommand("dsearch-search", args, (stdout, exitCode) => {
|
||||||
if (exitCode === 0) {
|
if (exitCode === 0) {
|
||||||
try {
|
try {
|
||||||
const response = JSON.parse(stdout)
|
const response = JSON.parse(stdout);
|
||||||
searchResultsReceived(response)
|
searchResultsReceived(response);
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback({ "result": response })
|
callback({
|
||||||
|
"result": response
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const error = "failed to parse search response"
|
const error = "failed to parse search response";
|
||||||
errorOccurred(error)
|
errorOccurred(error);
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback({ "error": error })
|
callback({
|
||||||
|
"error": error
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (exitCode === 124) {
|
} else if (exitCode === 124) {
|
||||||
const error = "search timed out"
|
const error = "search timed out";
|
||||||
errorOccurred(error)
|
errorOccurred(error);
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback({ "error": error })
|
callback({
|
||||||
|
"error": error
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const error = "search failed"
|
const error = "search failed";
|
||||||
errorOccurred(error)
|
errorOccurred(error);
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback({ "error": error })
|
callback({
|
||||||
|
"error": error
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 100, 5000)
|
}, 100, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function rediscover() {
|
function rediscover() {
|
||||||
checkProcess.running = true
|
checkProcess.running = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool available: false
|
||||||
|
|
||||||
|
function detectAvailability() {
|
||||||
|
try {
|
||||||
|
const testObj = Qt.createQmlObject(`
|
||||||
|
import QtQuick
|
||||||
|
import QtMultimedia
|
||||||
|
Item {}
|
||||||
|
`, root, "MultimediaService.TestComponent");
|
||||||
|
if (testObj) {
|
||||||
|
testObj.destroy();
|
||||||
|
}
|
||||||
|
available = true;
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
available = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (!detectAvailability()) {
|
||||||
|
console.warn("MultimediaService: QtMultimedia not available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -252,7 +252,7 @@ Singleton {
|
|||||||
if (daysDiff === 0)
|
if (daysDiff === 0)
|
||||||
return timeStr;
|
return timeStr;
|
||||||
try {
|
try {
|
||||||
const localeName = (typeof Qt !== "undefined" && Qt.locale) ? Qt.locale().name : "en-US";
|
const localeName = (typeof I18n !== "undefined" && I18n.locale) ? I18n.locale().name : "en-US";
|
||||||
const weekday = date.toLocaleDateString(localeName, {
|
const weekday = date.toLocaleDateString(localeName, {
|
||||||
weekday: "long"
|
weekday: "long"
|
||||||
});
|
});
|
||||||
@@ -695,7 +695,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const localeName = (typeof Qt !== "undefined" && Qt.locale) ? Qt.locale().name : "en-US";
|
const localeName = (typeof I18n !== "undefined" && I18n.locale) ? I18n.locale().name : "en-US";
|
||||||
const weekday = time.toLocaleDateString(localeName, {
|
const weekday = time.toLocaleDateString(localeName, {
|
||||||
weekday: "long"
|
weekday: "long"
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ Singleton {
|
|||||||
property var notificationModal: null
|
property var notificationModal: null
|
||||||
property var wifiPasswordModal: null
|
property var wifiPasswordModal: null
|
||||||
property var wifiPasswordModalLoader: null
|
property var wifiPasswordModalLoader: null
|
||||||
|
property var wifiQRCodeModal: null
|
||||||
|
property var wifiQRCodeModalLoader: null
|
||||||
property var polkitAuthModal: null
|
property var polkitAuthModal: null
|
||||||
property var polkitAuthModalLoader: null
|
property var polkitAuthModalLoader: null
|
||||||
property var bluetoothPairingModal: null
|
property var bluetoothPairingModal: null
|
||||||
@@ -661,6 +663,13 @@ Singleton {
|
|||||||
wifiPasswordModal.show(ssid);
|
wifiPasswordModal.show(ssid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showWifiQRCodeModal(ssid) {
|
||||||
|
if (wifiQRCodeModalLoader)
|
||||||
|
wifiQRCodeModalLoader.active = true;
|
||||||
|
if (wifiQRCodeModal)
|
||||||
|
wifiQRCodeModal.show(ssid);
|
||||||
|
}
|
||||||
|
|
||||||
function showHiddenNetworkModal() {
|
function showHiddenNetworkModal() {
|
||||||
if (wifiPasswordModalLoader)
|
if (wifiPasswordModalLoader)
|
||||||
wifiPasswordModalLoader.active = true;
|
wifiPasswordModalLoader.active = true;
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ Singleton {
|
|||||||
const terminal = Quickshell.env("TERMINAL") || "xterm";
|
const terminal = Quickshell.env("TERMINAL") || "xterm";
|
||||||
|
|
||||||
if (SettingsData.updaterUseCustomCommand && SettingsData.updaterCustomCommand.length > 0) {
|
if (SettingsData.updaterUseCustomCommand && SettingsData.updaterCustomCommand.length > 0) {
|
||||||
const updateCommand = `${SettingsData.updaterCustomCommand} && echo "Updates complete! Press Enter to close..." && read`;
|
const updateCommand = `${SettingsData.updaterCustomCommand} && echo -n "Updates complete! " ; echo "Press Enter to close..." && read`;
|
||||||
const termClass = SettingsData.updaterTerminalAdditionalParams;
|
const termClass = SettingsData.updaterTerminalAdditionalParams;
|
||||||
|
|
||||||
var finalCommand = [terminal];
|
var finalCommand = [terminal];
|
||||||
@@ -274,7 +274,7 @@ Singleton {
|
|||||||
} else {
|
} else {
|
||||||
const params = packageManagerParams[pkgManager].upgradeSettings.params.join(" ");
|
const params = packageManagerParams[pkgManager].upgradeSettings.params.join(" ");
|
||||||
const sudo = packageManagerParams[pkgManager].upgradeSettings.requiresSudo ? "sudo" : "";
|
const sudo = packageManagerParams[pkgManager].upgradeSettings.requiresSudo ? "sudo" : "";
|
||||||
const updateCommand = `${sudo} ${pkgManager} ${params} && echo "Updates complete! Press Enter to close..." && read`;
|
const updateCommand = `${sudo} ${pkgManager} ${params} && echo -n "Updates complete! " ; echo "Press Enter to close..." && read`;
|
||||||
|
|
||||||
updater.command = [terminal, "-e", "sh", "-c", updateCommand];
|
updater.command = [terminal, "-e", "sh", "-c", updateCommand];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -444,7 +444,7 @@ Singleton {
|
|||||||
|
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
date.setDate(date.getDate() + index);
|
date.setDate(date.getDate() + index);
|
||||||
const locale = Qt.locale();
|
const locale = I18n.locale();
|
||||||
return locale.dayName(date.getDay(), Locale.ShortFormat);
|
return locale.dayName(date.getDay(), Locale.ShortFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
v1.4.3
|
v1.5-beta
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Window
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
@@ -18,9 +19,30 @@ Rectangle {
|
|||||||
|
|
||||||
signal imageSaved(string filePath)
|
signal imageSaved(string filePath)
|
||||||
|
|
||||||
|
property string _pendingSavePath: ""
|
||||||
|
property var _attachedWindow: root.Window.window
|
||||||
|
|
||||||
|
on_AttachedWindowChanged: {
|
||||||
|
if (_attachedWindow && _pendingSavePath !== "") {
|
||||||
|
Qt.callLater(function () {
|
||||||
|
if (root._pendingSavePath !== "") {
|
||||||
|
let path = root._pendingSavePath;
|
||||||
|
root._pendingSavePath = "";
|
||||||
|
root.saveImageToFile(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function saveImageToFile(filePath) {
|
function saveImageToFile(filePath) {
|
||||||
if (activeImage.status !== Image.Ready)
|
if (activeImage.status !== Image.Ready)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (!activeImage.Window.window) {
|
||||||
|
_pendingSavePath = filePath;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
activeImage.grabToImage(function (result) {
|
activeImage.grabToImage(function (result) {
|
||||||
if (result && result.saveToFile(filePath)) {
|
if (result && result.saveToFile(filePath)) {
|
||||||
root.imageSaved(filePath);
|
root.imageSaved(filePath);
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ Item {
|
|||||||
visible: false
|
visible: false
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (typeof updatesEnabled !== "undefined")
|
if (typeof updatesEnabled !== "undefined" && !root.overlayContent)
|
||||||
updatesEnabled = false;
|
updatesEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#%PAM-1.0
|
||||||
|
|
||||||
|
auth required pam_u2f.so cue nouserok timeout=10
|
||||||
@@ -4,10 +4,12 @@ return {
|
|||||||
priority = 1000,
|
priority = 1000,
|
||||||
config = function()
|
config = function()
|
||||||
require('base16-colorscheme').setup({
|
require('base16-colorscheme').setup({
|
||||||
base00 = '{{dank16.color0.default.hex}}',
|
|
||||||
base01 = '{{dank16.color0.default.hex}}',
|
base00 = '{{colors.background.dark.hex}}',
|
||||||
base02 = '{{dank16.color8.default.hex}}',
|
base01 = '{{colors.surface_container_low.dark.hex}}',
|
||||||
base03 = '{{dank16.color8.default.hex}}',
|
base02 = '{{colors.surface_container.dark.hex}}',
|
||||||
|
base03 = '{{dank16.color8.dark.hex}}',
|
||||||
|
base0B = '{{dank16.color3.dark.hex}}',
|
||||||
base04 = '{{dank16.color7.default.hex}}',
|
base04 = '{{dank16.color7.default.hex}}',
|
||||||
base05 = '{{dank16.color15.default.hex}}',
|
base05 = '{{dank16.color15.default.hex}}',
|
||||||
base06 = '{{dank16.color15.default.hex}}',
|
base06 = '{{dank16.color15.default.hex}}',
|
||||||
@@ -15,65 +17,12 @@ return {
|
|||||||
base08 = '{{dank16.color9.default.hex}}',
|
base08 = '{{dank16.color9.default.hex}}',
|
||||||
base09 = '{{dank16.color9.default.hex}}',
|
base09 = '{{dank16.color9.default.hex}}',
|
||||||
base0A = '{{dank16.color12.default.hex}}',
|
base0A = '{{dank16.color12.default.hex}}',
|
||||||
base0B = '{{dank16.color10.default.hex}}',
|
|
||||||
base0C = '{{dank16.color14.default.hex}}',
|
base0C = '{{dank16.color14.default.hex}}',
|
||||||
base0D = '{{dank16.color12.default.hex}}',
|
base0D = '{{dank16.color12.default.hex}}',
|
||||||
base0E = '{{dank16.color13.default.hex}}',
|
base0E = '{{dank16.color13.default.hex}}',
|
||||||
base0F = '{{dank16.color13.default.hex}}',
|
base0F = '{{dank16.color13.default.hex}}',
|
||||||
})
|
})
|
||||||
|
|
||||||
vim.api.nvim_set_hl(0, 'Visual', {
|
|
||||||
bg = '{{dank16.color8.default.hex}}',
|
|
||||||
fg = '{{dank16.color15.default.hex}}',
|
|
||||||
bold = true
|
|
||||||
})
|
|
||||||
vim.api.nvim_set_hl(0, 'Statusline', {
|
|
||||||
bg = '{{dank16.color12.default.hex}}',
|
|
||||||
fg = '{{dank16.color0.default.hex}}',
|
|
||||||
})
|
|
||||||
vim.api.nvim_set_hl(0, 'LineNr', { fg = '{{dank16.color8.default.hex}}' })
|
|
||||||
vim.api.nvim_set_hl(0, 'CursorLineNr', { fg = '{{dank16.color14.default.hex}}', bold = true })
|
|
||||||
|
|
||||||
vim.api.nvim_set_hl(0, 'Statement', {
|
|
||||||
fg = '{{dank16.color13.default.hex}}',
|
|
||||||
bold = true
|
|
||||||
})
|
|
||||||
vim.api.nvim_set_hl(0, 'Keyword', { link = 'Statement' })
|
|
||||||
vim.api.nvim_set_hl(0, 'Repeat', { link = 'Statement' })
|
|
||||||
vim.api.nvim_set_hl(0, 'Conditional', { link = 'Statement' })
|
|
||||||
|
|
||||||
vim.api.nvim_set_hl(0, 'Function', {
|
|
||||||
fg = '{{dank16.color12.default.hex}}',
|
|
||||||
bold = true
|
|
||||||
})
|
|
||||||
vim.api.nvim_set_hl(0, 'Macro', {
|
|
||||||
fg = '{{dank16.color12.default.hex}}',
|
|
||||||
italic = true
|
|
||||||
})
|
|
||||||
vim.api.nvim_set_hl(0, '@function.macro', { link = 'Macro' })
|
|
||||||
|
|
||||||
vim.api.nvim_set_hl(0, 'Type', {
|
|
||||||
fg = '{{dank16.color14.default.hex}}',
|
|
||||||
bold = true,
|
|
||||||
italic = true
|
|
||||||
})
|
|
||||||
vim.api.nvim_set_hl(0, 'Structure', { link = 'Type' })
|
|
||||||
|
|
||||||
vim.api.nvim_set_hl(0, 'String', {
|
|
||||||
fg = '{{dank16.color10.default.hex}}',
|
|
||||||
italic = true
|
|
||||||
})
|
|
||||||
|
|
||||||
vim.api.nvim_set_hl(0, 'Operator', { fg = '{{dank16.color7.default.hex}}' })
|
|
||||||
vim.api.nvim_set_hl(0, 'Delimiter', { fg = '{{dank16.color7.default.hex}}' })
|
|
||||||
vim.api.nvim_set_hl(0, '@punctuation.bracket', { link = 'Delimiter' })
|
|
||||||
vim.api.nvim_set_hl(0, '@punctuation.delimiter', { link = 'Delimiter' })
|
|
||||||
|
|
||||||
vim.api.nvim_set_hl(0, 'Comment', {
|
|
||||||
fg = '{{dank16.color8.default.hex}}',
|
|
||||||
italic = true
|
|
||||||
})
|
|
||||||
|
|
||||||
local current_file_path = vim.fn.stdpath("config") .. "/lua/plugins/dankcolors.lua"
|
local current_file_path = vim.fn.stdpath("config") .. "/lua/plugins/dankcolors.lua"
|
||||||
if not _G._matugen_theme_watcher then
|
if not _G._matugen_theme_watcher then
|
||||||
local uv = vim.uv or vim.loop
|
local uv = vim.uv or vim.loop
|
||||||
|
|||||||
+532
-466
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import re
|
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -82,7 +82,15 @@ CATEGORY_KEYWORDS = {
|
|||||||
"Displays": ["monitor", "screen", "resolution"],
|
"Displays": ["monitor", "screen", "resolution"],
|
||||||
"Desktop Widgets": ["conky", "desktop clock"],
|
"Desktop Widgets": ["conky", "desktop clock"],
|
||||||
"Audio": ["sound", "volume", "speaker", "microphone", "headphones", "pipewire"],
|
"Audio": ["sound", "volume", "speaker", "microphone", "headphones", "pipewire"],
|
||||||
"Window Rules": ["window", "rules", "matching", "floating", "fullscreen", "opacity"],
|
"Window Rules": [
|
||||||
|
"window",
|
||||||
|
"rules",
|
||||||
|
"matching",
|
||||||
|
"floating",
|
||||||
|
"fullscreen",
|
||||||
|
"opacity",
|
||||||
|
],
|
||||||
|
"Locale": ["locale", "language", "country"],
|
||||||
}
|
}
|
||||||
|
|
||||||
TAB_INDEX_MAP = {
|
TAB_INDEX_MAP = {
|
||||||
@@ -115,6 +123,7 @@ TAB_INDEX_MAP = {
|
|||||||
"DesktopWidgetsTab.qml": 27,
|
"DesktopWidgetsTab.qml": 27,
|
||||||
"WindowRulesTab.qml": 28,
|
"WindowRulesTab.qml": 28,
|
||||||
"AudioTab.qml": 29,
|
"AudioTab.qml": 29,
|
||||||
|
"LocaleTab.qml": 30,
|
||||||
}
|
}
|
||||||
|
|
||||||
TAB_CATEGORY_MAP = {
|
TAB_CATEGORY_MAP = {
|
||||||
@@ -147,6 +156,7 @@ TAB_CATEGORY_MAP = {
|
|||||||
27: "Desktop Widgets",
|
27: "Desktop Widgets",
|
||||||
28: "Window Rules",
|
28: "Window Rules",
|
||||||
29: "Audio",
|
29: "Audio",
|
||||||
|
30: "Locale",
|
||||||
}
|
}
|
||||||
|
|
||||||
SEARCHABLE_COMPONENTS = [
|
SEARCHABLE_COMPONENTS = [
|
||||||
|
|||||||
@@ -2,17 +2,26 @@
|
|||||||
"%1 Animation Speed": {
|
"%1 Animation Speed": {
|
||||||
"%1 Animation Speed": "Velocità Animazione %1"
|
"%1 Animation Speed": "Velocità Animazione %1"
|
||||||
},
|
},
|
||||||
"%1 DMS bind(s) may be overridden by config binds that come after the include.": {
|
"%1 DMS bind may be overridden by config binds that come after the include.": {
|
||||||
"%1 DMS bind(s) may be overridden by config binds that come after the include.": "%1 associazione/i tasti di DMS potrebbe/ero essere sovrascritta/e da scorciatoie di config successive all'inclusione."
|
"%1 DMS bind may be overridden by config binds that come after the include.": "%1 associazione tasti di DMS potrebbe essere sovrascritta da scorciatoie di config successive all'inclusione."
|
||||||
},
|
},
|
||||||
"%1 adapter(s), none connected": {
|
"%1 DMS binds may be overridden by config binds that come after the include.": {
|
||||||
"%1 adapter(s), none connected": "%1 adattatore/i, nessuno connesso"
|
"%1 DMS binds may be overridden by config binds that come after the include.": "%1 associazioni tasti di DMS potrebbero essere sovrascritte da scorciatoie di config successive all'inclusione."
|
||||||
|
},
|
||||||
|
"%1 adapter, none connected": {
|
||||||
|
"%1 adapter, none connected": "%1 adattatore, nessuno connesso"
|
||||||
|
},
|
||||||
|
"%1 adapters, none connected": {
|
||||||
|
"%1 adapters, none connected": "%1 adattatori, nessuno connesso"
|
||||||
},
|
},
|
||||||
"%1 characters": {
|
"%1 characters": {
|
||||||
"%1 characters": "%1 caratteri"
|
"%1 characters": "%1 caratteri"
|
||||||
},
|
},
|
||||||
"%1 class(es)": {
|
"%1 class": {
|
||||||
"%1 class(es)": "%1 classe/i"
|
"%1 class": "%1 classe"
|
||||||
|
},
|
||||||
|
"%1 classes": {
|
||||||
|
"%1 classes": "%1 classi"
|
||||||
},
|
},
|
||||||
"%1 connected": {
|
"%1 connected": {
|
||||||
"%1 connected": "%1 connesso"
|
"%1 connected": "%1 connesso"
|
||||||
@@ -29,8 +38,11 @@
|
|||||||
"%1 disconnected (hidden)": {
|
"%1 disconnected (hidden)": {
|
||||||
"%1 disconnected (hidden)": "%1 disconnesso (nascosto)"
|
"%1 disconnected (hidden)": "%1 disconnesso (nascosto)"
|
||||||
},
|
},
|
||||||
"%1 display(s)": {
|
"%1 display": {
|
||||||
"%1 display(s)": "%1 schermo/i"
|
"%1 display": "%1 schermo"
|
||||||
|
},
|
||||||
|
"%1 displays": {
|
||||||
|
"%1 displays": "%1 schermi"
|
||||||
},
|
},
|
||||||
"%1 exists but is not included in config. Custom keybinds will not work until this is fixed.": {
|
"%1 exists but is not included in config. Custom keybinds will not work until this is fixed.": {
|
||||||
"%1 exists but is not included in config. Custom keybinds will not work until this is fixed.": "%1 esiste ma non è incluso nella configurazione. Le scorciatoie personalizzate non funzioneranno finché non sarà risolto."
|
"%1 exists but is not included in config. Custom keybinds will not work until this is fixed.": "%1 esiste ma non è incluso nella configurazione. Le scorciatoie personalizzate non funzioneranno finché non sarà risolto."
|
||||||
@@ -41,14 +53,20 @@
|
|||||||
"%1 is now included in config": {
|
"%1 is now included in config": {
|
||||||
"%1 is now included in config": "%1 è ora incluso nella configurazione"
|
"%1 is now included in config": "%1 è ora incluso nella configurazione"
|
||||||
},
|
},
|
||||||
"%1 job(s)": {
|
"%1 job": {
|
||||||
"%1 job(s)": "%1 stampa/e"
|
"%1 job": "%1 stampa"
|
||||||
|
},
|
||||||
|
"%1 jobs": {
|
||||||
|
"%1 jobs": "%1 stampe"
|
||||||
},
|
},
|
||||||
"%1 notifications": {
|
"%1 notifications": {
|
||||||
"%1 notifications": "%1 notifiche"
|
"%1 notifications": "%1 notifiche"
|
||||||
},
|
},
|
||||||
"%1 printer(s)": {
|
"%1 printer": {
|
||||||
"%1 printer(s)": "%1 stampante/i"
|
"%1 printer": "%1 stampante"
|
||||||
|
},
|
||||||
|
"%1 printers": {
|
||||||
|
"%1 printers": "%1 stampanti"
|
||||||
},
|
},
|
||||||
"%1 variants": {
|
"%1 variants": {
|
||||||
"%1 variants": "%1 varianti"
|
"%1 variants": "%1 varianti"
|
||||||
@@ -2779,14 +2797,23 @@
|
|||||||
"Last launched %1": {
|
"Last launched %1": {
|
||||||
"Last launched %1": "Ultimo avvio %1"
|
"Last launched %1": "Ultimo avvio %1"
|
||||||
},
|
},
|
||||||
"Last launched %1 day%2 ago": {
|
"Last launched %1 day ago": {
|
||||||
"Last launched %1 day%2 ago": "Ultimo avvio %1 giorno/i fa"
|
"Last launched %1 day ago": "Ultimo avvio %1 giorno fa"
|
||||||
},
|
},
|
||||||
"Last launched %1 hour%2 ago": {
|
"Last launched %1 days ago": {
|
||||||
"Last launched %1 hour%2 ago": "Ultimo avvio %1 ora/e fa"
|
"Last launched %1 days ago": "Ultimo avvio %1 giorni fa"
|
||||||
},
|
},
|
||||||
"Last launched %1 minute%2 ago": {
|
"Last launched %1 hour ago": {
|
||||||
"Last launched %1 minute%2 ago": "Ultimo avvio %1 minuto/i fa"
|
"Last launched %1 hour ago": "Ultimo avvio %1 ora fa"
|
||||||
|
},
|
||||||
|
"Last launched %1 hours ago": {
|
||||||
|
"Last launched %1 hours ago": "Ultimo avvio %1 ore fa"
|
||||||
|
},
|
||||||
|
"Last launched %1 minute ago": {
|
||||||
|
"Last launched %1 minute ago": "Ultimo avvio %1 minuto fa"
|
||||||
|
},
|
||||||
|
"Last launched %1 minutes ago": {
|
||||||
|
"Last launched %1 minutes ago": "Ultimo avvio %1 minuti fa"
|
||||||
},
|
},
|
||||||
"Last launched just now": {
|
"Last launched just now": {
|
||||||
"Last launched just now": "Appena avviato"
|
"Last launched just now": "Appena avviato"
|
||||||
|
|||||||
@@ -3771,6 +3771,48 @@
|
|||||||
],
|
],
|
||||||
"description": "Use fingerprint reader for lock screen authentication (requires enrolled fingerprints)"
|
"description": "Use fingerprint reader for lock screen authentication (requires enrolled fingerprints)"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "enableU2f",
|
||||||
|
"label": "Enable security key authentication",
|
||||||
|
"tabIndex": 11,
|
||||||
|
"category": "Lock Screen",
|
||||||
|
"keywords": [
|
||||||
|
"authentication",
|
||||||
|
"enable",
|
||||||
|
"fido",
|
||||||
|
"hardware",
|
||||||
|
"key",
|
||||||
|
"lock",
|
||||||
|
"lockscreen",
|
||||||
|
"login",
|
||||||
|
"password",
|
||||||
|
"screen",
|
||||||
|
"security",
|
||||||
|
"u2f",
|
||||||
|
"yubikey"
|
||||||
|
],
|
||||||
|
"description": "Use a FIDO2/U2F security key (e.g. YubiKey) for lock screen authentication (requires enrolled keys)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"section": "u2fMode",
|
||||||
|
"label": "Security key mode",
|
||||||
|
"tabIndex": 11,
|
||||||
|
"category": "Lock Screen",
|
||||||
|
"keywords": [
|
||||||
|
"alternative",
|
||||||
|
"authentication",
|
||||||
|
"factor",
|
||||||
|
"key",
|
||||||
|
"lock",
|
||||||
|
"lockscreen",
|
||||||
|
"mode",
|
||||||
|
"second",
|
||||||
|
"security",
|
||||||
|
"u2f",
|
||||||
|
"yubikey"
|
||||||
|
],
|
||||||
|
"description": "Alternative lets the key unlock on its own. Second factor requires password or fingerprint first, then the key."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "loginctlLockIntegration",
|
"section": "loginctlLockIntegration",
|
||||||
"label": "Enable loginctl lock integration",
|
"label": "Enable loginctl lock integration",
|
||||||
@@ -3883,6 +3925,27 @@
|
|||||||
],
|
],
|
||||||
"description": "Automatically lock the screen when DMS starts"
|
"description": "Automatically lock the screen when DMS starts"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "lockBeforeSuspend",
|
||||||
|
"label": "Lock before suspend",
|
||||||
|
"tabIndex": 11,
|
||||||
|
"category": "Lock Screen",
|
||||||
|
"keywords": [
|
||||||
|
"automatic",
|
||||||
|
"automatically",
|
||||||
|
"before",
|
||||||
|
"lock",
|
||||||
|
"login",
|
||||||
|
"password",
|
||||||
|
"prepares",
|
||||||
|
"screen",
|
||||||
|
"security",
|
||||||
|
"sleep",
|
||||||
|
"suspend",
|
||||||
|
"system"
|
||||||
|
],
|
||||||
|
"description": "Automatically lock the screen when the system prepares to suspend"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "lockScreenNotificationMode",
|
"section": "lockScreenNotificationMode",
|
||||||
"label": "Notification Display",
|
"label": "Notification Display",
|
||||||
@@ -5807,27 +5870,6 @@
|
|||||||
"icon": "schedule",
|
"icon": "schedule",
|
||||||
"description": "Gradually fade the screen before locking with a configurable grace period"
|
"description": "Gradually fade the screen before locking with a configurable grace period"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"section": "lockBeforeSuspend",
|
|
||||||
"label": "Lock before suspend",
|
|
||||||
"tabIndex": 21,
|
|
||||||
"category": "Power & Sleep",
|
|
||||||
"keywords": [
|
|
||||||
"automatically",
|
|
||||||
"before",
|
|
||||||
"energy",
|
|
||||||
"lock",
|
|
||||||
"power",
|
|
||||||
"prepares",
|
|
||||||
"screen",
|
|
||||||
"security",
|
|
||||||
"shutdown",
|
|
||||||
"sleep",
|
|
||||||
"suspend",
|
|
||||||
"system"
|
|
||||||
],
|
|
||||||
"description": "Automatically lock the screen when the system prepares to suspend"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"section": "fadeToLockGracePeriod",
|
"section": "fadeToLockGracePeriod",
|
||||||
"label": "Lock fade grace period",
|
"label": "Lock fade grace period",
|
||||||
@@ -6395,5 +6437,33 @@
|
|||||||
"volume"
|
"volume"
|
||||||
],
|
],
|
||||||
"icon": "computer"
|
"icon": "computer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"section": "_tab_30",
|
||||||
|
"label": "Locale",
|
||||||
|
"tabIndex": 30,
|
||||||
|
"category": "Locale",
|
||||||
|
"keywords": [
|
||||||
|
"country",
|
||||||
|
"language",
|
||||||
|
"locale"
|
||||||
|
],
|
||||||
|
"icon": "language"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"section": "locale",
|
||||||
|
"label": "Locale Settings",
|
||||||
|
"tabIndex": 30,
|
||||||
|
"category": "Locale",
|
||||||
|
"keywords": [
|
||||||
|
"change",
|
||||||
|
"country",
|
||||||
|
"interface",
|
||||||
|
"language",
|
||||||
|
"locale",
|
||||||
|
"settings"
|
||||||
|
],
|
||||||
|
"icon": "language",
|
||||||
|
"description": "Change the locale used by the DMS interface."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -2043,6 +2043,13 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Change the locale used by the DMS interface.",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Channel",
|
"term": "Channel",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -3114,6 +3121,13 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Current Locale",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Current Monitor",
|
"term": "Current Monitor",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -3849,6 +3863,13 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Disk Usage Display",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Disks",
|
"term": "Disks",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -6936,6 +6957,20 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Locale",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"term": "Locale Settings",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Location",
|
"term": "Location",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -10184,6 +10219,20 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Remaining",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"term": "Remaining / Total",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Remove",
|
"term": "Remove",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -11549,6 +11598,13 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Show Memory in GB",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Show Network",
|
"term": "Show Network",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -11661,6 +11717,13 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Show Swap",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Show System Date",
|
"term": "Show System Date",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -11766,6 +11829,13 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Show in GB",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Show launcher overlay when typing in Niri overview. Disable to use another launcher.",
|
"term": "Show launcher overlay when typing in Niri overview. Disable to use another launcher.",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -12914,6 +12984,13 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Total",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Total Jobs",
|
"term": "Total Jobs",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -14656,5 +14733,47 @@
|
|||||||
"context": "Keyboard hints when enter-to-paste is enabled",
|
"context": "Keyboard hints when enter-to-paste is enabled",
|
||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"term": "What's New",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"term": "Read Full Release Notes",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"term": "Read Full Release Notes",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"term": "Got It",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"term": "Caps Lock is on",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"term": "↑/↓: Nav • Space: Expand • Enter: Action/Expand • E: Text",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user